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

@ -5,40 +5,41 @@ char *Constraint::DescriptionString(void) {
char *s;
switch(type) {
case POINTS_COINCIDENT: s = "pts-coincident"; break;
case PT_PT_DISTANCE: s = "pt-pt-distance"; break;
case PT_LINE_DISTANCE: s = "pt-line-distance"; break;
case PT_PLANE_DISTANCE: s = "pt-plane-distance"; break;
case PT_FACE_DISTANCE: s = "pt-face-distance"; break;
case PROJ_PT_DISTANCE: s = "proj-pt-pt-distance"; break;
case PT_IN_PLANE: s = "pt-in-plane"; break;
case PT_ON_LINE: s = "pt-on-line"; break;
case PT_ON_FACE: s = "pt-on-face"; break;
case EQUAL_LENGTH_LINES:s = "eq-length"; break;
case EQ_LEN_PT_LINE_D: s = "eq-length-and-pt-ln-dist"; break;
case EQ_PT_LN_DISTANCES:s = "eq-pt-line-distances"; break;
case LENGTH_RATIO: s = "length-ratio"; break;
case SYMMETRIC: s = "symmetric"; break;
case SYMMETRIC_HORIZ: s = "symmetric-h"; break;
case SYMMETRIC_VERT: s = "symmetric-v"; break;
case SYMMETRIC_LINE: s = "symmetric-line"; break;
case AT_MIDPOINT: s = "at-midpoint"; break;
case HORIZONTAL: s = "horizontal"; break;
case VERTICAL: s = "vertical"; break;
case DIAMETER: s = "diameter"; break;
case PT_ON_CIRCLE: s = "pt-on-circle"; break;
case SAME_ORIENTATION: s = "same-orientation"; break;
case ANGLE: s = "angle"; break;
case PARALLEL: s = "parallel"; break;
case ARC_LINE_TANGENT: s = "arc-line-tangent"; break;
case CUBIC_LINE_TANGENT:s = "cubic-line-tangent"; break;
case PERPENDICULAR: s = "perpendicular"; break;
case EQUAL_RADIUS: s = "eq-radius"; break;
case EQUAL_ANGLE: s = "eq-angle"; break;
case EQUAL_LINE_ARC_LEN:s = "eq-line-len-arc-len"; break;
case WHERE_DRAGGED: s = "lock-where-dragged"; break;
case COMMENT: s = "comment"; break;
default: s = "???"; break;
case POINTS_COINCIDENT: s = "pts-coincident"; break;
case PT_PT_DISTANCE: s = "pt-pt-distance"; break;
case PT_LINE_DISTANCE: s = "pt-line-distance"; break;
case PT_PLANE_DISTANCE: s = "pt-plane-distance"; break;
case PT_FACE_DISTANCE: s = "pt-face-distance"; break;
case PROJ_PT_DISTANCE: s = "proj-pt-pt-distance"; break;
case PT_IN_PLANE: s = "pt-in-plane"; break;
case PT_ON_LINE: s = "pt-on-line"; break;
case PT_ON_FACE: s = "pt-on-face"; break;
case EQUAL_LENGTH_LINES: s = "eq-length"; break;
case EQ_LEN_PT_LINE_D: s = "eq-length-and-pt-ln-dist"; break;
case EQ_PT_LN_DISTANCES: s = "eq-pt-line-distances"; break;
case LENGTH_RATIO: s = "length-ratio"; break;
case SYMMETRIC: s = "symmetric"; break;
case SYMMETRIC_HORIZ: s = "symmetric-h"; break;
case SYMMETRIC_VERT: s = "symmetric-v"; break;
case SYMMETRIC_LINE: s = "symmetric-line"; break;
case AT_MIDPOINT: s = "at-midpoint"; break;
case HORIZONTAL: s = "horizontal"; break;
case VERTICAL: s = "vertical"; break;
case DIAMETER: s = "diameter"; break;
case PT_ON_CIRCLE: s = "pt-on-circle"; break;
case SAME_ORIENTATION: s = "same-orientation"; break;
case ANGLE: s = "angle"; break;
case PARALLEL: s = "parallel"; break;
case ARC_LINE_TANGENT: s = "arc-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 EQUAL_RADIUS: s = "eq-radius"; break;
case EQUAL_ANGLE: s = "eq-angle"; break;
case EQUAL_LINE_ARC_LEN: s = "eq-line-len-arc-len"; break;
case WHERE_DRAGGED: s = "lock-where-dragged"; break;
case COMMENT: s = "comment"; break;
default: s = "???"; break;
}
sprintf(ret, "c%03x-%s", h.v, s);
@ -616,16 +617,42 @@ void Constraint::MenuConstrain(int id) {
c.type = CUBIC_LINE_TANGENT;
c.entityA = cubic->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 {
Error("Bad selection for parallel / tangent constraint. This "
"constraint can apply to:\n\n"
" * two line segments (parallel)\n"
" * a line segment and a normal (parallel)\n"
" * two normals (parallel)\n"
" * a line segment and an arc, that share an endpoint "
"(tangent)\n"
" * a line segment and a cubic bezier, that share an "
"endpoint (tangent)\n");
" * two line segments, arcs, or beziers, that share "
"an endpoint (tangent)\n");
return;
}
AddConstraint(&c);

View File

@ -689,6 +689,44 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
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: {
EntityBase *ea = SK.GetEntity(entityA), *eb = SK.GetEntity(entityB);
if(eb->group.v != group.v) {

View File

@ -698,6 +698,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
break;
}
case CURVE_CURVE_TANGENT:
case CUBIC_LINE_TANGENT:
case ARC_LINE_TANGENT: {
Vector textAt, u, v;
@ -712,7 +713,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
textAt = p.Plus(r.WithMagnitude(14/SS.GW.scale));
u = norm->NormalU();
v = norm->NormalV();
} else {
} else if(type == CUBIC_LINE_TANGENT) {
Vector n;
if(workplane.v == Entity::FREE_IN_3D.v) {
u = gr;
@ -731,6 +732,37 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
Vector dir = SK.GetEntity(entityB)->VectorGetNum();
Vector out = n.Cross(dir);
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) {

View File

@ -130,6 +130,16 @@ ExprVector EntityBase::CubicGetFinishTangentExprs(void) {
poff = SK.GetEntity(point[2+extraPoints])->PointGetExprs();
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) {
return (type == WORKPLANE);

View File

@ -414,9 +414,26 @@ SLVS_C_ARC_LINE_TANGENT**
SLVS_C_CUBIC_LINE_TANGENT*
The cubic entityA is tangent to the line entityB. If other is false,
then the cubic is tangent at its beginning (point[0]). If other is
true, then the arc is tangent at its end (point[3]).
The cubic entityA is tangent to the line entityB. The variable
other indicates:
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

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_PROJ_PT_DISTANCE: t = Constraint::PROJ_PT_DISTANCE; 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;
}
@ -183,6 +184,7 @@ default: dbp("bad constraint type %d", sc->type); return;
c.entityC.v = sc->entityC;
c.entityD.v = sc->entityD;
c.other = (sc->other) ? true : false;
c.other2 = (sc->other2) ? true : false;
SK.constraint.Add(&c);
}

View File

@ -100,6 +100,7 @@ typedef struct {
#define SLVS_C_EQUAL_RADIUS 100029
#define SLVS_C_PROJ_PT_DISTANCE 100030
#define SLVS_C_WHERE_DRAGGED 100031
#define SLVS_C_CURVE_CURVE_TANGENT 100032
typedef struct {
Slvs_hConstraint h;
@ -118,6 +119,7 @@ typedef struct {
Slvs_hEntity entityD;
int other;
int other2;
} 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.entityD.v", 'x', &(SS.sv.c.entityD.v) },
{ '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.comment", 'N', &(SS.sv.c.comment) },
{ 'c', "Constraint.disp.offset.x", 'f', &(SS.sv.c.disp.offset.x) },

View File

@ -399,6 +399,8 @@ public:
Vector CubicGetFinishNum(void);
ExprVector CubicGetStartTangentExprs(void);
ExprVector CubicGetFinishTangentExprs(void);
Vector CubicGetStartTangentNum(void);
Vector CubicGetFinishTangentNum(void);
bool HasEndpoints(void);
Vector EndpointStart();
@ -509,40 +511,41 @@ public:
static const hConstraint NO_CONSTRAINT;
static const int POINTS_COINCIDENT = 20;
static const int PT_PT_DISTANCE = 30;
static const int PT_PLANE_DISTANCE = 31;
static const int PT_LINE_DISTANCE = 32;
static const int PT_FACE_DISTANCE = 33;
static const int PROJ_PT_DISTANCE = 34;
static const int PT_IN_PLANE = 41;
static const int PT_ON_LINE = 42;
static const int PT_ON_FACE = 43;
static const int EQUAL_LENGTH_LINES = 50;
static const int LENGTH_RATIO = 51;
static const int EQ_LEN_PT_LINE_D = 52;
static const int EQ_PT_LN_DISTANCES = 53;
static const int EQUAL_ANGLE = 54;
static const int EQUAL_LINE_ARC_LEN = 55;
static const int SYMMETRIC = 60;
static const int SYMMETRIC_HORIZ = 61;
static const int SYMMETRIC_VERT = 62;
static const int SYMMETRIC_LINE = 63;
static const int AT_MIDPOINT = 70;
static const int HORIZONTAL = 80;
static const int VERTICAL = 81;
static const int DIAMETER = 90;
static const int PT_ON_CIRCLE = 100;
static const int SAME_ORIENTATION = 110;
static const int ANGLE = 120;
static const int PARALLEL = 121;
static const int PERPENDICULAR = 122;
static const int ARC_LINE_TANGENT = 123;
static const int CUBIC_LINE_TANGENT = 124;
static const int EQUAL_RADIUS = 130;
static const int WHERE_DRAGGED = 200;
static const int POINTS_COINCIDENT = 20;
static const int PT_PT_DISTANCE = 30;
static const int PT_PLANE_DISTANCE = 31;
static const int PT_LINE_DISTANCE = 32;
static const int PT_FACE_DISTANCE = 33;
static const int PROJ_PT_DISTANCE = 34;
static const int PT_IN_PLANE = 41;
static const int PT_ON_LINE = 42;
static const int PT_ON_FACE = 43;
static const int EQUAL_LENGTH_LINES = 50;
static const int LENGTH_RATIO = 51;
static const int EQ_LEN_PT_LINE_D = 52;
static const int EQ_PT_LN_DISTANCES = 53;
static const int EQUAL_ANGLE = 54;
static const int EQUAL_LINE_ARC_LEN = 55;
static const int SYMMETRIC = 60;
static const int SYMMETRIC_HORIZ = 61;
static const int SYMMETRIC_VERT = 62;
static const int SYMMETRIC_LINE = 63;
static const int AT_MIDPOINT = 70;
static const int HORIZONTAL = 80;
static const int VERTICAL = 81;
static const int DIAMETER = 90;
static const int PT_ON_CIRCLE = 100;
static const int SAME_ORIENTATION = 110;
static const int ANGLE = 120;
static const int PARALLEL = 121;
static const int PERPENDICULAR = 122;
static const int ARC_LINE_TANGENT = 123;
static const int CUBIC_LINE_TANGENT = 124;
static const int CURVE_CURVE_TANGENT = 125;
static const int EQUAL_RADIUS = 130;
static const int WHERE_DRAGGED = 200;
static const int COMMENT = 1000;
static const int COMMENT = 1000;
int type;
@ -558,6 +561,7 @@ public:
hEntity entityC;
hEntity entityD;
bool other;
bool other2;
bool reference; // a ref dimension, that generates no eqs
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
more tangencies, and rounding (of requests, not parametric)
rounding (of requests, not parametric)
-----
rounding, as a special group