diff --git a/constraint.cpp b/constraint.cpp index 28032c5a..1cb21438 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -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); diff --git a/constrainteq.cpp b/constrainteq.cpp index 3bba57e3..c35529b7 100644 --- a/constrainteq.cpp +++ b/constrainteq.cpp @@ -689,6 +689,44 @@ void ConstraintBase::GenerateReal(IdList *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) { diff --git a/drawconstraint.cpp b/drawconstraint.cpp index f3bebe97..29e4d260 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -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) { diff --git a/entity.cpp b/entity.cpp index 03a7b1c5..a54647b0 100644 --- a/entity.cpp +++ b/entity.cpp @@ -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); diff --git a/exposed/DOC.txt b/exposed/DOC.txt index 0e70b12f..fe68f938 100644 --- a/exposed/DOC.txt +++ b/exposed/DOC.txt @@ -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 diff --git a/exposed/lib.cpp b/exposed/lib.cpp index f0e25a12..817b519b 100644 --- a/exposed/lib.cpp +++ b/exposed/lib.cpp @@ -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); } diff --git a/exposed/slvs.h b/exposed/slvs.h index d1916300..3641d25c 100644 --- a/exposed/slvs.h +++ b/exposed/slvs.h @@ -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; diff --git a/file.cpp b/file.cpp index 79b76ba5..80fa724b 100644 --- a/file.cpp +++ b/file.cpp @@ -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) }, diff --git a/sketch.h b/sketch.h index 7cc2209c..3b44f354 100644 --- a/sketch.h +++ b/sketch.h @@ -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 diff --git a/wishlist.txt b/wishlist.txt index 4d6e80dc..956557b9 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -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