Add a constraint for tangency between any combination of arcs

and cubics. Also add that to the library interface.

It might have been better to use a single constraint for that,
plus all the line-curve or line-line cases, but it would break
backwards compatibility if I did that now, and perhaps be
confusing with the 'other' member (which is meaningless for
lines) anyways.

[git-p4: depot-paths = "//depot/solvespace/": change = 2141]
solver
Jonathan Westhues 2010-05-09 20:14:06 -08:00
parent 9f7ff34b98
commit 949df4d139
10 changed files with 209 additions and 76 deletions

View File

@ -32,6 +32,7 @@ char *Constraint::DescriptionString(void) {
case PARALLEL: s = "parallel"; break; case PARALLEL: s = "parallel"; break;
case ARC_LINE_TANGENT: s = "arc-line-tangent"; break; case ARC_LINE_TANGENT: s = "arc-line-tangent"; break;
case CUBIC_LINE_TANGENT: s = "cubic-line-tangent"; break; case CUBIC_LINE_TANGENT: s = "cubic-line-tangent"; break;
case CURVE_CURVE_TANGENT: s = "curve-curve-tangent"; break;
case PERPENDICULAR: s = "perpendicular"; break; case PERPENDICULAR: s = "perpendicular"; break;
case EQUAL_RADIUS: s = "eq-radius"; break; case EQUAL_RADIUS: s = "eq-radius"; break;
case EQUAL_ANGLE: s = "eq-angle"; break; case EQUAL_ANGLE: s = "eq-angle"; break;
@ -616,16 +617,42 @@ void Constraint::MenuConstrain(int id) {
c.type = CUBIC_LINE_TANGENT; c.type = CUBIC_LINE_TANGENT;
c.entityA = cubic->h; c.entityA = cubic->h;
c.entityB = line->h; c.entityB = line->h;
} else if(gs.cubics + gs.arcs == 2 && gs.n == 2) {
if(!SS.GW.LockedInWorkplane()) {
Error("Curve-curve tangency must apply in workplane.");
return;
}
Entity *eA = SK.GetEntity(gs.entity[0]),
*eB = SK.GetEntity(gs.entity[1]);
Vector as = eA->EndpointStart(),
af = eA->EndpointFinish(),
bs = eB->EndpointStart(),
bf = eB->EndpointFinish();
if(as.Equals(bs)) {
c.other = false; c.other2 = false;
} else if(as.Equals(bf)) {
c.other = false; c.other2 = true;
} else if(af.Equals(bs)) {
c.other = true; c.other2 = false;
} else if(af.Equals(bf)) {
c.other = true; c.other2 = true;
} else {
Error("The curves must share an endpoint. Constrain them "
"with Constrain -> On Point before constraining "
"tangent.");
return;
}
c.type = CURVE_CURVE_TANGENT;
c.entityA = eA->h;
c.entityB = eB->h;
} else { } else {
Error("Bad selection for parallel / tangent constraint. This " Error("Bad selection for parallel / tangent constraint. This "
"constraint can apply to:\n\n" "constraint can apply to:\n\n"
" * two line segments (parallel)\n" " * two line segments (parallel)\n"
" * a line segment and a normal (parallel)\n" " * a line segment and a normal (parallel)\n"
" * two normals (parallel)\n" " * two normals (parallel)\n"
" * a line segment and an arc, that share an endpoint " " * two line segments, arcs, or beziers, that share "
"(tangent)\n" "an endpoint (tangent)\n");
" * a line segment and a cubic bezier, that share an "
"endpoint (tangent)\n");
return; return;
} }
AddConstraint(&c); AddConstraint(&c);

View File

