Improve the non-parametric rounding. It now works on both lines and
circles, using a numerical method. And the user can specify a radius, instead of letting us choose automatically, and specify whether the original lines should be kept and made construction, or deleted. [git-p4: depot-paths = "//depot/solvespace/": change = 2146]
This commit is contained in:
parent
a05cc4dda8
commit
fe2ea5d5e1
@ -83,7 +83,7 @@ void Constraint::AddConstraint(Constraint *c, bool rememberForUndo) {
|
|||||||
|
|
||||||
void Constraint::Constrain(int type, hEntity ptA, hEntity ptB,
|
void Constraint::Constrain(int type, hEntity ptA, hEntity ptB,
|
||||||
hEntity entityA, hEntity entityB,
|
hEntity entityA, hEntity entityB,
|
||||||
bool other)
|
bool other, bool other2)
|
||||||
{
|
{
|
||||||
Constraint c;
|
Constraint c;
|
||||||
memset(&c, 0, sizeof(c));
|
memset(&c, 0, sizeof(c));
|
||||||
@ -95,14 +95,15 @@ void Constraint::Constrain(int type, hEntity ptA, hEntity ptB,
|
|||||||
c.entityA = entityA;
|
c.entityA = entityA;
|
||||||
c.entityB = entityB;
|
c.entityB = entityB;
|
||||||
c.other = other;
|
c.other = other;
|
||||||
|
c.other2 = other2;
|
||||||
AddConstraint(&c, false);
|
AddConstraint(&c, false);
|
||||||
}
|
}
|
||||||
void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA){
|
void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA){
|
||||||
Constrain(type, ptA, ptB, entityA, Entity::NO_ENTITY, false);
|
Constrain(type, ptA, ptB, entityA, Entity::NO_ENTITY, false, false);
|
||||||
}
|
}
|
||||||
void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) {
|
void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) {
|
||||||
Constrain(POINTS_COINCIDENT, ptA, ptB,
|
Constrain(POINTS_COINCIDENT, ptA, ptB,
|
||||||
Entity::NO_ENTITY, Entity::NO_ENTITY, false);
|
Entity::NO_ENTITY, Entity::NO_ENTITY, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Constraint::MenuConstrain(int id) {
|
void Constraint::MenuConstrain(int id) {
|
||||||
|
@ -95,6 +95,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
|||||||
{ 1, "&Text in TrueType Font\tT", MNU_TTF_TEXT, 'T', mReq },
|
{ 1, "&Text in TrueType Font\tT", MNU_TTF_TEXT, 'T', mReq },
|
||||||
{ 1, NULL, 0, NULL },
|
{ 1, NULL, 0, NULL },
|
||||||
{ 1, "To&ggle Construction\tG", MNU_CONSTRUCTION, 'G', mReq },
|
{ 1, "To&ggle Construction\tG", MNU_CONSTRUCTION, 'G', mReq },
|
||||||
|
{ 1, "Tangent &Arc at Point\tShift+A", MNU_TANGENT_ARC, 'A'|S, mReq },
|
||||||
{ 1, "Split Curves at &Intersection\tI", MNU_SPLIT_CURVES, 'I', mReq },
|
{ 1, "Split Curves at &Intersection\tI", MNU_SPLIT_CURVES, 'I', mReq },
|
||||||
|
|
||||||
{ 0, "&Constrain", 0, NULL },
|
{ 0, "&Constrain", 0, NULL },
|
||||||
@ -847,16 +848,22 @@ void GraphicsWindow::MenuRequest(int id) {
|
|||||||
InvalidateGraphics();
|
InvalidateGraphics();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MNU_ARC: {
|
case MNU_TANGENT_ARC:
|
||||||
SS.GW.GroupSelection();
|
SS.GW.GroupSelection();
|
||||||
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
|
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
|
||||||
SS.GW.MakeTangentArc();
|
SS.GW.MakeTangentArc();
|
||||||
|
} else if(SS.GW.gs.n != 0) {
|
||||||
|
Error("Bad selection for tangent arc at point. Select a "
|
||||||
|
"single point, or select nothing to set up arc "
|
||||||
|
"parameters.");
|
||||||
} else {
|
} else {
|
||||||
s = "click point on arc (draws anti-clockwise)";
|
SS.TW.GoToScreen(TextWindow::SCREEN_TANGENT_ARC);
|
||||||
goto c;
|
SS.GW.ForceTextWindowShown();
|
||||||
|
SS.later.showTW = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
case MNU_ARC: s = "click point on arc (draws anti-clockwise)"; goto c;
|
||||||
case MNU_DATUM_POINT: s = "click to place datum point"; goto c;
|
case MNU_DATUM_POINT: s = "click to place datum point"; goto c;
|
||||||
case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c;
|
case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c;
|
||||||
case MNU_CUBIC: s = "click first point of cubic segment"; goto c;
|
case MNU_CUBIC: s = "click first point of cubic segment"; goto c;
|
||||||
|
345
modify.cpp
345
modify.cpp
@ -16,6 +16,12 @@ void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Let's say that A is coincident with B, and B is coincident with C. This
|
||||||
|
// implies that A is coincident with C; but if we delete B, then both
|
||||||
|
// constraints must be deleted too (since they reference B), and A is no
|
||||||
|
// longer constrained to C. This routine adds back that constraint.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
|
void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
|
||||||
Request *r = SK.GetRequest(hr);
|
Request *r = SK.GetRequest(hr);
|
||||||
if(r->group.v != SS.GW.activeGroup.v) return;
|
if(r->group.v != SS.GW.activeGroup.v) return;
|
||||||
@ -71,6 +77,131 @@ void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
|
|||||||
ld.Clear();
|
ld.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// A curve by its parametric equation, helper functions for computing tangent
|
||||||
|
// arcs by a numerical method.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void GraphicsWindow::ParametricCurve::MakeFromEntity(hEntity he, bool reverse) {
|
||||||
|
ZERO(this);
|
||||||
|
Entity *e = SK.GetEntity(he);
|
||||||
|
if(e->type == Entity::LINE_SEGMENT) {
|
||||||
|
isLine = true;
|
||||||
|
p0 = e->EndpointStart(),
|
||||||
|
p1 = e->EndpointFinish();
|
||||||
|
if(reverse) {
|
||||||
|
SWAP(Vector, p0, p1);
|
||||||
|
}
|
||||||
|
} else if(e->type == Entity::ARC_OF_CIRCLE) {
|
||||||
|
isLine = false;
|
||||||
|
p0 = SK.GetEntity(e->point[0])->PointGetNum();
|
||||||
|
Vector pe = SK.GetEntity(e->point[1])->PointGetNum();
|
||||||
|
r = (pe.Minus(p0)).Magnitude();
|
||||||
|
e->ArcGetAngles(&theta0, &theta1, &dtheta);
|
||||||
|
if(reverse) {
|
||||||
|
SWAP(double, theta0, theta1);
|
||||||
|
dtheta = -dtheta;
|
||||||
|
}
|
||||||
|
EntityBase *wrkpln = SK.GetEntity(e->workplane)->Normal();
|
||||||
|
u = wrkpln->NormalU();
|
||||||
|
v = wrkpln->NormalV();
|
||||||
|
} else {
|
||||||
|
oops();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double GraphicsWindow::ParametricCurve::LengthForAuto(void) {
|
||||||
|
if(isLine) {
|
||||||
|
// Allow a third of the line to disappear with auto radius
|
||||||
|
return (p1.Minus(p0)).Magnitude() / 3;
|
||||||
|
} else {
|
||||||
|
// But only a twentieth of the arc; shorter means fewer numerical
|
||||||
|
// problems since the curve is more linear over shorter sections.
|
||||||
|
return (fabs(dtheta)*r)/20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Vector GraphicsWindow::ParametricCurve::PointAt(double t) {
|
||||||
|
if(isLine) {
|
||||||
|
return p0.Plus((p1.Minus(p0)).ScaledBy(t));
|
||||||
|
} else {
|
||||||
|
double theta = theta0 + dtheta*t;
|
||||||
|
return p0.Plus(u.ScaledBy(r*cos(theta)).Plus(v.ScaledBy(r*sin(theta))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Vector GraphicsWindow::ParametricCurve::TangentAt(double t) {
|
||||||
|
if(isLine) {
|
||||||
|
return p1.Minus(p0);
|
||||||
|
} else {
|
||||||
|
double theta = theta0 + dtheta*t;
|
||||||
|
Vector t = u.ScaledBy(-r*sin(theta)).Plus(v.ScaledBy(r*cos(theta)));
|
||||||
|
t = t.ScaledBy(dtheta);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hRequest GraphicsWindow::ParametricCurve::CreateRequestTrimmedTo(double t,
|
||||||
|
bool extraConstraints, hEntity orig, hEntity arc, bool arcFinish)
|
||||||
|
{
|
||||||
|
hRequest hr;
|
||||||
|
Entity *e;
|
||||||
|
if(isLine) {
|
||||||
|
hr = SS.GW.AddRequest(Request::LINE_SEGMENT, false),
|
||||||
|
e = SK.GetEntity(hr.entity(0));
|
||||||
|
SK.GetEntity(e->point[0])->PointForceTo(PointAt(t));
|
||||||
|
SK.GetEntity(e->point[1])->PointForceTo(PointAt(1));
|
||||||
|
ConstrainPointIfCoincident(e->point[0]);
|
||||||
|
ConstrainPointIfCoincident(e->point[1]);
|
||||||
|
if(extraConstraints) {
|
||||||
|
Constraint::Constrain(Constraint::PT_ON_LINE,
|
||||||
|
hr.entity(1), Entity::NO_ENTITY, orig);
|
||||||
|
}
|
||||||
|
Constraint::Constrain(Constraint::ARC_LINE_TANGENT,
|
||||||
|
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
||||||
|
arc, e->h, arcFinish, false);
|
||||||
|
} else {
|
||||||
|
hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false),
|
||||||
|
e = SK.GetEntity(hr.entity(0));
|
||||||
|
SK.GetEntity(e->point[0])->PointForceTo(p0);
|
||||||
|
if(dtheta > 0) {
|
||||||
|
SK.GetEntity(e->point[1])->PointForceTo(PointAt(t));
|
||||||
|
SK.GetEntity(e->point[2])->PointForceTo(PointAt(1));
|
||||||
|
} else {
|
||||||
|
SK.GetEntity(e->point[2])->PointForceTo(PointAt(t));
|
||||||
|
SK.GetEntity(e->point[1])->PointForceTo(PointAt(1));
|
||||||
|
}
|
||||||
|
ConstrainPointIfCoincident(e->point[0]);
|
||||||
|
ConstrainPointIfCoincident(e->point[1]);
|
||||||
|
ConstrainPointIfCoincident(e->point[2]);
|
||||||
|
// The tangency constraint alone is enough to fully constrain it,
|
||||||
|
// so there's no need for more.
|
||||||
|
Constraint::Constrain(Constraint::CURVE_CURVE_TANGENT,
|
||||||
|
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
||||||
|
arc, e->h, arcFinish, (dtheta < 0));
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// If a point in the same group as hpt, and numerically coincident with hpt,
|
||||||
|
// happens to exist, then constrain that point coincident to hpt.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void GraphicsWindow::ParametricCurve::ConstrainPointIfCoincident(hEntity hpt) {
|
||||||
|
Entity *e, *pt;
|
||||||
|
pt = SK.GetEntity(hpt);
|
||||||
|
Vector ev, ptv;
|
||||||
|
ptv = pt->PointGetNum();
|
||||||
|
|
||||||
|
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||||
|
if(e->h.v == pt->h.v) continue;
|
||||||
|
if(!e->IsPoint()) continue;
|
||||||
|
if(e->group.v != pt->group.v) continue;
|
||||||
|
if(e->workplane.v != pt->workplane.v) continue;
|
||||||
|
|
||||||
|
ev = e->PointGetNum();
|
||||||
|
if(!ev.Equals(ptv)) continue;
|
||||||
|
|
||||||
|
Constraint::ConstrainCoincident(hpt, e->h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// A single point must be selected when this function is called. We find two
|
// A single point must be selected when this function is called. We find two
|
||||||
// non-construction line segments that join at this point, and create a
|
// non-construction line segments that join at this point, and create a
|
||||||
@ -83,131 +214,187 @@ void GraphicsWindow::MakeTangentArc(void) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find two line segments that join at the specified point,
|
// The point corresponding to the vertex to be rounded.
|
||||||
// and blend them with a tangent arc. First, find the
|
|
||||||
// requests that generate the line segments.
|
|
||||||
Vector pshared = SK.GetEntity(gs.point[0])->PointGetNum();
|
Vector pshared = SK.GetEntity(gs.point[0])->PointGetNum();
|
||||||
ClearSelection();
|
ClearSelection();
|
||||||
|
|
||||||
|
// First, find two requests (that are not construction, and that are
|
||||||
|
// in our group and workplane) that generate entities that have an
|
||||||
|
// endpoint at our vertex to be rounded.
|
||||||
int i, c = 0;
|
int i, c = 0;
|
||||||
Entity *line[2];
|
Entity *ent[2];
|
||||||
Request *lineReq[2];
|
Request *req[2];
|
||||||
bool point1[2];
|
hRequest hreq[2];
|
||||||
|
hEntity hent[2];
|
||||||
|
bool pointf[2];
|
||||||
for(i = 0; i < SK.request.n; i++) {
|
for(i = 0; i < SK.request.n; i++) {
|
||||||
Request *r = &(SK.request.elem[i]);
|
Request *r = &(SK.request.elem[i]);
|
||||||
if(r->group.v != activeGroup.v) continue;
|
if(r->group.v != activeGroup.v) continue;
|
||||||
if(r->type != Request::LINE_SEGMENT) continue;
|
if(r->workplane.v != ActiveWorkplane().v) continue;
|
||||||
if(r->construction) continue;
|
if(r->construction) continue;
|
||||||
|
if(r->type != Request::LINE_SEGMENT &&
|
||||||
|
r->type != Request::ARC_OF_CIRCLE)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Entity *e = SK.GetEntity(r->h.entity(0));
|
Entity *e = SK.GetEntity(r->h.entity(0));
|
||||||
Vector p0 = SK.GetEntity(e->point[0])->PointGetNum(),
|
Vector ps = e->EndpointStart(),
|
||||||
p1 = SK.GetEntity(e->point[1])->PointGetNum();
|
pf = e->EndpointFinish();
|
||||||
|
|
||||||
if(p0.Equals(pshared) || p1.Equals(pshared)) {
|
if(ps.Equals(pshared) || pf.Equals(pshared)) {
|
||||||
if(c < 2) {
|
if(c < 2) {
|
||||||
line[c] = e;
|
// We record the entity and request and their handles,
|
||||||
lineReq[c] = r;
|
// and whether the vertex to be rounded is the start or
|
||||||
point1[c] = (p1.Equals(pshared));
|
// finish of this entity.
|
||||||
|
ent[c] = e;
|
||||||
|
hent[c] = e->h;
|
||||||
|
req[c] = r;
|
||||||
|
hreq[c] = r->h;
|
||||||
|
pointf[c] = (pf.Equals(pshared));
|
||||||
}
|
}
|
||||||
c++;
|
c++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(c != 2) {
|
if(c != 2) {
|
||||||
Error("To create a tangent arc, select a point where "
|
Error("To create a tangent arc, select a point where two "
|
||||||
"two non-construction line segments join.");
|
"non-construction lines or cicles in this group and "
|
||||||
|
"workplane join.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SS.UndoRemember();
|
|
||||||
|
|
||||||
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
||||||
Vector wn = wrkpl->Normal()->NormalN();
|
Vector wn = wrkpl->Normal()->NormalN();
|
||||||
|
|
||||||
hEntity hshared = (line[0])->point[point1[0] ? 1 : 0],
|
// Based on these two entities, we make the objects that we'll use to
|
||||||
hother0 = (line[0])->point[point1[0] ? 0 : 1],
|
// numerically find the tangent arc.
|
||||||
hother1 = (line[1])->point[point1[1] ? 0 : 1];
|
ParametricCurve pc[2];
|
||||||
|
pc[0].MakeFromEntity(ent[0]->h, pointf[0]);
|
||||||
Vector pother0 = SK.GetEntity(hother0)->PointGetNum(),
|
pc[1].MakeFromEntity(ent[1]->h, pointf[1]);
|
||||||
pother1 = SK.GetEntity(hother1)->PointGetNum();
|
|
||||||
|
|
||||||
Vector v0shared = pshared.Minus(pother0),
|
|
||||||
v1shared = pshared.Minus(pother1);
|
|
||||||
|
|
||||||
hEntity srcline0 = (line[0])->h,
|
|
||||||
srcline1 = (line[1])->h;
|
|
||||||
|
|
||||||
(lineReq[0])->construction = true;
|
|
||||||
(lineReq[1])->construction = true;
|
|
||||||
|
|
||||||
// And thereafter we mustn't touch the entity or req ptrs,
|
// And thereafter we mustn't touch the entity or req ptrs,
|
||||||
// because the new requests/entities we add might force a
|
// because the new requests/entities we add might force a
|
||||||
// realloc.
|
// realloc.
|
||||||
memset(line, 0, sizeof(line));
|
memset(ent, 0, sizeof(ent));
|
||||||
memset(lineReq, 0, sizeof(lineReq));
|
memset(req, 0, sizeof(req));
|
||||||
|
|
||||||
// The sign of vv determines whether shortest distance is
|
Vector pinter;
|
||||||
// clockwise or anti-clockwise.
|
double r, vv;
|
||||||
Vector v = (wn.Cross(v0shared)).WithMagnitude(1);
|
// We now do Newton iterations to find the tangent arc, and its positions
|
||||||
double vv = v1shared.Dot(v);
|
// t back along the two curves, starting from shared point of the curves
|
||||||
|
// at t = 0. Lots of iterations helps convergence, and this is still
|
||||||
|
// ~10 ms for everything.
|
||||||
|
int iters = 1000;
|
||||||
|
double t[2] = { 0, 0 }, tp[2];
|
||||||
|
for(i = 0; i < iters + 20; i++) {
|
||||||
|
Vector p0 = pc[0].PointAt(t[0]),
|
||||||
|
p1 = pc[1].PointAt(t[1]),
|
||||||
|
t0 = pc[0].TangentAt(t[0]),
|
||||||
|
t1 = pc[1].TangentAt(t[1]);
|
||||||
|
|
||||||
double dot = (v0shared.WithMagnitude(1)).Dot(
|
pinter = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
|
||||||
v1shared.WithMagnitude(1));
|
p1, p1.Plus(t1),
|
||||||
double theta = acos(dot);
|
NULL, NULL, NULL);
|
||||||
double r = 200/scale;
|
|
||||||
// Set the radius so that no more than one third of the
|
|
||||||
// line segment disappears.
|
|
||||||
r = min(r, v0shared.Magnitude()*tan(theta/2)/3);
|
|
||||||
r = min(r, v1shared.Magnitude()*tan(theta/2)/3);
|
|
||||||
double el = r/tan(theta/2);
|
|
||||||
|
|
||||||
hRequest rln0 = AddRequest(Request::LINE_SEGMENT, false),
|
// The sign of vv determines whether shortest distance is
|
||||||
rln1 = AddRequest(Request::LINE_SEGMENT, false);
|
// clockwise or anti-clockwise.
|
||||||
hRequest rarc = AddRequest(Request::ARC_OF_CIRCLE, false);
|
Vector v = (wn.Cross(t0)).WithMagnitude(1);
|
||||||
|
vv = t1.Dot(v);
|
||||||
|
|
||||||
Entity *ln0 = SK.GetEntity(rln0.entity(0)),
|
double dot = (t0.WithMagnitude(1)).Dot(t1.WithMagnitude(1));
|
||||||
*ln1 = SK.GetEntity(rln1.entity(0));
|
double theta = acos(dot);
|
||||||
Entity *arc = SK.GetEntity(rarc.entity(0));
|
|
||||||
|
|
||||||
SK.GetEntity(ln0->point[0])->PointForceTo(pother0);
|
if(SS.tangentArcManual) {
|
||||||
Constraint::ConstrainCoincident(ln0->point[0], hother0);
|
r = SS.tangentArcRadius;
|
||||||
|
} else {
|
||||||
|
r = 200/scale;
|
||||||
|
// Set the radius so that no more than one third of the
|
||||||
|
// line segment disappears.
|
||||||
|
r = min(r, pc[0].LengthForAuto()*tan(theta/2));
|
||||||
|
r = min(r, pc[1].LengthForAuto()*tan(theta/2));;
|
||||||
|
}
|
||||||
|
// We are source-stepping the radius, to improve convergence. So
|
||||||
|
// ramp that for most of the iterations, and then do a few at
|
||||||
|
// the end with that constant for polishing.
|
||||||
|
if(i < iters) {
|
||||||
|
r *= 0.1 + 0.9*i/((double)iters);
|
||||||
|
}
|
||||||
|
|
||||||
SK.GetEntity(ln1->point[0])->PointForceTo(pother1);
|
// The distance from the intersection of the lines to the endpoint
|
||||||
Constraint::ConstrainCoincident(ln1->point[0], hother1);
|
// of the arc, along each line.
|
||||||
|
double el = r/tan(theta/2);
|
||||||
|
|
||||||
Vector arc0 = pshared.Minus(v0shared.WithMagnitude(el));
|
// Compute the endpoints of the arc, for each curve
|
||||||
Vector arc1 = pshared.Minus(v1shared.WithMagnitude(el));
|
Vector pa0 = pinter.Plus(t0.WithMagnitude(el)),
|
||||||
|
pa1 = pinter.Plus(t1.WithMagnitude(el));
|
||||||
|
|
||||||
SK.GetEntity(ln0->point[1])->PointForceTo(arc0);
|
tp[0] = t[0];
|
||||||
SK.GetEntity(ln1->point[1])->PointForceTo(arc1);
|
tp[1] = t[1];
|
||||||
|
|
||||||
Constraint::Constrain(Constraint::PT_ON_LINE,
|
// And convert those points to parameter values along the curve.
|
||||||
ln0->point[1], Entity::NO_ENTITY, srcline0);
|
t[0] += (pa0.Minus(p0)).DivPivoting(t0);
|
||||||
Constraint::Constrain(Constraint::PT_ON_LINE,
|
t[1] += (pa1.Minus(p1)).DivPivoting(t1);
|
||||||
ln1->point[1], Entity::NO_ENTITY, srcline1);
|
}
|
||||||
|
|
||||||
Vector center = arc0;
|
// Stupid check for convergence, and for an out of range result (as
|
||||||
|
// we would get, for example, if the line is too short to fit the
|
||||||
|
// rounding arc).
|
||||||
|
if(fabs(tp[0] - t[0]) > 1e-3 || fabs(tp[1] - t[1]) > 1e-3 ||
|
||||||
|
t[0] < 0.01 || t[1] < 0.01 ||
|
||||||
|
t[0] > 0.99 || t[1] > 0.99 ||
|
||||||
|
isnan(t[0]) || isnan(t[1]))
|
||||||
|
{
|
||||||
|
Error("Couldn't round this corner. Try a smaller radius, or try "
|
||||||
|
"creating the desired geometry by hand with tangency "
|
||||||
|
"constraints.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the location of the center of the arc
|
||||||
|
Vector center = pc[0].PointAt(t[0]),
|
||||||
|
v0inter = pinter.Minus(center);
|
||||||
int a, b;
|
int a, b;
|
||||||
if(vv < 0) {
|
if(vv < 0) {
|
||||||
a = 1; b = 2;
|
a = 1; b = 2;
|
||||||
center = center.Minus(v0shared.Cross(wn).WithMagnitude(r));
|
center = center.Minus(v0inter.Cross(wn).WithMagnitude(r));
|
||||||
} else {
|
} else {
|
||||||
a = 2; b = 1;
|
a = 2; b = 1;
|
||||||
center = center.Plus(v0shared.Cross(wn).WithMagnitude(r));
|
center = center.Plus(v0inter.Cross(wn).WithMagnitude(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
SK.GetEntity(arc->point[0])->PointForceTo(center);
|
SS.UndoRemember();
|
||||||
SK.GetEntity(arc->point[a])->PointForceTo(arc0);
|
|
||||||
SK.GetEntity(arc->point[b])->PointForceTo(arc1);
|
|
||||||
|
|
||||||
Constraint::ConstrainCoincident(arc->point[a], ln0->point[1]);
|
hRequest harc = AddRequest(Request::ARC_OF_CIRCLE, false);
|
||||||
Constraint::ConstrainCoincident(arc->point[b], ln1->point[1]);
|
Entity *earc = SK.GetEntity(harc.entity(0));
|
||||||
|
hEntity hearc = earc->h;
|
||||||
|
|
||||||
Constraint::Constrain(Constraint::ARC_LINE_TANGENT,
|
SK.GetEntity(earc->point[0])->PointForceTo(center);
|
||||||
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
SK.GetEntity(earc->point[a])->PointForceTo(pc[0].PointAt(t[0]));
|
||||||
arc->h, ln0->h, (a==2));
|
SK.GetEntity(earc->point[b])->PointForceTo(pc[1].PointAt(t[1]));
|
||||||
Constraint::Constrain(Constraint::ARC_LINE_TANGENT,
|
|
||||||
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
earc = NULL;
|
||||||
arc->h, ln1->h, (b==2));
|
|
||||||
|
pc[0].CreateRequestTrimmedTo(t[0], !SS.tangentArcDeleteOld,
|
||||||
|
hent[0], hearc, (b == 1));
|
||||||
|
pc[1].CreateRequestTrimmedTo(t[1], !SS.tangentArcDeleteOld,
|
||||||
|
hent[1], hearc, (a == 1));
|
||||||
|
|
||||||
|
// Now either make the original entities construction, or delete them
|
||||||
|
// entirely, according to user preference.
|
||||||
|
Request *re;
|
||||||
|
SK.request.ClearTags();
|
||||||
|
for(re = SK.request.First(); re; re = SK.request.NextAfter(re)) {
|
||||||
|
if(re->h.v == hreq[0].v || re->h.v == hreq[1].v) {
|
||||||
|
if(SS.tangentArcDeleteOld) {
|
||||||
|
re->tag = 1;
|
||||||
|
} else {
|
||||||
|
re->construction = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(SS.tangentArcDeleteOld) {
|
||||||
|
DeleteTaggedRequests();
|
||||||
|
}
|
||||||
|
|
||||||
SS.later.generateAll = true;
|
SS.later.generateAll = true;
|
||||||
}
|
}
|
||||||
|
2
sketch.h
2
sketch.h
@ -629,7 +629,7 @@ public:
|
|||||||
static void Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA);
|
static void Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA);
|
||||||
static void Constrain(int type, hEntity ptA, hEntity ptB,
|
static void Constrain(int type, hEntity ptA, hEntity ptB,
|
||||||
hEntity entityA, hEntity entityB,
|
hEntity entityA, hEntity entityB,
|
||||||
bool other);
|
bool other, bool other2);
|
||||||
};
|
};
|
||||||
|
|
||||||
class hEquation {
|
class hEquation {
|
||||||
|
@ -34,6 +34,8 @@ void SolveSpace::CheckLicenseFromRegistry(void) {
|
|||||||
void SolveSpace::Init(char *cmdLine) {
|
void SolveSpace::Init(char *cmdLine) {
|
||||||
CheckLicenseFromRegistry();
|
CheckLicenseFromRegistry();
|
||||||
|
|
||||||
|
SS.tangentArcRadius = 10.0;
|
||||||
|
|
||||||
// Then, load the registry settings.
|
// Then, load the registry settings.
|
||||||
int i;
|
int i;
|
||||||
// Default list of colors for the model material
|
// Default list of colors for the model material
|
||||||
|
@ -652,6 +652,12 @@ public:
|
|||||||
bool usePerspectiveProj;
|
bool usePerspectiveProj;
|
||||||
double CameraTangent(void);
|
double CameraTangent(void);
|
||||||
|
|
||||||
|
// Some stuff relating to the tangent arcs created non-parametrically
|
||||||
|
// as special requests.
|
||||||
|
double tangentArcRadius;
|
||||||
|
bool tangentArcManual;
|
||||||
|
bool tangentArcDeleteOld;
|
||||||
|
|
||||||
// The platform-dependent code calls this before entering the msg loop
|
// The platform-dependent code calls this before entering the msg loop
|
||||||
void Init(char *cmdLine);
|
void Init(char *cmdLine);
|
||||||
void CheckLicenseFromRegistry(void);
|
void CheckLicenseFromRegistry(void);
|
||||||
|
@ -587,6 +587,51 @@ void TextWindow::ShowStepDimension(void) {
|
|||||||
Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome);
|
Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// When we're creating tangent arcs (as requests, not as some parametric
|
||||||
|
// thing). User gets to specify the radius, and whether the old untrimmed
|
||||||
|
// curves are kept or deleted.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void TextWindow::ScreenChangeTangentArc(int link, DWORD v) {
|
||||||
|
switch(link) {
|
||||||
|
case 'r': {
|
||||||
|
char str[1024];
|
||||||
|
strcpy(str, SS.MmToString(SS.tangentArcRadius));
|
||||||
|
SS.TW.edit.meaning = EDIT_TANGENT_ARC_RADIUS;
|
||||||
|
ShowTextEditControl(12, 3, str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'a': SS.tangentArcManual = !SS.tangentArcManual; break;
|
||||||
|
case 'd': SS.tangentArcDeleteOld = !SS.tangentArcDeleteOld; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TextWindow::ShowTangentArc(void) {
|
||||||
|
Printf(true, "%FtTANGENT ARC PARAMETERS%E");
|
||||||
|
|
||||||
|
Printf(true, "%Ft radius of created arc%E");
|
||||||
|
if(SS.tangentArcManual) {
|
||||||
|
Printf(false, "%Ba %s %Fl%Lr%f[change]%E",
|
||||||
|
SS.MmToString(SS.tangentArcRadius),
|
||||||
|
&(TextWindow::ScreenChangeTangentArc));
|
||||||
|
} else {
|
||||||
|
Printf(false, "%Ba automatic");
|
||||||
|
}
|
||||||
|
|
||||||
|
Printf(false, "");
|
||||||
|
Printf(false, " %Fd%f%La%c choose radius automatically%E",
|
||||||
|
&ScreenChangeTangentArc,
|
||||||
|
!SS.tangentArcManual ? CHECK_TRUE : CHECK_FALSE);
|
||||||
|
Printf(false, " %Fd%f%Ld%c delete original entities afterward%E",
|
||||||
|
&ScreenChangeTangentArc,
|
||||||
|
SS.tangentArcDeleteOld ? CHECK_TRUE : CHECK_FALSE);
|
||||||
|
|
||||||
|
Printf(false, "");
|
||||||
|
Printf(false, "To create a tangent arc at a point,");
|
||||||
|
Printf(false, "select that point and then choose");
|
||||||
|
Printf(false, "Sketch -> Tangent Arc at Point.");
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// The edit control is visible, and the user just pressed enter.
|
// The edit control is visible, and the user just pressed enter.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -681,6 +726,17 @@ void TextWindow::EditControlDone(char *s) {
|
|||||||
shown.dimSteps = min(300, max(1, atoi(s)));
|
shown.dimSteps = min(300, max(1, atoi(s)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EDIT_TANGENT_ARC_RADIUS: {
|
||||||
|
Expr *e = Expr::From(s, true);
|
||||||
|
if(!e) break;
|
||||||
|
if(e->Eval() < LENGTH_EPS) {
|
||||||
|
Error("Radius cannot be zero or negative.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SS.tangentArcRadius = SS.ExprToMm(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
if(EditControlDoneForStyles(s)) cnt++;
|
if(EditControlDoneForStyles(s)) cnt++;
|
||||||
|
@ -277,6 +277,7 @@ void TextWindow::Show(void) {
|
|||||||
case SCREEN_STYLE_INFO: ShowStyleInfo(); break;
|
case SCREEN_STYLE_INFO: ShowStyleInfo(); break;
|
||||||
case SCREEN_PASTE_TRANSFORMED: ShowPasteTransformed(); break;
|
case SCREEN_PASTE_TRANSFORMED: ShowPasteTransformed(); break;
|
||||||
case SCREEN_EDIT_VIEW: ShowEditView(); break;
|
case SCREEN_EDIT_VIEW: ShowEditView(); break;
|
||||||
|
case SCREEN_TANGENT_ARC: ShowTangentArc(); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Printf(false, "");
|
Printf(false, "");
|
||||||
|
23
ui.h
23
ui.h
@ -99,6 +99,7 @@ public:
|
|||||||
static const int SCREEN_STYLE_INFO = 6;
|
static const int SCREEN_STYLE_INFO = 6;
|
||||||
static const int SCREEN_PASTE_TRANSFORMED = 7;
|
static const int SCREEN_PASTE_TRANSFORMED = 7;
|
||||||
static const int SCREEN_EDIT_VIEW = 8;
|
static const int SCREEN_EDIT_VIEW = 8;
|
||||||
|
static const int SCREEN_TANGENT_ARC = 9;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int screen;
|
int screen;
|
||||||
|
|
||||||
@ -163,6 +164,8 @@ public:
|
|||||||
static const int EDIT_VIEW_ORIGIN = 701;
|
static const int EDIT_VIEW_ORIGIN = 701;
|
||||||
static const int EDIT_VIEW_PROJ_RIGHT = 702;
|
static const int EDIT_VIEW_PROJ_RIGHT = 702;
|
||||||
static const int EDIT_VIEW_PROJ_UP = 703;
|
static const int EDIT_VIEW_PROJ_UP = 703;
|
||||||
|
// For tangent arc
|
||||||
|
static const int EDIT_TANGENT_ARC_RADIUS = 800;
|
||||||
struct {
|
struct {
|
||||||
bool showAgain;
|
bool showAgain;
|
||||||
int meaning;
|
int meaning;
|
||||||
@ -188,6 +191,7 @@ public:
|
|||||||
void ShowStepDimension(void);
|
void ShowStepDimension(void);
|
||||||
void ShowPasteTransformed(void);
|
void ShowPasteTransformed(void);
|
||||||
void ShowEditView(void);
|
void ShowEditView(void);
|
||||||
|
void ShowTangentArc(void);
|
||||||
// Special screen, based on selection
|
// Special screen, based on selection
|
||||||
void DescribeSelection(void);
|
void DescribeSelection(void);
|
||||||
|
|
||||||
@ -240,6 +244,8 @@ public:
|
|||||||
static void ScreenStepDimFinish(int link, DWORD v);
|
static void ScreenStepDimFinish(int link, DWORD v);
|
||||||
static void ScreenStepDimGo(int link, DWORD v);
|
static void ScreenStepDimGo(int link, DWORD v);
|
||||||
|
|
||||||
|
static void ScreenChangeTangentArc(int link, DWORD v);
|
||||||
|
|
||||||
static void ScreenPasteTransformed(int link, DWORD v);
|
static void ScreenPasteTransformed(int link, DWORD v);
|
||||||
|
|
||||||
static void ScreenHome(int link, DWORD v);
|
static void ScreenHome(int link, DWORD v);
|
||||||
@ -335,6 +341,7 @@ public:
|
|||||||
MNU_CUBIC,
|
MNU_CUBIC,
|
||||||
MNU_TTF_TEXT,
|
MNU_TTF_TEXT,
|
||||||
MNU_SPLIT_CURVES,
|
MNU_SPLIT_CURVES,
|
||||||
|
MNU_TANGENT_ARC,
|
||||||
MNU_CONSTRUCTION,
|
MNU_CONSTRUCTION,
|
||||||
// Group
|
// Group
|
||||||
MNU_GROUP_3D,
|
MNU_GROUP_3D,
|
||||||
@ -478,6 +485,22 @@ public:
|
|||||||
hRequest AddRequest(int type, bool rememberForUndo);
|
hRequest AddRequest(int type, bool rememberForUndo);
|
||||||
hRequest AddRequest(int type);
|
hRequest AddRequest(int type);
|
||||||
|
|
||||||
|
class ParametricCurve {
|
||||||
|
public:
|
||||||
|
bool isLine; // else circle
|
||||||
|
Vector p0, p1;
|
||||||
|
Vector u, v;
|
||||||
|
double r, theta0, theta1, dtheta;
|
||||||
|
|
||||||
|
void MakeFromEntity(hEntity he, bool reverse);
|
||||||
|
Vector PointAt(double t);
|
||||||
|
Vector TangentAt(double t);
|
||||||
|
double LengthForAuto(void);
|
||||||
|
|
||||||
|
hRequest CreateRequestTrimmedTo(double t, bool extraConstraints,
|
||||||
|
hEntity orig, hEntity arc, bool arcFinish);
|
||||||
|
void ConstrainPointIfCoincident(hEntity hpt);
|
||||||
|
};
|
||||||
void MakeTangentArc(void);
|
void MakeTangentArc(void);
|
||||||
void SplitLinesOrCurves(void);
|
void SplitLinesOrCurves(void);
|
||||||
hEntity SplitEntity(hEntity he, Vector pinter);
|
hEntity SplitEntity(hEntity he, Vector pinter);
|
||||||
|
Loading…
Reference in New Issue
Block a user