Add tangency constraints, for line segments against arcs or cubics.

These are just a convenience, since it would be possible to get the
same result by drawing a construction line.

[git-p4: depot-paths = "//depot/solvespace/": change = 1836]
This commit is contained in:
Jonathan Westhues 2008-07-13 04:44:05 -08:00
parent 36870360cb
commit 88fc69116f
8 changed files with 158 additions and 11 deletions

View File

@ -31,6 +31,8 @@ char *Constraint::DescriptionString(void) {
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 COMMENT: s = "comment"; break;
@ -378,7 +380,7 @@ void Constraint::MenuConstrain(int id) {
if(gs.constraints == 1 && gs.n == 0) {
Constraint *c = SS.GetConstraint(gs.constraint[0]);
if(c->type == ANGLE) {
c->otherAngle = !(c->otherAngle);
c->other = !(c->other);
c->ModifyToSatisfy();
break;
}
@ -405,7 +407,7 @@ void Constraint::MenuConstrain(int id) {
c.entityA = gs.vector[0];
c.entityB = gs.vector[1];
c.valA = 0;
c.otherAngle = true;
c.other = true;
} else {
Error("Bad selection for angle constraint.");
return;
@ -419,8 +421,56 @@ void Constraint::MenuConstrain(int id) {
c.type = PARALLEL;
c.entityA = gs.vector[0];
c.entityB = gs.vector[1];
} else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
Entity *line = SS.GetEntity(gs.entity[0]);
Entity *arc = SS.GetEntity(gs.entity[1]);
if(line->type == Entity::ARC_OF_CIRCLE) {
SWAP(Entity *, line, arc);
}
Vector l0 = SS.GetEntity(line->point[0])->PointGetNum(),
l1 = SS.GetEntity(line->point[1])->PointGetNum();
Vector a1 = SS.GetEntity(arc->point[1])->PointGetNum(),
a2 = SS.GetEntity(arc->point[2])->PointGetNum();
if(l0.Equals(a1) || l1.Equals(a1)) {
c.other = false;
} else if(l0.Equals(a2) || l1.Equals(a2)) {
c.other = true;
} else {
Error("The tangent arc and line segment must share an "
"endpoint. Constrain them with Constrain -> "
"On Point before constraining tangent.");
return;
}
c.type = ARC_LINE_TANGENT;
c.entityA = arc->h;
c.entityB = line->h;
} else if(gs.lineSegments == 1 && gs.cubics == 1 && gs.n == 2) {
Entity *line = SS.GetEntity(gs.entity[0]);
Entity *cubic = SS.GetEntity(gs.entity[1]);
if(line->type == Entity::CUBIC) {
SWAP(Entity *, line, cubic);
}
Vector l0 = SS.GetEntity(line->point[0])->PointGetNum(),
l1 = SS.GetEntity(line->point[1])->PointGetNum();
Vector a0 = SS.GetEntity(cubic->point[0])->PointGetNum(),
a3 = SS.GetEntity(cubic->point[3])->PointGetNum();
if(l0.Equals(a0) || l1.Equals(a0)) {
c.other = false;
} else if(l0.Equals(a3) || l1.Equals(a3)) {
c.other = true;
} else {
Error("The tangent cubic and line segment must share an "
"endpoint. Constrain them with Constrain -> "
"On Point before constraining tangent.");
return;
}
c.type = CUBIC_LINE_TANGENT;
c.entityA = cubic->h;
c.entityB = line->h;
} else {
Error("Bad selection for parallel constraint.");
Error("Bad selection for parallel / tangent constraint.");
return;
}
AddConstraint(&c);
@ -563,7 +613,7 @@ void Constraint::ModifyToSatisfy(void) {
if(type == ANGLE) {
Vector a = SS.GetEntity(entityA)->VectorGetNum();
Vector b = SS.GetEntity(entityB)->VectorGetNum();
if(otherAngle) a = a.ScaledBy(-1);
if(other) a = a.ScaledBy(-1);
if(workplane.v != Entity::FREE_IN_3D.v) {
a = a.ProjectVectorInto(workplane);
b = b.ProjectVectorInto(workplane);
@ -961,7 +1011,7 @@ void Constraint::GenerateReal(IdList<Equation,hEquation> *l) {
Entity *b = SS.GetEntity(entityB);
ExprVector ae = a->VectorGetExprs();
ExprVector be = b->VectorGetExprs();
if(otherAngle) ae = ae.ScaledBy(Expr::From(-1));
if(other) ae = ae.ScaledBy(Expr::From(-1));
Expr *c;
if(workplane.v == Entity::FREE_IN_3D.v) {
Expr *mags = (ae.Magnitude())->Times(be.Magnitude());
@ -992,6 +1042,45 @@ void Constraint::GenerateReal(IdList<Equation,hEquation> *l) {
break;
}
case ARC_LINE_TANGENT: {
Entity *arc = SS.GetEntity(entityA);
Entity *line = SS.GetEntity(entityB);
ExprVector ac = SS.GetEntity(arc->point[0])->PointGetExprs();
ExprVector ap =
SS.GetEntity(arc->point[other ? 2 : 1])->PointGetExprs();
ExprVector ld = line->VectorGetExprs();
// The line is perpendicular to the radius
AddEq(l, ld.Dot(ac.Minus(ap)), 0);
break;
}
case CUBIC_LINE_TANGENT: {
Entity *cubic = SS.GetEntity(entityA);
Entity *line = SS.GetEntity(entityB);
ExprVector endpoint =
SS.GetEntity(cubic->point[other ? 3 : 0])->PointGetExprs();
ExprVector ctrlpoint =
SS.GetEntity(cubic->point[other ? 2 : 1])->PointGetExprs();
ExprVector a = endpoint.Minus(ctrlpoint);
ExprVector b = line->VectorGetExprs();
if(workplane.v == Entity::FREE_IN_3D.v) {
AddEq(l, VectorsParallel(0, a, b), 0);
AddEq(l, VectorsParallel(1, a, b), 1);
} else {
Entity *w = SS.GetEntity(workplane);
ExprVector wn = w->Normal()->NormalExprsN();
AddEq(l, (a.Cross(b)).Dot(wn), 0);
}
break;
}
case PARALLEL: {
Entity *ea = SS.GetEntity(entityA), *eb = SS.GetEntity(entityB);
if(eb->group.v != group.v) {

View File

@ -791,8 +791,13 @@ void GraphicsWindow::GroupSelection(void) {
switch(e->type) {
case Entity::WORKPLANE: (gs.workplanes)++; break;
case Entity::LINE_SEGMENT: (gs.lineSegments)++; break;
case Entity::CUBIC: (gs.cubics)++; break;
case Entity::ARC_OF_CIRCLE:
(gs.circlesOrArcs)++;
(gs.arcs)++;
break;
case Entity::CIRCLE: (gs.circlesOrArcs)++; break;
}
}

View File

@ -305,7 +305,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
Vector b0 = b->VectorGetRefPoint();
Vector da = a->VectorGetNum();
Vector db = b->VectorGetNum();
if(otherAngle) da = da.ScaledBy(-1);
if(other) da = da.ScaledBy(-1);
if(workplane.v != Entity::FREE_IN_3D.v) {
a0 = a0.ProjectInto(workplane);
@ -411,6 +411,55 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
break;
}
case CUBIC_LINE_TANGENT:
case ARC_LINE_TANGENT: {
Vector textAt, u, v;
if(type == ARC_LINE_TANGENT) {
Entity *arc = SS.GetEntity(entityA);
Entity *norm = SS.GetEntity(arc->normal);
Vector c = SS.GetEntity(arc->point[0])->PointGetNum();
Vector p =
SS.GetEntity(arc->point[other ? 2 : 1])->PointGetNum();
Vector r = p.Minus(c);
textAt = p.Plus(r.WithMagnitude(14/SS.GW.scale));
u = norm->NormalU();
v = norm->NormalV();
} else {
Vector n;
if(workplane.v == Entity::FREE_IN_3D.v) {
u = gr;
v = gu;
n = gn;
} else {
Entity *wn = SS.GetEntity(workplane)->Normal();
u = wn->NormalU();
v = wn->NormalV();
n = wn->NormalN();
}
Entity *cubic = SS.GetEntity(entityA);
Vector p =
SS.GetEntity(cubic->point[other ? 3 : 0])->PointGetNum();
Vector dir = SS.GetEntity(entityB)->VectorGetNum();
Vector out = n.Cross(dir);
textAt = p.Plus(out.WithMagnitude(14/SS.GW.scale));
}
if(dogd.drawing) {
glPushMatrix();
glxTranslatev(textAt);
glxOntoWorkplane(u, v);
glxWriteTextRefCenter("T");
glPopMatrix();
} else {
dogd.refp = textAt;
Point2d ref = SS.GW.ProjectPoint(dogd.refp);
dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10);
}
break;
}
case PARALLEL: {
for(int i = 0; i < 2; i++) {
Entity *e = SS.GetEntity(i == 0 ? entityA : entityB);

View File

@ -132,7 +132,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 'c', "Constraint.ptC.v", 'x', &(SS.sv.c.ptC.v) },
{ 'c', "Constraint.entityA.v", 'x', &(SS.sv.c.entityA.v) },
{ 'c', "Constraint.entityB.v", 'x', &(SS.sv.c.entityB.v) },
{ 'c', "Constraint.otherAngle", 'b', &(SS.sv.c.otherAngle) },
{ 'c', "Constraint.other", 'b', &(SS.sv.c.other) },
{ '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

@ -25,11 +25,11 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 0, "&Edit", 0, NULL },
{ 1, "&Undo\tCtrl+Z", MNU_UNDO, 'Z'|C, mEdit },
{ 1, "&Redo\tCtrl+Y", MNU_REDO, 'Y'|C, mEdit },
{ 1, "Re&generate All\tSpace", MNU_REGEN_ALL, ' ', mEdit },
{ 1, NULL, 0, NULL },
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
{ 1, NULL, 0, NULL },
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
{ 1, "Re&generate All\tSpace", MNU_REGEN_ALL, ' ', mEdit },
{ 0, "&View", 0, NULL },
{ 1, "Zoom &In\t+", MNU_ZOOM_IN, '+', mView },
@ -87,7 +87,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "Length Ra&tio\tZ", MNU_RATIO, 'Z', mCon },
{ 1, "At &Midpoint\tM", MNU_AT_MIDPOINT, 'M', mCon },
{ 1, "S&ymmetric\tY", MNU_SYMMETRIC, 'Y', mCon },
{ 1, "Para&llel\tL", MNU_PARALLEL, 'L', mCon },
{ 1, "Para&llel / Tangent\tL", MNU_PARALLEL, 'L', mCon },
{ 1, "&Perpendicular\t[", MNU_PERPENDICULAR, '[', mCon },
{ 1, "Same Orient&ation\tX", MNU_ORIENTED_SAME, 'X', mCon },
{ 1, NULL, 0, NULL },

View File

@ -619,7 +619,7 @@ void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, bool emphasized) {
FindEdgeOn(a, b, &n, &nOther, tr->meta, cnt++);
if(n != 1) {
if(!emphasized) {
sel->AddEdge(a, b);
if(n == 0) sel->AddEdge(a, b);
} else {
dbp("hanging: n=%d (%.3f %.3f %.3f) (%.3f %.3f %.3f)",
n, CO(a), CO(b));

View File

@ -463,6 +463,8 @@ public:
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 COMMENT = 1000;
@ -482,7 +484,7 @@ public:
hEntity ptC;
hEntity entityA;
hEntity entityB;
bool otherAngle;
bool other;
bool reference; // a ref dimension, that generates no eqs

2
ui.h
View File

@ -316,6 +316,8 @@ public:
int faces;
int lineSegments;
int circlesOrArcs;
int arcs;
int cubics;
int anyNormals;
int vectors;
int constraints;