@ -689,6 +689,44 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
break; break;
} }
case CURVE_CURVE_TANGENT: {
bool parallel = true;
int i;
ExprVector dir[2];
for(i = 0; i < 2; i++) {
EntityBase *e = SK.GetEntity((i == 0) ? entityA : entityB);
bool oth = (i == 0) ? other : other2;
if(e->type == Entity::ARC_OF_CIRCLE) {
ExprVector center, endpoint;
center = SK.GetEntity(e->point[0])->PointGetExprs();
endpoint =
SK.GetEntity(e->point[oth ? 2 : 1])->PointGetExprs();
dir[i] = endpoint.Minus(center);
// We're using the vector from the center of the arc to
// an endpoint; so that's normal to the tangent, not
// parallel.
parallel = !parallel;
} else if(e->type == Entity::CUBIC) {
if(oth) {
dir[i] = e->CubicGetFinishTangentExprs();
} else {
dir[i] = e->CubicGetStartTangentExprs();
}
} else {
oops();
}
}
if(parallel) {
EntityBase *w = SK.GetEntity(workplane);
ExprVector wn = w->Normal()->NormalExprsN();
AddEq(l, ((dir[0]).Cross(dir[1])).Dot(wn), 0);
} else {
AddEq(l, (dir[0]).Dot(dir[1]), 0);
}
break;
}
case PARALLEL: { case PARALLEL: {
EntityBase *ea = SK.GetEntity(entityA), *eb = SK.GetEntity(entityB); EntityBase *ea = SK.GetEntity(entityA), *eb = SK.GetEntity(entityB);
if(eb->group.v != group.v) { if(eb->group.v != group.v) {

View File

@ -698,6 +698,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
break; break;
} }
case CURVE_CURVE_TANGENT:
case CUBIC_LINE_TANGENT: case CUBIC_LINE_TANGENT:
case ARC_LINE_TANGENT: { case ARC_LINE_TANGENT: {
Vector textAt, u, v; Vector textAt, u, v;
@ -712,7 +713,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
textAt = p.Plus(r.WithMagnitude(14/SS.GW.scale)); textAt = p.Plus(r.WithMagnitude(14/SS.GW.scale));
u = norm->NormalU(); u = norm->NormalU();
v = norm->NormalV(); v = norm->NormalV();
} else { } else if(type == CUBIC_LINE_TANGENT) {
Vector n; Vector n;
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane.v == Entity::FREE_IN_3D.v) {
u = gr; u = gr;
@ -731,6 +732,37 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
Vector dir = SK.GetEntity(entityB)->VectorGetNum(); Vector dir = SK.GetEntity(entityB)->VectorGetNum();
Vector out = n.Cross(dir); Vector out = n.Cross(dir);
textAt = p.Plus(out.WithMagnitude(14/SS.GW.scale)); textAt = p.Plus(out.WithMagnitude(14/SS.GW.scale));
} else {
Vector n, dir;
EntityBase *wn = SK.GetEntity(workplane)->Normal();
u = wn->NormalU();
v = wn->NormalV();
n = wn->NormalN();
EntityBase *eA = SK.GetEntity(entityA);
// Big pain; we have to get a vector tangent to the curve
// at the shared point, which could be from either a cubic
// or an arc.
if(other) {
textAt = eA->EndpointFinish();
if(eA->type == Entity::CUBIC) {
dir = eA->CubicGetFinishTangentNum();
} else {
dir = SK.GetEntity(eA->point[0])->PointGetNum().Minus(
SK.GetEntity(eA->point[2])->PointGetNum());
dir = n.Cross(dir);
}
} else {
textAt = eA->EndpointStart();
if(eA->type == Entity::CUBIC) {
dir = eA->CubicGetStartTangentNum();
} else {
dir = SK.GetEntity(eA->point[0])->PointGetNum().Minus(
SK.GetEntity(eA->point[1])->PointGetNum());
dir = n.Cross(dir);
}
}
dir = n.Cross(dir);
textAt = textAt.Plus(dir.WithMagnitude(14/SS.GW.scale));
} }
if(dogd.drawing) { if(dogd.drawing) {

View File

@ -130,6 +130,16 @@ ExprVector EntityBase::CubicGetFinishTangentExprs(void) {
poff = SK.GetEntity(point[2+extraPoints])->PointGetExprs(); poff = SK.GetEntity(point[2+extraPoints])->PointGetExprs();
return (pon.Minus(poff)); return (pon.Minus(poff));
} }
Vector EntityBase::CubicGetStartTangentNum(void) {
Vector pon = SK.GetEntity(point[0])->PointGetNum(),
poff = SK.GetEntity(point[1])->PointGetNum();
return (pon.Minus(poff));
}
Vector EntityBase::CubicGetFinishTangentNum(void) {
Vector pon = SK.GetEntity(point[3+extraPoints])->PointGetNum(),
poff = SK.GetEntity(point[2+extraPoints])->PointGetNum();
return (pon.Minus(poff));
}
bool EntityBase::IsWorkplane(void) { bool EntityBase::IsWorkplane(void) {
return (type == WORKPLANE); return (type == WORKPLANE);

View File

@ -414,9 +414,26 @@ SLVS_C_ARC_LINE_TANGENT**
SLVS_C_CUBIC_LINE_TANGENT* SLVS_C_CUBIC_LINE_TANGENT*
The cubic entityA is tangent to the line entityB. If other is false, The cubic entityA is tangent to the line entityB. The variable
then the cubic is tangent at its beginning (point[0]). If other is other indicates:
true, then the arc is tangent at its end (point[3]).
if false: the cubic is tangent at its beginning
if true: the cubic is tangent at its end
The beginning of the cubic is point[0], and the end is point[3].
SLVS_C_CURVE_CURVE_TANGENT**
The two entities entityA and entityB are tangent. These entities can
each be either an arc or a cubic, in any combination. The flags
other and other2 indicate which endpoint of the curve is tangent,
for entityA and entityB respectively:
if false: the entity is tangent at its beginning
if true: the entity is tangent at its end
For cubics, point[0] is the beginning, and point[3] is the end. For
arcs, point[1] is the beginning, and point[2] is the end.
SLVS_C_EQUAL_RADIUS SLVS_C_EQUAL_RADIUS

View File

@ -166,6 +166,7 @@ case SLVS_C_CUBIC_LINE_TANGENT: t = Constraint::CUBIC_LINE_TANGENT; break;
case SLVS_C_EQUAL_RADIUS: t = Constraint::EQUAL_RADIUS; break; case SLVS_C_EQUAL_RADIUS: t = Constraint::EQUAL_RADIUS; break;
case SLVS_C_PROJ_PT_DISTANCE: t = Constraint::PROJ_PT_DISTANCE; break; case SLVS_C_PROJ_PT_DISTANCE: t = Constraint::PROJ_PT_DISTANCE; break;
case SLVS_C_WHERE_DRAGGED: t = Constraint::WHERE_DRAGGED; break; case SLVS_C_WHERE_DRAGGED: t = Constraint::WHERE_DRAGGED; break;
case SLVS_C_CURVE_CURVE_TANGENT:t = Constraint::CURVE_CURVE_TANGENT; break;
default: dbp("bad constraint type %d", sc->type); return; default: dbp("bad constraint type %d", sc->type); return;
} }
@ -183,6 +184,7 @@ default: dbp("bad constraint type %d", sc->type); return;
c.entityC.v = sc->entityC; c.entityC.v = sc->entityC;
c.entityD.v = sc->entityD; c.entityD.v = sc->entityD;
c.other = (sc->other) ? true : false; c.other = (sc->other) ? true : false;
c.other2 = (sc->other2) ? true : false;
SK.constraint.Add(&c); SK.constraint.Add(&c);
} }

View File

@ -100,6 +100,7 @@ typedef struct {
#define SLVS_C_EQUAL_RADIUS 100029 #define SLVS_C_EQUAL_RADIUS 100029
#define SLVS_C_PROJ_PT_DISTANCE 100030 #define SLVS_C_PROJ_PT_DISTANCE 100030
#define SLVS_C_WHERE_DRAGGED 100031 #define SLVS_C_WHERE_DRAGGED 100031
#define SLVS_C_CURVE_CURVE_TANGENT 100032
typedef struct { typedef struct {
Slvs_hConstraint h; Slvs_hConstraint h;
@ -118,6 +119,7 @@ typedef struct {
Slvs_hEntity entityD; Slvs_hEntity entityD;
int other; int other;
int other2;
} Slvs_Constraint; } Slvs_Constraint;

View File

@ -171,6 +171,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 'c', "Constraint.entityC.v", 'x', &(SS.sv.c.entityC.v) }, { 'c', "Constraint.entityC.v", 'x', &(SS.sv.c.entityC.v) },
{ 'c', "Constraint.entityD.v", 'x', &(SS.sv.c.entityD.v) }, { 'c', "Constraint.entityD.v", 'x', &(SS.sv.c.entityD.v) },
{ 'c', "Constraint.other", 'b', &(SS.sv.c.other) }, { 'c', "Constraint.other", 'b', &(SS.sv.c.other) },
{ 'c', "Constraint.other2", 'b', &(SS.sv.c.other2) },
{ 'c', "Constraint.reference", 'b', &(SS.sv.c.reference) }, { 'c', "Constraint.reference", 'b', &(SS.sv.c.reference) },
{ 'c', "Constraint.comment", 'N', &(SS.sv.c.comment) }, { 'c', "Constraint.comment", 'N', &(SS.sv.c.comment) },
{ 'c', "Constraint.disp.offset.x", 'f', &(SS.sv.c.disp.offset.x) }, { 'c', "Constraint.disp.offset.x", 'f', &(SS.sv.c.disp.offset.x) },

View File

@ -399,6 +399,8 @@ public:
Vector CubicGetFinishNum(void); Vector CubicGetFinishNum(void);
ExprVector CubicGetStartTangentExprs(void); ExprVector CubicGetStartTangentExprs(void);
ExprVector CubicGetFinishTangentExprs(void); ExprVector CubicGetFinishTangentExprs(void);
Vector CubicGetStartTangentNum(void);
Vector CubicGetFinishTangentNum(void);
bool HasEndpoints(void); bool HasEndpoints(void);
Vector EndpointStart(); Vector EndpointStart();
@ -539,6 +541,7 @@ public:
static const int PERPENDICULAR = 122; static const int PERPENDICULAR = 122;
static const int ARC_LINE_TANGENT = 123; static const int ARC_LINE_TANGENT = 123;
static const int CUBIC_LINE_TANGENT = 124; static const int CUBIC_LINE_TANGENT = 124;
static const int CURVE_CURVE_TANGENT = 125;
static const int EQUAL_RADIUS = 130; static const int EQUAL_RADIUS = 130;
static const int WHERE_DRAGGED = 200; static const int WHERE_DRAGGED = 200;
@ -558,6 +561,7 @@ public:
hEntity entityC; hEntity entityC;
hEntity entityD; hEntity entityD;
bool other; bool other;
bool other2;
bool reference; // a ref dimension, that generates no eqs bool reference; // a ref dimension, that generates no eqs
NameStr comment; // since comments are represented as constraints NameStr comment; // since comments are represented as constraints

View File

@ -1,5 +1,5 @@
fix bug with rotation in plane where green line stays displayed fix bug with rotation in plane where green line stays displayed
more tangencies, and rounding (of requests, not parametric) rounding (of requests, not parametric)
----- -----
rounding, as a special group rounding, as a special group