diff --git a/CHANGELOG.md b/CHANGELOG.md index c8c6f0ac..72ed7290 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,8 @@ Bugs fixed: * A step rotate/translate group using a group forced to triangle mesh as a source group also gets forced to triangle mesh. * Paste Transformed with a negative scale does not invert arcs. + * The tangent arc now modifies the original entities instead of deleting + them, such that their constraints are retained. 2.x --- diff --git a/src/modify.cpp b/src/modify.cpp index 539ac43e..82a6e973 100644 --- a/src/modify.cpp +++ b/src/modify.cpp @@ -161,19 +161,32 @@ Vector GraphicsWindow::ParametricCurve::TangentAt(double t) { return t; } } -hRequest GraphicsWindow::ParametricCurve::CreateRequestTrimmedTo(double t, - bool extraConstraints, hEntity orig, hEntity arc, bool arcFinish) +/** Changes or copies the given entity and connects it to the arc. + * \param t Where on this parametric curve does it connect to the arc. + * \param reuseOrig Should the original entity be modified + * \param orig The original entity. + * \param arc The arc that will be connected to. + * \param arcFinish Whether to connect to the end point of the arc. + * \param pointf When changing the original entity, whether the end point should be modified. + */ +void GraphicsWindow::ParametricCurve::CreateRequestTrimmedTo(double t, + bool reuseOrig, hEntity orig, hEntity arc, bool arcFinish, bool pointf) { hRequest hr; Entity *e; if(isLine) { - hr = SS.GW.AddRequest(Request::Type::LINE_SEGMENT, /*rememberForUndo=*/false), - e = SK.GetEntity(hr.entity(0)); - SK.GetEntity(e->point[0])->PointForceTo(PointAt(t)); - SK.GetEntity(e->point[1])->PointForceTo(PointAt(1)); - ConstrainPointIfCoincident(e->point[0]); - ConstrainPointIfCoincident(e->point[1]); - if(extraConstraints) { + if (reuseOrig) { + e = SK.GetEntity(orig); + int i = pointf ? 1 : 0; + SK.GetEntity(e->point[i])->PointForceTo(PointAt(t)); + ConstrainPointIfCoincident(e->point[i]); + } else { + hr = SS.GW.AddRequest(Request::Type::LINE_SEGMENT, /*rememberForUndo=*/false), + e = SK.GetEntity(hr.entity(0)); + SK.GetEntity(e->point[0])->PointForceTo(PointAt(t)); + SK.GetEntity(e->point[1])->PointForceTo(PointAt(1)); + ConstrainPointIfCoincident(e->point[0]); + ConstrainPointIfCoincident(e->point[1]); Constraint::Constrain(Constraint::Type::PT_ON_LINE, hr.entity(1), Entity::NO_ENTITY, orig); } @@ -181,26 +194,32 @@ hRequest GraphicsWindow::ParametricCurve::CreateRequestTrimmedTo(double t, Entity::NO_ENTITY, Entity::NO_ENTITY, arc, e->h, /*other=*/arcFinish, /*other2=*/false); } else { - hr = SS.GW.AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false), - e = SK.GetEntity(hr.entity(0)); - SK.GetEntity(e->point[0])->PointForceTo(p0); - if(dtheta > 0) { - SK.GetEntity(e->point[1])->PointForceTo(PointAt(t)); - SK.GetEntity(e->point[2])->PointForceTo(PointAt(1)); + if (reuseOrig) { + e = SK.GetEntity(orig); + int i = pointf ? 2 : 1; + SK.GetEntity(e->point[i])->PointForceTo(PointAt(t)); + ConstrainPointIfCoincident(e->point[i]); } else { - SK.GetEntity(e->point[2])->PointForceTo(PointAt(t)); - SK.GetEntity(e->point[1])->PointForceTo(PointAt(1)); + hr = SS.GW.AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false), + e = SK.GetEntity(hr.entity(0)); + SK.GetEntity(e->point[0])->PointForceTo(p0); + if(dtheta > 0) { + SK.GetEntity(e->point[1])->PointForceTo(PointAt(t)); + SK.GetEntity(e->point[2])->PointForceTo(PointAt(1)); + } else { + SK.GetEntity(e->point[2])->PointForceTo(PointAt(t)); + SK.GetEntity(e->point[1])->PointForceTo(PointAt(1)); + } + ConstrainPointIfCoincident(e->point[0]); + ConstrainPointIfCoincident(e->point[1]); + ConstrainPointIfCoincident(e->point[2]); } - ConstrainPointIfCoincident(e->point[0]); - ConstrainPointIfCoincident(e->point[1]); - ConstrainPointIfCoincident(e->point[2]); // The tangency constraint alone is enough to fully constrain it, // so there's no need for more. Constraint::Constrain(Constraint::Type::CURVE_CURVE_TANGENT, Entity::NO_ENTITY, Entity::NO_ENTITY, arc, e->h, /*other=*/arcFinish, /*other2=*/(dtheta < 0)); } - return hr; } //----------------------------------------------------------------------------- @@ -388,6 +407,27 @@ void GraphicsWindow::MakeTangentArc() { SS.UndoRemember(); + if (SS.tangentArcModify) { + // Delete the coincident constraint for the removed point. + SK.constraint.ClearTags(); + for(i = 0; i < SK.constraint.n; i++) { + Constraint *cs = &(SK.constraint.elem[i]); + if(cs->group.v != activeGroup.v) continue; + if(cs->workplane.v != ActiveWorkplane().v) continue; + if(cs->type != Constraint::Type::POINTS_COINCIDENT) continue; + if (SK.GetEntity(cs->ptA)->PointGetNum().Equals(pshared)) { + cs->tag = 1; + } + } + SK.constraint.RemoveTagged(); + } else { + // Make the original entities construction, or delete them + // entirely, according to user preference. + SK.GetRequest(hreq[0])->construction = true; + SK.GetRequest(hreq[1])->construction = true; + } + + // Create and position the new tangent arc. hRequest harc = AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false); Entity *earc = SK.GetEntity(harc.entity(0)); hEntity hearc = earc->h; @@ -398,27 +438,11 @@ void GraphicsWindow::MakeTangentArc() { earc = NULL; - pc[0].CreateRequestTrimmedTo(t[0], !SS.tangentArcDeleteOld, - hent[0], hearc, /*arcFinish=*/(b == 1)); - pc[1].CreateRequestTrimmedTo(t[1], !SS.tangentArcDeleteOld, - hent[1], hearc, /*arcFinish=*/(a == 1)); - - // Now either make the original entities construction, or delete them - // entirely, according to user preference. - Request *re; - SK.request.ClearTags(); - for(re = SK.request.First(); re; re = SK.request.NextAfter(re)) { - if(re->h.v == hreq[0].v || re->h.v == hreq[1].v) { - if(SS.tangentArcDeleteOld) { - re->tag = 1; - } else { - re->construction = true; - } - } - } - if(SS.tangentArcDeleteOld) { - DeleteTaggedRequests(); - } + // Modify or duplicate the original entities and connect them to the tangent arc. + pc[0].CreateRequestTrimmedTo(t[0], SS.tangentArcModify, + hent[0], hearc, /*arcFinish=*/(b == 1), pointf[0]); + pc[1].CreateRequestTrimmedTo(t[1], SS.tangentArcModify, + hent[1], hearc, /*arcFinish=*/(a == 1), pointf[1]); } hEntity GraphicsWindow::SplitLine(hEntity he, Vector pinter) { diff --git a/src/solvespace.h b/src/solvespace.h index 395adff7..8e911929 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -638,7 +638,7 @@ public: // as special requests. double tangentArcRadius; bool tangentArcManual; - bool tangentArcDeleteOld; + bool tangentArcModify; // The platform-dependent code calls this before entering the msg loop void Init(); diff --git a/src/textscreens.cpp b/src/textscreens.cpp index 0614cb62..a0e2e9a9 100644 --- a/src/textscreens.cpp +++ b/src/textscreens.cpp @@ -643,7 +643,7 @@ void TextWindow::ScreenChangeTangentArc(int link, uint32_t v) { } case 'a': SS.tangentArcManual = !SS.tangentArcManual; break; - case 'd': SS.tangentArcDeleteOld = !SS.tangentArcDeleteOld; break; + case 'm': SS.tangentArcModify = !SS.tangentArcModify; break; } } void TextWindow::ShowTangentArc() { @@ -662,9 +662,9 @@ void TextWindow::ShowTangentArc() { Printf(false, " %Fd%f%La%s choose radius automatically%E", &ScreenChangeTangentArc, !SS.tangentArcManual ? CHECK_TRUE : CHECK_FALSE); - Printf(false, " %Fd%f%Ld%s delete original entities afterward%E", + Printf(false, " %Fd%f%Lm%s modify original entities%E", &ScreenChangeTangentArc, - SS.tangentArcDeleteOld ? CHECK_TRUE : CHECK_FALSE); + SS.tangentArcModify ? CHECK_TRUE : CHECK_FALSE); Printf(false, ""); Printf(false, "To create a tangent arc at a point,"); diff --git a/src/ui.h b/src/ui.h index bae265da..fcbcffb9 100644 --- a/src/ui.h +++ b/src/ui.h @@ -668,8 +668,8 @@ public: Vector TangentAt(double t); double LengthForAuto(); - hRequest CreateRequestTrimmedTo(double t, bool extraConstraints, - hEntity orig, hEntity arc, bool arcFinish); + void CreateRequestTrimmedTo(double t, bool reuseOrig, + hEntity orig, hEntity arc, bool arcFinish, bool pointf); void ConstrainPointIfCoincident(hEntity hpt); }; void MakeTangentArc(); @@ -718,6 +718,7 @@ public: void HitTestMakeSelection(Point2d mp); void ClearSelection(); void ClearNonexistentSelectionItems(); + /// This structure is filled by a call to GroupSelection(). struct { std::vector point; std::vector entity; @@ -740,7 +741,7 @@ public: int stylables; int constraintLabels; int withEndpoints; - int n; + int n; ///< Number of selected items } gs; void GroupSelection(); bool IsSelected(Selection *s);