diff --git a/src/modify.cpp b/src/modify.cpp index d3d4427..63e9738 100644 --- a/src/modify.cpp +++ b/src/modify.cpp @@ -8,8 +8,8 @@ #include "solvespace.h" //----------------------------------------------------------------------------- -// Replace a point-coincident constraint on oldpt with that same constraint -// on newpt. Useful when splitting or tangent arcing. +// Replace constraints on oldpt with the same constraints on newpt. +// Useful when splitting, tangent arcing, or removing bezier points. //----------------------------------------------------------------------------- void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) { int i; @@ -23,6 +23,27 @@ void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) { } } +//----------------------------------------------------------------------------- +// Remove constraints on hpt. Useful when removing bezier points. +//----------------------------------------------------------------------------- +void GraphicsWindow::RemoveConstraintsForPointBeingDeleted(hEntity hpt) { + SK.constraint.ClearTags(); + for(int i = 0; i < SK.constraint.n; i++) { + Constraint *c = &(SK.constraint.elem[i]); + if(c->ptA.v == hpt.v || c->ptB.v == hpt.v) { + c->tag = 1; + (SS.deleted.constraints)++; + if(c->type != Constraint::POINTS_COINCIDENT && + c->type != Constraint::HORIZONTAL && + c->type != Constraint::VERTICAL) + { + (SS.deleted.nonTrivialConstraints)++; + } + } + } + SK.constraint.RemoveTagged(); +} + //----------------------------------------------------------------------------- // Let's say that A is coincident with B, and B is coincident with C. This // implies that A is coincident with C; but if we delete B, then both @@ -67,9 +88,12 @@ void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) { c->tag = 1; } } - // These would get removed anyways when we regenerated, but do it now; - // that way subsequent calls of this function (if multiple coincident - // points are getting deleted) will work correctly. + // Remove constraints without waiting for regeneration; this way + // if another point takes the place of the deleted one (e.g. when + // removing control points of a bezier) the constraint doesn't + // spuriously move. Similarly, subsequent calls of this function + // (if multiple coincident points are getting deleted) will work + // correctly. SK.constraint.RemoveTagged(); // If more than one point was constrained coincident with hpt, then diff --git a/src/mouse.cpp b/src/mouse.cpp index e44b143..6526588 100644 --- a/src/mouse.cpp +++ b/src/mouse.cpp @@ -567,6 +567,18 @@ void GraphicsWindow::MouseRightUp(double x, double y) { } if(gs.comments > 0 || gs.points > 0) { AddContextMenuItem("Snap to Grid", CMNU_SNAP_TO_GRID); + + if(gs.points == 1) { + hRequest hr = gs.point[0].request(); + if(hr.v != 0) { + Request *r = SK.GetRequest(hr); + int index = r->IndexOfPoint(gs.point[0]); + if((r->type == Request::CUBIC && (index > 1 && index < r->extraPoints + 2)) || + r->type == Request::CUBIC_PERIODIC) { + AddContextMenuItem("Remove Spline Point", CMNU_REMOVE_SPLINE_PT); + } + } + } } if(gs.points == 1) { @@ -675,6 +687,36 @@ void GraphicsWindow::MouseRightUp(double x, double y) { MenuEdit(MNU_SNAP_TO_GRID); break; + case CMNU_REMOVE_SPLINE_PT: { + hRequest hr = gs.point[0].request(); + Request *r = SK.GetRequest(hr); + + int index = r->IndexOfPoint(gs.point[0]); + if(r->extraPoints == 0) oops(); + + SS.UndoRemember(); + Entity *e = SK.GetEntity(r->h.entity(0)); + + // First, fix point-coincident constraints involving this point. + // Then, remove all other constraints, since they would otherwise + // jump to an adjacent one and mess up the bezier after generation. + FixConstraintsForPointBeingDeleted(e->point[index]); + RemoveConstraintsForPointBeingDeleted(e->point[index]); + + for(int i = index; i < MAX_POINTS_IN_ENTITY - 1; i++) { + if(e->point[i + 1].v == 0) break; + Entity *p0 = SK.GetEntity(e->point[i]); + Entity *p1 = SK.GetEntity(e->point[i + 1]); + ReplacePointInConstraints(p1->h, p0->h); + p0->PointForceTo(p1->PointGetNum()); + } + r->extraPoints--; + SS.MarkGroupDirtyByEntity(gs.point[0]); + SS.ScheduleGenerateAll(); + ClearSelection(); + break; + } + case CMNU_GROUP_INFO: { hGroup hg; if(gs.entities == 1) { diff --git a/src/request.cpp b/src/request.cpp index 2da6eaf..b770e06 100644 --- a/src/request.cpp +++ b/src/request.cpp @@ -184,6 +184,18 @@ std::string Request::DescriptionString(void) { return ssprintf("r%03x-%s", h.v, s); } +int Request::IndexOfPoint(hEntity he) { + if(type == DATUM_POINT) { + return (he.v == h.entity(0).v) ? 0 : -1; + } + for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) { + if(he.v == h.entity(i + 1).v) { + return i; + } + } + return -1; +} + hParam Request::AddParam(IdList *param, hParam hp) { Param pa = {}; pa.h = hp; diff --git a/src/sketch.h b/src/sketch.h index a9a697a..7b34070 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -299,6 +299,7 @@ public: void Generate(EntityList *entity, ParamList *param); std::string DescriptionString(void); + int IndexOfPoint(hEntity he); void Clear(void) {} }; diff --git a/src/ui.h b/src/ui.h index a490d07..e1c5637 100644 --- a/src/ui.h +++ b/src/ui.h @@ -601,6 +601,7 @@ public: hEntity SplitCircle(hEntity he, Vector pinter); hEntity SplitCubic(hEntity he, Vector pinter); void ReplacePointInConstraints(hEntity oldpt, hEntity newpt); + void RemoveConstraintsForPointBeingDeleted(hEntity hpt); void FixConstraintsForRequestBeingDeleted(hRequest hr); void FixConstraintsForPointBeingDeleted(hEntity hpt); @@ -678,6 +679,7 @@ public: CMNU_OTHER_ANGLE = 0x131, CMNU_DEL_COINCIDENT = 0x132, CMNU_SNAP_TO_GRID = 0x140, + CMNU_REMOVE_SPLINE_PT = 0x141, CMNU_FIRST_STYLE = 0x40000000 }; void ContextMenuListStyles(void);