2013-07-28 22:08:34 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// User-initiated (not parametric) operations to modify our sketch, by
|
|
|
|
// changing the requests, like to round a corner or split curves where they
|
|
|
|
// intersect.
|
|
|
|
//
|
|
|
|
// Copyright 2008-2013 Jonathan Westhues.
|
|
|
|
//-----------------------------------------------------------------------------
|
2009-01-03 12:27:33 +00:00
|
|
|
#include "solvespace.h"
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Replace a point-coincident constraint on oldpt with that same constraint
|
|
|
|
// on newpt. Useful when splitting or tangent arcing.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) {
|
|
|
|
int i;
|
2009-04-19 05:53:16 +00:00
|
|
|
for(i = 0; i < SK.constraint.n; i++) {
|
|
|
|
Constraint *c = &(SK.constraint.elem[i]);
|
2009-01-03 12:27:33 +00:00
|
|
|
|
|
|
|
if(c->type == Constraint::POINTS_COINCIDENT) {
|
|
|
|
if(c->ptA.v == oldpt.v) c->ptA = newpt;
|
|
|
|
if(c->ptB.v == oldpt.v) c->ptB = newpt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// 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
|
|
|
|
// constraints must be deleted too (since they reference B), and A is no
|
|
|
|
// longer constrained to C. This routine adds back that constraint.
|
|
|
|
//-----------------------------------------------------------------------------
|
2009-07-08 09:36:18 +00:00
|
|
|
void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
|
|
|
|
Request *r = SK.GetRequest(hr);
|
|
|
|
if(r->group.v != SS.GW.activeGroup.v) return;
|
|
|
|
|
|
|
|
Entity *e;
|
|
|
|
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
|
|
|
if(!(e->h.isFromRequest())) continue;
|
|
|
|
if(e->h.request().v != hr.v) continue;
|
|
|
|
|
|
|
|
if(e->type != Entity::POINT_IN_2D &&
|
|
|
|
e->type != Entity::POINT_IN_3D)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a point generated by the request being deleted; so fix
|
|
|
|
// the constraints for that.
|
|
|
|
FixConstraintsForPointBeingDeleted(e->h);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
|
|
|
|
List<hEntity> ld;
|
|
|
|
ZERO(&ld);
|
|
|
|
|
|
|
|
Constraint *c;
|
|
|
|
SK.constraint.ClearTags();
|
|
|
|
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
|
|
|
if(c->type != Constraint::POINTS_COINCIDENT) continue;
|
|
|
|
if(c->group.v != SS.GW.activeGroup.v) continue;
|
|
|
|
|
|
|
|
if(c->ptA.v == hpt.v) {
|
|
|
|
ld.Add(&(c->ptB));
|
|
|
|
c->tag = 1;
|
|
|
|
}
|
|
|
|
if(c->ptB.v == hpt.v) {
|
|
|
|
ld.Add(&(c->ptA));
|
|
|
|
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.
|
|
|
|
SK.constraint.RemoveTagged();
|
|
|
|
|
|
|
|
// If more than one point was constrained coincident with hpt, then
|
|
|
|
// those two points were implicitly coincident with each other. By
|
|
|
|
// deleting hpt (and all constraints that mention it), we will delete
|
|
|
|
// that relationship. So put it back here now.
|
|
|
|
int i;
|
|
|
|
for(i = 1; i < ld.n; i++) {
|
|
|
|
Constraint::ConstrainCoincident(ld.elem[i-1], ld.elem[i]);
|
|
|
|
}
|
|
|
|
ld.Clear();
|
|
|
|
}
|
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// A curve by its parametric equation, helper functions for computing tangent
|
|
|
|
// arcs by a numerical method.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void GraphicsWindow::ParametricCurve::MakeFromEntity(hEntity he, bool reverse) {
|
|
|
|
ZERO(this);
|
|
|
|
Entity *e = SK.GetEntity(he);
|
|
|
|
if(e->type == Entity::LINE_SEGMENT) {
|
|
|
|
isLine = true;
|
|
|
|
p0 = e->EndpointStart(),
|
|
|
|
p1 = e->EndpointFinish();
|
|
|
|
if(reverse) {
|
|
|
|
SWAP(Vector, p0, p1);
|
|
|
|
}
|
|
|
|
} else if(e->type == Entity::ARC_OF_CIRCLE) {
|
|
|
|
isLine = false;
|
|
|
|
p0 = SK.GetEntity(e->point[0])->PointGetNum();
|
|
|
|
Vector pe = SK.GetEntity(e->point[1])->PointGetNum();
|
|
|
|
r = (pe.Minus(p0)).Magnitude();
|
|
|
|
e->ArcGetAngles(&theta0, &theta1, &dtheta);
|
|
|
|
if(reverse) {
|
|
|
|
SWAP(double, theta0, theta1);
|
|
|
|
dtheta = -dtheta;
|
|
|
|
}
|
|
|
|
EntityBase *wrkpln = SK.GetEntity(e->workplane)->Normal();
|
|
|
|
u = wrkpln->NormalU();
|
|
|
|
v = wrkpln->NormalV();
|
|
|
|
} else {
|
|
|
|
oops();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
double GraphicsWindow::ParametricCurve::LengthForAuto(void) {
|
|
|
|
if(isLine) {
|
|
|
|
// Allow a third of the line to disappear with auto radius
|
|
|
|
return (p1.Minus(p0)).Magnitude() / 3;
|
|
|
|
} else {
|
|
|
|
// But only a twentieth of the arc; shorter means fewer numerical
|
|
|
|
// problems since the curve is more linear over shorter sections.
|
|
|
|
return (fabs(dtheta)*r)/20;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Vector GraphicsWindow::ParametricCurve::PointAt(double t) {
|
|
|
|
if(isLine) {
|
|
|
|
return p0.Plus((p1.Minus(p0)).ScaledBy(t));
|
|
|
|
} else {
|
|
|
|
double theta = theta0 + dtheta*t;
|
|
|
|
return p0.Plus(u.ScaledBy(r*cos(theta)).Plus(v.ScaledBy(r*sin(theta))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Vector GraphicsWindow::ParametricCurve::TangentAt(double t) {
|
|
|
|
if(isLine) {
|
|
|
|
return p1.Minus(p0);
|
|
|
|
} else {
|
|
|
|
double theta = theta0 + dtheta*t;
|
|
|
|
Vector t = u.ScaledBy(-r*sin(theta)).Plus(v.ScaledBy(r*cos(theta)));
|
|
|
|
t = t.ScaledBy(dtheta);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hRequest GraphicsWindow::ParametricCurve::CreateRequestTrimmedTo(double t,
|
|
|
|
bool extraConstraints, hEntity orig, hEntity arc, bool arcFinish)
|
|
|
|
{
|
|
|
|
hRequest hr;
|
|
|
|
Entity *e;
|
|
|
|
if(isLine) {
|
|
|
|
hr = SS.GW.AddRequest(Request::LINE_SEGMENT, 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) {
|
|
|
|
Constraint::Constrain(Constraint::PT_ON_LINE,
|
|
|
|
hr.entity(1), Entity::NO_ENTITY, orig);
|
|
|
|
}
|
|
|
|
Constraint::Constrain(Constraint::ARC_LINE_TANGENT,
|
|
|
|
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
|
|
|
arc, e->h, arcFinish, false);
|
|
|
|
} else {
|
|
|
|
hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, 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]);
|
|
|
|
// The tangency constraint alone is enough to fully constrain it,
|
|
|
|
// so there's no need for more.
|
|
|
|
Constraint::Constrain(Constraint::CURVE_CURVE_TANGENT,
|
|
|
|
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
|
|
|
arc, e->h, arcFinish, (dtheta < 0));
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// If a point in the same group as hpt, and numerically coincident with hpt,
|
|
|
|
// happens to exist, then constrain that point coincident to hpt.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void GraphicsWindow::ParametricCurve::ConstrainPointIfCoincident(hEntity hpt) {
|
|
|
|
Entity *e, *pt;
|
|
|
|
pt = SK.GetEntity(hpt);
|
|
|
|
Vector ev, ptv;
|
|
|
|
ptv = pt->PointGetNum();
|
|
|
|
|
|
|
|
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
|
|
|
if(e->h.v == pt->h.v) continue;
|
|
|
|
if(!e->IsPoint()) continue;
|
|
|
|
if(e->group.v != pt->group.v) continue;
|
|
|
|
if(e->workplane.v != pt->workplane.v) continue;
|
|
|
|
|
|
|
|
ev = e->PointGetNum();
|
|
|
|
if(!ev.Equals(ptv)) continue;
|
|
|
|
|
|
|
|
Constraint::ConstrainCoincident(hpt, e->h);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-03 12:27:33 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// A single point must be selected when this function is called. We find two
|
|
|
|
// non-construction line segments that join at this point, and create a
|
|
|
|
// tangent arc joining them.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void GraphicsWindow::MakeTangentArc(void) {
|
|
|
|
if(!LockedInWorkplane()) {
|
|
|
|
Error("Must be sketching in workplane to create tangent "
|
|
|
|
"arc.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
// The point corresponding to the vertex to be rounded.
|
2009-04-19 05:53:16 +00:00
|
|
|
Vector pshared = SK.GetEntity(gs.point[0])->PointGetNum();
|
2009-01-03 12:27:33 +00:00
|
|
|
ClearSelection();
|
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
// First, find two requests (that are not construction, and that are
|
|
|
|
// in our group and workplane) that generate entities that have an
|
|
|
|
// endpoint at our vertex to be rounded.
|
2009-01-03 12:27:33 +00:00
|
|
|
int i, c = 0;
|
2010-05-16 16:36:23 +00:00
|
|
|
Entity *ent[2];
|
|
|
|
Request *req[2];
|
|
|
|
hRequest hreq[2];
|
|
|
|
hEntity hent[2];
|
|
|
|
bool pointf[2];
|
2009-04-19 05:53:16 +00:00
|
|
|
for(i = 0; i < SK.request.n; i++) {
|
|
|
|
Request *r = &(SK.request.elem[i]);
|
2009-01-03 12:27:33 +00:00
|
|
|
if(r->group.v != activeGroup.v) continue;
|
2010-05-16 16:36:23 +00:00
|
|
|
if(r->workplane.v != ActiveWorkplane().v) continue;
|
2009-01-03 12:27:33 +00:00
|
|
|
if(r->construction) continue;
|
2010-05-16 16:36:23 +00:00
|
|
|
if(r->type != Request::LINE_SEGMENT &&
|
|
|
|
r->type != Request::ARC_OF_CIRCLE)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2009-04-19 05:53:16 +00:00
|
|
|
Entity *e = SK.GetEntity(r->h.entity(0));
|
2010-05-16 16:36:23 +00:00
|
|
|
Vector ps = e->EndpointStart(),
|
|
|
|
pf = e->EndpointFinish();
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
if(ps.Equals(pshared) || pf.Equals(pshared)) {
|
2009-01-03 12:27:33 +00:00
|
|
|
if(c < 2) {
|
2010-05-16 16:36:23 +00:00
|
|
|
// We record the entity and request and their handles,
|
|
|
|
// and whether the vertex to be rounded is the start or
|
|
|
|
// finish of this entity.
|
|
|
|
ent[c] = e;
|
|
|
|
hent[c] = e->h;
|
|
|
|
req[c] = r;
|
|
|
|
hreq[c] = r->h;
|
|
|
|
pointf[c] = (pf.Equals(pshared));
|
2009-01-03 12:27:33 +00:00
|
|
|
}
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(c != 2) {
|
2010-05-16 16:36:23 +00:00
|
|
|
Error("To create a tangent arc, select a point where two "
|
|
|
|
"non-construction lines or cicles in this group and "
|
|
|
|
"workplane join.");
|
2009-01-03 12:27:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-04-19 05:53:16 +00:00
|
|
|
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
2009-01-03 12:27:33 +00:00
|
|
|
Vector wn = wrkpl->Normal()->NormalN();
|
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
// Based on these two entities, we make the objects that we'll use to
|
|
|
|
// numerically find the tangent arc.
|
|
|
|
ParametricCurve pc[2];
|
|
|
|
pc[0].MakeFromEntity(ent[0]->h, pointf[0]);
|
|
|
|
pc[1].MakeFromEntity(ent[1]->h, pointf[1]);
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
// And thereafter we mustn't touch the entity or req ptrs,
|
|
|
|
// because the new requests/entities we add might force a
|
|
|
|
// realloc.
|
|
|
|
memset(ent, 0, sizeof(ent));
|
|
|
|
memset(req, 0, sizeof(req));
|
|
|
|
|
|
|
|
Vector pinter;
|
|
|
|
double r, vv;
|
|
|
|
// We now do Newton iterations to find the tangent arc, and its positions
|
|
|
|
// t back along the two curves, starting from shared point of the curves
|
|
|
|
// at t = 0. Lots of iterations helps convergence, and this is still
|
|
|
|
// ~10 ms for everything.
|
|
|
|
int iters = 1000;
|
|
|
|
double t[2] = { 0, 0 }, tp[2];
|
|
|
|
for(i = 0; i < iters + 20; i++) {
|
|
|
|
Vector p0 = pc[0].PointAt(t[0]),
|
|
|
|
p1 = pc[1].PointAt(t[1]),
|
|
|
|
t0 = pc[0].TangentAt(t[0]),
|
|
|
|
t1 = pc[1].TangentAt(t[1]);
|
|
|
|
|
|
|
|
pinter = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
|
|
|
|
p1, p1.Plus(t1),
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
|
|
|
|
// The sign of vv determines whether shortest distance is
|
|
|
|
// clockwise or anti-clockwise.
|
|
|
|
Vector v = (wn.Cross(t0)).WithMagnitude(1);
|
|
|
|
vv = t1.Dot(v);
|
|
|
|
|
|
|
|
double dot = (t0.WithMagnitude(1)).Dot(t1.WithMagnitude(1));
|
|
|
|
double theta = acos(dot);
|
|
|
|
|
|
|
|
if(SS.tangentArcManual) {
|
|
|
|
r = SS.tangentArcRadius;
|
|
|
|
} else {
|
|
|
|
r = 200/scale;
|
|
|
|
// Set the radius so that no more than one third of the
|
|
|
|
// line segment disappears.
|
|
|
|
r = min(r, pc[0].LengthForAuto()*tan(theta/2));
|
|
|
|
r = min(r, pc[1].LengthForAuto()*tan(theta/2));;
|
|
|
|
}
|
|
|
|
// We are source-stepping the radius, to improve convergence. So
|
|
|
|
// ramp that for most of the iterations, and then do a few at
|
|
|
|
// the end with that constant for polishing.
|
|
|
|
if(i < iters) {
|
|
|
|
r *= 0.1 + 0.9*i/((double)iters);
|
|
|
|
}
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
// The distance from the intersection of the lines to the endpoint
|
|
|
|
// of the arc, along each line.
|
|
|
|
double el = r/tan(theta/2);
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
// Compute the endpoints of the arc, for each curve
|
|
|
|
Vector pa0 = pinter.Plus(t0.WithMagnitude(el)),
|
|
|
|
pa1 = pinter.Plus(t1.WithMagnitude(el));
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
tp[0] = t[0];
|
|
|
|
tp[1] = t[1];
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
// And convert those points to parameter values along the curve.
|
|
|
|
t[0] += (pa0.Minus(p0)).DivPivoting(t0);
|
|
|
|
t[1] += (pa1.Minus(p1)).DivPivoting(t1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stupid check for convergence, and for an out of range result (as
|
|
|
|
// we would get, for example, if the line is too short to fit the
|
|
|
|
// rounding arc).
|
|
|
|
if(fabs(tp[0] - t[0]) > 1e-3 || fabs(tp[1] - t[1]) > 1e-3 ||
|
|
|
|
t[0] < 0.01 || t[1] < 0.01 ||
|
|
|
|
t[0] > 0.99 || t[1] > 0.99 ||
|
|
|
|
isnan(t[0]) || isnan(t[1]))
|
|
|
|
{
|
|
|
|
Error("Couldn't round this corner. Try a smaller radius, or try "
|
|
|
|
"creating the desired geometry by hand with tangency "
|
|
|
|
"constraints.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the location of the center of the arc
|
|
|
|
Vector center = pc[0].PointAt(t[0]),
|
|
|
|
v0inter = pinter.Minus(center);
|
2009-01-03 12:27:33 +00:00
|
|
|
int a, b;
|
|
|
|
if(vv < 0) {
|
|
|
|
a = 1; b = 2;
|
2010-05-16 16:36:23 +00:00
|
|
|
center = center.Minus(v0inter.Cross(wn).WithMagnitude(r));
|
2009-01-03 12:27:33 +00:00
|
|
|
} else {
|
|
|
|
a = 2; b = 1;
|
2010-05-16 16:36:23 +00:00
|
|
|
center = center.Plus(v0inter.Cross(wn).WithMagnitude(r));
|
2009-01-03 12:27:33 +00:00
|
|
|
}
|
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
SS.UndoRemember();
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2010-05-16 16:36:23 +00:00
|
|
|
hRequest harc = AddRequest(Request::ARC_OF_CIRCLE, false);
|
|
|
|
Entity *earc = SK.GetEntity(harc.entity(0));
|
|
|
|
hEntity hearc = earc->h;
|
|
|
|
|
|
|
|
SK.GetEntity(earc->point[0])->PointForceTo(center);
|
|
|
|
SK.GetEntity(earc->point[a])->PointForceTo(pc[0].PointAt(t[0]));
|
|
|
|
SK.GetEntity(earc->point[b])->PointForceTo(pc[1].PointAt(t[1]));
|
|
|
|
|
|
|
|
earc = NULL;
|
|
|
|
|
|
|
|
pc[0].CreateRequestTrimmedTo(t[0], !SS.tangentArcDeleteOld,
|
|
|
|
hent[0], hearc, (b == 1));
|
|
|
|
pc[1].CreateRequestTrimmedTo(t[1], !SS.tangentArcDeleteOld,
|
|
|
|
hent[1], hearc, (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();
|
|
|
|
}
|
2009-01-03 12:27:33 +00:00
|
|
|
|
|
|
|
SS.later.generateAll = true;
|
|
|
|
}
|
|
|
|
|
2009-07-07 08:21:59 +00:00
|
|
|
hEntity GraphicsWindow::SplitLine(hEntity he, Vector pinter) {
|
2009-01-03 12:27:33 +00:00
|
|
|
// Save the original endpoints, since we're about to delete this entity.
|
2009-04-19 05:53:16 +00:00
|
|
|
Entity *e01 = SK.GetEntity(he);
|
2009-01-03 12:27:33 +00:00
|
|
|
hEntity hep0 = e01->point[0], hep1 = e01->point[1];
|
2009-04-19 05:53:16 +00:00
|
|
|
Vector p0 = SK.GetEntity(hep0)->PointGetNum(),
|
|
|
|
p1 = SK.GetEntity(hep1)->PointGetNum();
|
2009-01-03 12:27:33 +00:00
|
|
|
|
|
|
|
// Add the two line segments this one gets split into.
|
|
|
|
hRequest r0i = AddRequest(Request::LINE_SEGMENT, false),
|
|
|
|
ri1 = AddRequest(Request::LINE_SEGMENT, false);
|
|
|
|
// Don't get entities till after adding, realloc issues
|
|
|
|
|
2009-04-19 05:53:16 +00:00
|
|
|
Entity *e0i = SK.GetEntity(r0i.entity(0)),
|
|
|
|
*ei1 = SK.GetEntity(ri1.entity(0));
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2009-04-19 05:53:16 +00:00
|
|
|
SK.GetEntity(e0i->point[0])->PointForceTo(p0);
|
|
|
|
SK.GetEntity(e0i->point[1])->PointForceTo(pinter);
|
|
|
|
SK.GetEntity(ei1->point[0])->PointForceTo(pinter);
|
|
|
|
SK.GetEntity(ei1->point[1])->PointForceTo(p1);
|
2009-01-03 12:27:33 +00:00
|
|
|
|
|
|
|
ReplacePointInConstraints(hep0, e0i->point[0]);
|
|
|
|
ReplacePointInConstraints(hep1, ei1->point[1]);
|
2009-07-07 08:21:59 +00:00
|
|
|
Constraint::ConstrainCoincident(e0i->point[1], ei1->point[0]);
|
|
|
|
return e0i->point[1];
|
2009-01-03 12:27:33 +00:00
|
|
|
}
|
|
|
|
|
2009-07-07 08:21:59 +00:00
|
|
|
hEntity GraphicsWindow::SplitCircle(hEntity he, Vector pinter) {
|
2009-04-19 05:53:16 +00:00
|
|
|
Entity *circle = SK.GetEntity(he);
|
2009-01-03 12:27:33 +00:00
|
|
|
if(circle->type == Entity::CIRCLE) {
|
|
|
|
// Start with an unbroken circle, split it into a 360 degree arc.
|
2009-04-19 05:53:16 +00:00
|
|
|
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
|
2009-01-03 12:27:33 +00:00
|
|
|
|
|
|
|
circle = NULL; // shortly invalid!
|
|
|
|
hRequest hr = AddRequest(Request::ARC_OF_CIRCLE, false);
|
|
|
|
|
2009-04-19 05:53:16 +00:00
|
|
|
Entity *arc = SK.GetEntity(hr.entity(0));
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2009-04-19 05:53:16 +00:00
|
|
|
SK.GetEntity(arc->point[0])->PointForceTo(center);
|
|
|
|
SK.GetEntity(arc->point[1])->PointForceTo(pinter);
|
|
|
|
SK.GetEntity(arc->point[2])->PointForceTo(pinter);
|
2009-07-07 08:21:59 +00:00
|
|
|
|
|
|
|
Constraint::ConstrainCoincident(arc->point[1], arc->point[2]);
|
|
|
|
return arc->point[1];
|
2009-01-03 12:27:33 +00:00
|
|
|
} else {
|
|
|
|
// Start with an arc, break it in to two arcs
|
2009-07-07 08:21:59 +00:00
|
|
|
hEntity hc = circle->point[0],
|
|
|
|
hs = circle->point[1],
|
|
|
|
hf = circle->point[2];
|
|
|
|
Vector center = SK.GetEntity(hc)->PointGetNum(),
|
|
|
|
start = SK.GetEntity(hs)->PointGetNum(),
|
|
|
|
finish = SK.GetEntity(hf)->PointGetNum();
|
2009-01-03 12:27:33 +00:00
|
|
|
|
|
|
|
circle = NULL; // shortly invalid!
|
|
|
|
hRequest hr0 = AddRequest(Request::ARC_OF_CIRCLE, false),
|
|
|
|
hr1 = AddRequest(Request::ARC_OF_CIRCLE, false);
|
|
|
|
|
2009-04-19 05:53:16 +00:00
|
|
|
Entity *arc0 = SK.GetEntity(hr0.entity(0)),
|
|
|
|
*arc1 = SK.GetEntity(hr1.entity(0));
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2009-04-19 05:53:16 +00:00
|
|
|
SK.GetEntity(arc0->point[0])->PointForceTo(center);
|
|
|
|
SK.GetEntity(arc0->point[1])->PointForceTo(start);
|
|
|
|
SK.GetEntity(arc0->point[2])->PointForceTo(pinter);
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2009-04-19 05:53:16 +00:00
|
|
|
SK.GetEntity(arc1->point[0])->PointForceTo(center);
|
|
|
|
SK.GetEntity(arc1->point[1])->PointForceTo(pinter);
|
|
|
|
SK.GetEntity(arc1->point[2])->PointForceTo(finish);
|
2009-07-07 08:21:59 +00:00
|
|
|
|
|
|
|
ReplacePointInConstraints(hs, arc0->point[1]);
|
|
|
|
ReplacePointInConstraints(hf, arc1->point[2]);
|
|
|
|
Constraint::ConstrainCoincident(arc0->point[2], arc1->point[1]);
|
|
|
|
return arc0->point[2];
|
2009-01-03 12:27:33 +00:00
|
|
|
}
|
2009-07-07 08:21:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hEntity GraphicsWindow::SplitCubic(hEntity he, Vector pinter) {
|
|
|
|
// Save the original endpoints, since we're about to delete this entity.
|
|
|
|
Entity *e01 = SK.GetEntity(he);
|
2009-11-10 03:57:24 +00:00
|
|
|
SBezierList sbl;
|
|
|
|
ZERO(&sbl);
|
|
|
|
e01->GenerateBezierCurves(&sbl);
|
2009-07-07 08:21:59 +00:00
|
|
|
|
2009-11-10 03:57:24 +00:00
|
|
|
hEntity hep0 = e01->point[0],
|
|
|
|
hep1 = e01->point[3+e01->extraPoints],
|
|
|
|
hep0n = Entity::NO_ENTITY, // the new start point
|
|
|
|
hep1n = Entity::NO_ENTITY, // the new finish point
|
|
|
|
hepin = Entity::NO_ENTITY; // the intersection point
|
2009-07-07 08:21:59 +00:00
|
|
|
|
2009-11-10 03:57:24 +00:00
|
|
|
// The curve may consist of multiple cubic segments. So find which one
|
|
|
|
// contains the intersection point.
|
2009-07-07 08:21:59 +00:00
|
|
|
double t;
|
2009-11-10 03:57:24 +00:00
|
|
|
int i, j;
|
|
|
|
for(i = 0; i < sbl.l.n; i++) {
|
|
|
|
SBezier *sb = &(sbl.l.elem[i]);
|
|
|
|
if(sb->deg != 3) oops();
|
|
|
|
|
|
|
|
sb->ClosestPointTo(pinter, &t, false);
|
|
|
|
if(pinter.Equals(sb->PointAt(t))) {
|
|
|
|
// Split that segment at the intersection.
|
|
|
|
SBezier b0i, bi1, b01 = *sb;
|
|
|
|
b01.SplitAt(t, &b0i, &bi1);
|
|
|
|
|
|
|
|
// Add the two cubic segments this one gets split into.
|
|
|
|
hRequest r0i = AddRequest(Request::CUBIC, false),
|
|
|
|
ri1 = AddRequest(Request::CUBIC, false);
|
|
|
|
// Don't get entities till after adding, realloc issues
|
|
|
|
|
|
|
|
Entity *e0i = SK.GetEntity(r0i.entity(0)),
|
|
|
|
*ei1 = SK.GetEntity(ri1.entity(0));
|
|
|
|
|
|
|
|
for(j = 0; j <= 3; j++) {
|
|
|
|
SK.GetEntity(e0i->point[j])->PointForceTo(b0i.ctrl[j]);
|
|
|
|
}
|
|
|
|
for(j = 0; j <= 3; j++) {
|
|
|
|
SK.GetEntity(ei1->point[j])->PointForceTo(bi1.ctrl[j]);
|
|
|
|
}
|
2009-07-07 08:21:59 +00:00
|
|
|
|
2009-11-10 03:57:24 +00:00
|
|
|
Constraint::ConstrainCoincident(e0i->point[3], ei1->point[0]);
|
|
|
|
if(i == 0) hep0n = e0i->point[0];
|
|
|
|
hep1n = ei1->point[3];
|
|
|
|
hepin = e0i->point[3];
|
|
|
|
} else {
|
|
|
|
hRequest r = AddRequest(Request::CUBIC, false);
|
|
|
|
Entity *e = SK.GetEntity(r.entity(0));
|
2009-07-07 08:21:59 +00:00
|
|
|
|
2009-11-10 03:57:24 +00:00
|
|
|
for(j = 0; j <= 3; j++) {
|
|
|
|
SK.GetEntity(e->point[j])->PointForceTo(sb->ctrl[j]);
|
|
|
|
}
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2009-11-10 03:57:24 +00:00
|
|
|
if(i == 0) hep0n = e->point[0];
|
|
|
|
hep1n = e->point[3];
|
|
|
|
}
|
|
|
|
}
|
2009-07-07 08:21:59 +00:00
|
|
|
|
2009-11-10 03:57:24 +00:00
|
|
|
sbl.Clear();
|
2009-07-07 08:21:59 +00:00
|
|
|
|
2009-11-10 03:57:24 +00:00
|
|
|
ReplacePointInConstraints(hep0, hep0n);
|
|
|
|
ReplacePointInConstraints(hep1, hep1n);
|
|
|
|
return hepin;
|
2009-07-07 08:21:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hEntity GraphicsWindow::SplitEntity(hEntity he, Vector pinter) {
|
|
|
|
Entity *e = SK.GetEntity(he);
|
|
|
|
int entityType = e->type;
|
|
|
|
|
|
|
|
hEntity ret;
|
|
|
|
if(e->IsCircle()) {
|
|
|
|
ret = SplitCircle(he, pinter);
|
|
|
|
} else if(e->type == Entity::LINE_SEGMENT) {
|
|
|
|
ret = SplitLine(he, pinter);
|
2009-11-10 03:57:24 +00:00
|
|
|
} else if(e->type == Entity::CUBIC || e->type == Entity::CUBIC_PERIODIC) {
|
2009-07-07 08:21:59 +00:00
|
|
|
ret = SplitCubic(he, pinter);
|
|
|
|
} else {
|
|
|
|
Error("Couldn't split this entity; lines, circles, or cubics only.");
|
|
|
|
return Entity::NO_ENTITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, delete the request that generated the original entity.
|
2009-10-22 14:02:08 +00:00
|
|
|
int reqType = EntReqTable::GetRequestForEntity(entityType);
|
2009-04-19 05:53:16 +00:00
|
|
|
SK.request.ClearTags();
|
2009-10-22 14:02:08 +00:00
|
|
|
for(int i = 0; i < SK.request.n; i++) {
|
2009-04-19 05:53:16 +00:00
|
|
|
Request *r = &(SK.request.elem[i]);
|
2009-01-03 12:27:33 +00:00
|
|
|
if(r->group.v != activeGroup.v) continue;
|
2009-07-07 08:21:59 +00:00
|
|
|
if(r->type != reqType) continue;
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2009-07-07 08:21:59 +00:00
|
|
|
// If the user wants to keep the old entities around, they can just
|
|
|
|
// mark them construction first.
|
2009-01-03 12:27:33 +00:00
|
|
|
if(he.v == r->h.entity(0).v && !r->construction) {
|
|
|
|
r->tag = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DeleteTaggedRequests();
|
2009-07-07 08:21:59 +00:00
|
|
|
|
|
|
|
return ret;
|
2009-01-03 12:27:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GraphicsWindow::SplitLinesOrCurves(void) {
|
|
|
|
if(!LockedInWorkplane()) {
|
|
|
|
Error("Must be sketching in workplane to split.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
GroupSelection();
|
2009-11-10 03:57:24 +00:00
|
|
|
if(!(gs.n == 2 &&(gs.lineSegments +
|
|
|
|
gs.circlesOrArcs +
|
|
|
|
gs.cubics +
|
|
|
|
gs.periodicCubics) == 2))
|
|
|
|
{
|
2009-07-07 08:21:59 +00:00
|
|
|
Error("Select two entities that intersect each other (e.g. two lines "
|
|
|
|
"or two circles or a circle and a line).");
|
|
|
|
return;
|
|
|
|
}
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2009-07-07 08:21:59 +00:00
|
|
|
hEntity ha = gs.entity[0],
|
|
|
|
hb = gs.entity[1];
|
|
|
|
Entity *ea = SK.GetEntity(ha),
|
|
|
|
*eb = SK.GetEntity(hb);
|
2009-01-03 12:27:33 +00:00
|
|
|
|
2009-07-07 08:21:59 +00:00
|
|
|
// Compute the possibly-rational Bezier curves for each of these entities
|
|
|
|
SBezierList sbla, sblb;
|
|
|
|
ZERO(&sbla);
|
|
|
|
ZERO(&sblb);
|
|
|
|
ea->GenerateBezierCurves(&sbla);
|
|
|
|
eb->GenerateBezierCurves(&sblb);
|
|
|
|
// and then compute the points where they intersect, based on those curves.
|
|
|
|
SPointList inters;
|
|
|
|
ZERO(&inters);
|
|
|
|
sbla.AllIntersectionsWith(&sblb, &inters);
|
|
|
|
|
|
|
|
if(inters.l.n > 0) {
|
2010-07-12 06:47:14 +00:00
|
|
|
Vector pi;
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-10 03:57:24 +00:00
|
|
|
SS.UndoRemember();
|
2009-07-07 08:21:59 +00:00
|
|
|
hEntity hia = SplitEntity(ha, pi),
|
|
|
|
hib = SplitEntity(hb, pi);
|
|
|
|
// 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);
|
2009-01-03 12:27:33 +00:00
|
|
|
}
|
|
|
|
} else {
|
2009-07-07 08:21:59 +00:00
|
|
|
Error("Can't split; no intersection found.");
|
2009-01-03 12:27:33 +00:00
|
|
|
}
|
|
|
|
|
2009-07-07 08:21:59 +00:00
|
|
|
// All done, clean up and regenerate.
|
|
|
|
inters.Clear();
|
|
|
|
sbla.Clear();
|
|
|
|
sblb.Clear();
|
2009-01-03 12:27:33 +00:00
|
|
|
ClearSelection();
|
|
|
|
SS.later.generateAll = true;
|
|
|
|
}
|
|
|
|
|