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
parent
9f7ff34b98
commit
949df4d139
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
10
entity.cpp
10
entity.cpp
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
1
file.cpp
1
file.cpp
|
@ -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) },
|
||||||
|
|
4
sketch.h
4
sketch.h
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue