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

pull/252/merge
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
constraint, the constraint is added as a reference, avoiding an error.
* 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:
* When dragging an arc or rectangle point, it will be automatically

View File

@ -603,53 +603,111 @@ void GraphicsWindow::SplitLinesOrCurves() {
}
GroupSelection();
if(!(gs.n == 2 &&(gs.lineSegments +
gs.circlesOrArcs +
gs.cubics +
gs.periodicCubics) == 2))
{
Error(_("Select two entities that intersect each other (e.g. two lines "
"or two circles or a circle and a line)."));
int n = gs.lineSegments + gs.circlesOrArcs + gs.cubics + gs.periodicCubics;
if(!((n == 2 && gs.points == 0) || (n == 1 && gs.points == 1))) {
Error(_("Select two entities that intersect each other "
"(e.g. two lines/circles/arcs or a line/circle/arc and a point)."));
return;
}
bool splitAtPoint = (gs.points == 1);
hEntity ha = gs.entity[0],
hb = gs.entity[1];
hb = splitAtPoint ? gs.point[0] : gs.entity[1];
Entity *ea = SK.GetEntity(ha),
*eb = SK.GetEntity(hb);
// Compute the possibly-rational Bezier curves for each of these entities
SBezierList sbla, sblb;
sbla = {};
sblb = {};
ea->GenerateBezierCurves(&sbla);
eb->GenerateBezierCurves(&sblb);
// and then compute the points where they intersect, based on those curves.
SPointList inters = {};
sbla.AllIntersectionsWith(&sblb, &inters);
SBezierList sbla = {},
sblb = {};
Vector pi = Vector::From(0, 0, 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;
SPoint *sp;
for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) {
double d = ProjectPoint(sp->p).DistanceTo(currentMousePosition);
if(d < dmin) {
dmin = d;
pi = sp->p;
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);
eb->GenerateBezierCurves(&sblb);
// ... and then compute the points where they intersect, based on those curves.
sbla.AllIntersectionsWith(&sblb, &inters);
// If there's multiple points, then take the one closest to the mouse pointer.
if(inters.l.n > 0) {
double dmin = VERY_POSITIVE;
SPoint *sp;
for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) {
double d = ProjectPoint(sp->p).DistanceTo(currentMousePosition);
if(d < dmin) {
dmin = d;
pi = sp->p;
}
}
}
foundInters = true;
}
// Then, actually split the entities.
if(foundInters) {
SS.UndoRemember();
// Remove any constraints we're going to replace.
SK.constraint.RemoveTagged();
hEntity hia = SplitEntity(ha, pi),
hib = SplitEntity(hb, pi);
hib = {};
// SplitEntity adds the coincident constraints to join the split halves
// of each original entity; and then we add the constraint to join
// the two entities together at the split point.
if(hia.v && hib.v) {
Constraint::ConstrainCoincident(hia, hib);
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) {
Constraint::ConstrainCoincident(hia, hib);
}
}
} else {
Error(_("Can't split; no intersection found."));
@ -661,4 +719,3 @@ void GraphicsWindow::SplitLinesOrCurves() {
sblb.Clear();
ClearSelection();
}