Implement splitting at point with "Split Curves at Intersection".

This commit is contained in:
Guido 2018-04-07 10:34:36 +02:00 committed by whitequark
parent 9cd0044803
commit d471e65e7d
2 changed files with 91 additions and 32 deletions

View File

@ -22,6 +22,8 @@ New sketch features:
* When adding a constraint which has a label and is redundant with another * When adding a constraint which has a label and is redundant with another
constraint, the constraint is added as a reference, avoiding an error. constraint, the constraint is added as a reference, avoiding an error.
* Datum points can be copied and pasted. * Datum points can be copied and pasted.
* "Split Curves at Intersection" can now split curves at point lying on curve,
not just at intersection of two curves.
New constraint features: New constraint features:
* When dragging an arc or rectangle point, it will be automatically * When dragging an arc or rectangle point, it will be automatically

View File

@ -603,35 +603,62 @@ void GraphicsWindow::SplitLinesOrCurves() {
} }
GroupSelection(); GroupSelection();
if(!(gs.n == 2 &&(gs.lineSegments + int n = gs.lineSegments + gs.circlesOrArcs + gs.cubics + gs.periodicCubics;
gs.circlesOrArcs + if(!((n == 2 && gs.points == 0) || (n == 1 && gs.points == 1))) {
gs.cubics + Error(_("Select two entities that intersect each other "
gs.periodicCubics) == 2)) "(e.g. two lines/circles/arcs or a line/circle/arc and a point)."));
{
Error(_("Select two entities that intersect each other (e.g. two lines "
"or two circles or a circle and a line)."));
return; return;
} }
bool splitAtPoint = (gs.points == 1);
hEntity ha = gs.entity[0], hEntity ha = gs.entity[0],
hb = gs.entity[1]; hb = splitAtPoint ? gs.point[0] : gs.entity[1];
Entity *ea = SK.GetEntity(ha), Entity *ea = SK.GetEntity(ha),
*eb = SK.GetEntity(hb); *eb = SK.GetEntity(hb);
SPointList inters = {};
// Compute the possibly-rational Bezier curves for each of these entities SBezierList sbla = {},
SBezierList sbla, sblb;
sbla = {};
sblb = {}; sblb = {};
Vector pi = Vector::From(0, 0, 0);
SK.constraint.ClearTags();
// First, decide the point where we're going to make the split.
bool foundInters = false;
if(splitAtPoint) {
// One of the entities is a point, and this point must be on the other entity.
// Verify that a corresponding point-coincident constraint exists for the point/entity.
Vector p0, p1;
if(ea->type == Entity::Type::LINE_SEGMENT) {
p0 = ea->EndpointStart();
p1 = ea->EndpointFinish();
}
for(Constraint &c : SK.constraint) {
if(c.ptA.request().v == hb.request().v &&
c.entityA.request().v == ha.request().v) {
pi = SK.GetEntity(c.ptA)->PointGetNum();
if(ea->type == Entity::Type::LINE_SEGMENT && !pi.OnLineSegment(p0, p1)) {
// The point isn't between line endpoints, so there isn't an actual
// intersection.
continue;
}
c.tag = 1;
foundInters = true;
break;
}
}
} else {
// Compute the possibly-rational Bezier curves for each of these non-point entities...
ea->GenerateBezierCurves(&sbla); ea->GenerateBezierCurves(&sbla);
eb->GenerateBezierCurves(&sblb); eb->GenerateBezierCurves(&sblb);
// and then compute the points where they intersect, based on those curves. // ... and then compute the points where they intersect, based on those curves.
SPointList inters = {};
sbla.AllIntersectionsWith(&sblb, &inters); sbla.AllIntersectionsWith(&sblb, &inters);
// If there's multiple points, then take the one closest to the mouse pointer.
if(inters.l.n > 0) { if(inters.l.n > 0) {
Vector pi = Vector::From(0, 0, 0);
// If there's multiple points, then take the one closest to the
// mouse pointer.
double dmin = VERY_POSITIVE; double dmin = VERY_POSITIVE;
SPoint *sp; SPoint *sp;
for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) { for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) {
@ -641,16 +668,47 @@ void GraphicsWindow::SplitLinesOrCurves() {
pi = sp->p; pi = sp->p;
} }
} }
}
foundInters = true;
}
// Then, actually split the entities.
if(foundInters) {
SS.UndoRemember(); SS.UndoRemember();
// Remove any constraints we're going to replace.
SK.constraint.RemoveTagged();
hEntity hia = SplitEntity(ha, pi), hEntity hia = SplitEntity(ha, pi),
hib = SplitEntity(hb, pi); hib = {};
// SplitEntity adds the coincident constraints to join the split halves // SplitEntity adds the coincident constraints to join the split halves
// of each original entity; and then we add the constraint to join // of each original entity; and then we add the constraint to join
// the two entities together at the split point. // the two entities together at the split point.
if(splitAtPoint) {
// Remove datum point, as it has now been superseded by the split point.
SK.request.ClearTags();
for(Request &r : SK.request) {
if(r.h.v == hb.request().v) {
if(r.type == Request::Type::DATUM_POINT) {
// Delete datum point.
r.tag = 1;
FixConstraintsForRequestBeingDeleted(r.h);
} else {
// Add constraint if not datum point, but endpoint of line/arc etc.
Constraint::ConstrainCoincident(hia, hb);
}
break;
}
}
SK.request.RemoveTagged();
} else {
// Split second non-point entity and add constraint.
hib = SplitEntity(hb, pi);
if(hia.v && hib.v) { if(hia.v && hib.v) {
Constraint::ConstrainCoincident(hia, hib); Constraint::ConstrainCoincident(hia, hib);
} }
}
} else { } else {
Error(_("Can't split; no intersection found.")); Error(_("Can't split; no intersection found."));
} }
@ -661,4 +719,3 @@ void GraphicsWindow::SplitLinesOrCurves() {
sblb.Clear(); sblb.Clear();
ClearSelection(); ClearSelection();
} }