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 SAME_ORIENTATION: s = "same-orientation"; break;
case ANGLE: s = "angle"; break; case ANGLE: s = "angle"; break;
case PARALLEL: s = "parallel"; 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 PERPENDICULAR: s = "perpendicular"; break;
case EQUAL_RADIUS: s = "eq-radius"; break; case EQUAL_RADIUS: s = "eq-radius"; break;
case COMMENT: s = "comment"; break; case COMMENT: s = "comment"; break;
@ -378,7 +380,7 @@ void Constraint::MenuConstrain(int id) {
if(gs.constraints == 1 && gs.n == 0) { if(gs.constraints == 1 && gs.n == 0) {
Constraint *c = SS.GetConstraint(gs.constraint[0]); Constraint *c = SS.GetConstraint(gs.constraint[0]);
if(c->type == ANGLE) { if(c->type == ANGLE) {
c->otherAngle = !(c->otherAngle); c->other = !(c->other);
c->ModifyToSatisfy(); c->ModifyToSatisfy();
break; break;
} }
@ -405,7 +407,7 @@ void Constraint::MenuConstrain(int id) {
c.entityA = gs.vector[0]; c.entityA = gs.vector[0];
c.entityB = gs.vector[1]; c.entityB = gs.vector[1];
c.valA = 0; c.valA = 0;
c.otherAngle = true; c.other = true;
} else { } else {
Error("Bad selection for angle constraint."); Error("Bad selection for angle constraint.");
return; return;
@ -419,8 +421,56 @@ void Constraint::MenuConstrain(int id) {
c.type = PARALLEL; c.type = PARALLEL;
c.entityA = gs.vector[0]; c.entityA = gs.vector[0];
c.entityB = gs.vector[1]; 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 { } else {
Error("Bad selection for parallel constraint."); 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 / tangent constraint.");
return; return;
} }
AddConstraint(&c); AddConstraint(&c);
@ -563,7 +613,7 @@ void Constraint::ModifyToSatisfy(void) {
if(type == ANGLE) { if(type == ANGLE) {
Vector a = SS.GetEntity(entityA)->VectorGetNum(); Vector a = SS.GetEntity(entityA)->VectorGetNum();
Vector b = SS.GetEntity(entityB)->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) { if(workplane.v != Entity::FREE_IN_3D.v) {
a = a.ProjectVectorInto(workplane); a = a.ProjectVectorInto(workplane);
b = b.ProjectVectorInto(workplane); b = b.ProjectVectorInto(workplane);
@ -961,7 +1011,7 @@ void Constraint::GenerateReal(IdList<Equation,hEquation> *l) {
Entity *b = SS.GetEntity(entityB); Entity *b = SS.GetEntity(entityB);
ExprVector ae = a->VectorGetExprs(); ExprVector ae = a->VectorGetExprs();
ExprVector be = b->VectorGetExprs(); ExprVector be = b->VectorGetExprs();
if(otherAngle) ae = ae.ScaledBy(Expr::From(-1)); if(other) ae = ae.ScaledBy(Expr::From(-1));
Expr *c; Expr *c;
if(workplane.v == Entity::FREE_IN_3D.v) { if(workplane.v == Entity::FREE_IN_3D.v) {
Expr *mags = (ae.Magnitude())->Times(be.Magnitude()); Expr *mags = (ae.Magnitude())->Times(be.Magnitude());
@ -992,6 +1042,45 @@ void Constraint::GenerateReal(IdList<Equation,hEquation> *l) {
break; 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: { case PARALLEL: {
Entity *ea = SS.GetEntity(entityA), *eb = SS.GetEntity(entityB); Entity *ea = SS.GetEntity(entityA), *eb = SS.GetEntity(entityB);
if(eb->group.v != group.v) { if(eb->group.v != group.v) {

View File

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

View File

@ -305,7 +305,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
Vector b0 = b->VectorGetRefPoint(); Vector b0 = b->VectorGetRefPoint();
Vector da = a->VectorGetNum(); Vector da = a->VectorGetNum();
Vector db = b->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) { if(workplane.v != Entity::FREE_IN_3D.v) {
a0 = a0.ProjectInto(workplane); a0 = a0.ProjectInto(workplane);
@ -411,6 +411,55 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
break; 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: { case PARALLEL: {
for(int i = 0; i < 2; i++) { for(int i = 0; i < 2; i++) {
Entity *e = SS.GetEntity(i == 0 ? entityA : entityB); 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.ptC.v", 'x', &(SS.sv.c.ptC.v) },
{ 'c', "Constraint.entityA.v", 'x', &(SS.sv.c.entityA.v) }, { 'c', "Constraint.entityA.v", 'x', &(SS.sv.c.entityA.v) },
{ 'c', "Constraint.entityB.v", 'x', &(SS.sv.c.entityB.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.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

@ -25,11 +25,11 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 0, "&Edit", 0, NULL }, { 0, "&Edit", 0, NULL },
{ 1, "&Undo\tCtrl+Z", MNU_UNDO, 'Z'|C, mEdit }, { 1, "&Undo\tCtrl+Z", MNU_UNDO, 'Z'|C, mEdit },
{ 1, "&Redo\tCtrl+Y", MNU_REDO, 'Y'|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, NULL, 0, NULL },
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit }, { 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
{ 1, NULL, 0, NULL }, { 1, NULL, 0, NULL },
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit }, { 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
{ 1, "Re&generate All\tSpace", MNU_REGEN_ALL, ' ', mEdit },
{ 0, "&View", 0, NULL }, { 0, "&View", 0, NULL },
{ 1, "Zoom &In\t+", MNU_ZOOM_IN, '+', mView }, { 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, "Length Ra&tio\tZ", MNU_RATIO, 'Z', mCon },
{ 1, "At &Midpoint\tM", MNU_AT_MIDPOINT, 'M', mCon }, { 1, "At &Midpoint\tM", MNU_AT_MIDPOINT, 'M', mCon },
{ 1, "S&ymmetric\tY", MNU_SYMMETRIC, 'Y', 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, "&Perpendicular\t[", MNU_PERPENDICULAR, '[', mCon },
{ 1, "Same Orient&ation\tX", MNU_ORIENTED_SAME, 'X', mCon }, { 1, "Same Orient&ation\tX", MNU_ORIENTED_SAME, 'X', mCon },
{ 1, NULL, 0, NULL }, { 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++); FindEdgeOn(a, b, &n, &nOther, tr->meta, cnt++);
if(n != 1) { if(n != 1) {
if(!emphasized) { if(!emphasized) {
sel->AddEdge(a, b); if(n == 0) sel->AddEdge(a, b);
} else { } else {
dbp("hanging: n=%d (%.3f %.3f %.3f) (%.3f %.3f %.3f)", dbp("hanging: n=%d (%.3f %.3f %.3f) (%.3f %.3f %.3f)",
n, CO(a), CO(b)); n, CO(a), CO(b));

View File

@ -463,6 +463,8 @@ public:
static const int ANGLE = 120; static const int ANGLE = 120;
static const int PARALLEL = 121; static const int PARALLEL = 121;
static const int PERPENDICULAR = 122; 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 EQUAL_RADIUS = 130;
static const int COMMENT = 1000; static const int COMMENT = 1000;
@ -482,7 +484,7 @@ public:
hEntity ptC; hEntity ptC;
hEntity entityA; hEntity entityA;
hEntity entityB; hEntity entityB;
bool otherAngle; bool other;
bool reference; // a ref dimension, that generates no eqs bool reference; // a ref dimension, that generates no eqs

2
ui.h
View File

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