Split cubic splines and periodic splines too; which is a pain,
because it can't be C2 continuous after splitting (since I'm doing uniform splines only, and the parametrization changes). So just knock everything down to cubic Beziers. Also draw lines to indicate angle and origin when Ctrl+dragging to rotate in plane of screen. [git-p4: depot-paths = "//depot/solvespace/": change = 2075]
This commit is contained in:
parent
c79ca083c1
commit
4cfe7eea64
11
draw.cpp
11
draw.cpp
|
@ -264,7 +264,7 @@ void GraphicsWindow::GroupSelection(void) {
|
||||||
case Entity::WORKPLANE: (gs.workplanes)++; break;
|
case Entity::WORKPLANE: (gs.workplanes)++; break;
|
||||||
case Entity::LINE_SEGMENT: (gs.lineSegments)++; break;
|
case Entity::LINE_SEGMENT: (gs.lineSegments)++; break;
|
||||||
case Entity::CUBIC: (gs.cubics)++; break;
|
case Entity::CUBIC: (gs.cubics)++; break;
|
||||||
// but don't count periodic cubics
|
case Entity::CUBIC_PERIODIC: (gs.periodicCubics)++; break;
|
||||||
|
|
||||||
case Entity::ARC_OF_CIRCLE:
|
case Entity::ARC_OF_CIRCLE:
|
||||||
(gs.circlesOrArcs)++;
|
(gs.circlesOrArcs)++;
|
||||||
|
@ -727,6 +727,15 @@ nogrid:;
|
||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(pending.drawLine) {
|
||||||
|
glLineWidth(1);
|
||||||
|
glxLockColorTo(Style::Color(Style::DATUM));
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glxVertex3v(pending.lnA);
|
||||||
|
glxVertex3v(pending.lnB);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
// And finally the toolbar.
|
// And finally the toolbar.
|
||||||
if(SS.showToolbar) {
|
if(SS.showToolbar) {
|
||||||
ToolbarDraw();
|
ToolbarDraw();
|
||||||
|
|
|
@ -39,6 +39,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||||
{ 1, "Cu&t\tCtrl+X", MNU_CUT, 'X'|C, mEdit },
|
{ 1, "Cu&t\tCtrl+X", MNU_CUT, 'X'|C, mEdit },
|
||||||
{ 1, "&Copy\tCtrl+C", MNU_COPY, 'C'|C, mEdit },
|
{ 1, "&Copy\tCtrl+C", MNU_COPY, 'C'|C, mEdit },
|
||||||
{ 1, "&Paste\tCtrl+V", MNU_PASTE, 'V'|C, mEdit },
|
{ 1, "&Paste\tCtrl+V", MNU_PASTE, 'V'|C, mEdit },
|
||||||
|
{ 1, "Paste &Transformed...\tCtrl+T", MNU_PASTE_TRANSFORM,'T'|C, mEdit },
|
||||||
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
|
{ 1, "&Delete\tDel", MNU_DELETE, 127, mEdit },
|
||||||
{ 1, NULL, 0, NULL },
|
{ 1, NULL, 0, NULL },
|
||||||
{ 1, "Select Edge Cha&in\tCtrl+I", MNU_SELECT_CHAIN, 'I'|C, mEdit },
|
{ 1, "Select Edge Cha&in\tCtrl+I", MNU_SELECT_CHAIN, 'I'|C, mEdit },
|
||||||
|
|
83
modify.cpp
83
modify.cpp
|
@ -16,7 +16,6 @@ void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
|
void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
|
||||||
Request *r = SK.GetRequest(hr);
|
Request *r = SK.GetRequest(hr);
|
||||||
if(r->group.v != SS.GW.activeGroup.v) return;
|
if(r->group.v != SS.GW.activeGroup.v) return;
|
||||||
|
@ -220,8 +219,6 @@ hEntity GraphicsWindow::SplitLine(hEntity he, Vector pinter) {
|
||||||
Vector p0 = SK.GetEntity(hep0)->PointGetNum(),
|
Vector p0 = SK.GetEntity(hep0)->PointGetNum(),
|
||||||
p1 = SK.GetEntity(hep1)->PointGetNum();
|
p1 = SK.GetEntity(hep1)->PointGetNum();
|
||||||
|
|
||||||
SS.UndoRemember();
|
|
||||||
|
|
||||||
// Add the two line segments this one gets split into.
|
// Add the two line segments this one gets split into.
|
||||||
hRequest r0i = AddRequest(Request::LINE_SEGMENT, false),
|
hRequest r0i = AddRequest(Request::LINE_SEGMENT, false),
|
||||||
ri1 = AddRequest(Request::LINE_SEGMENT, false);
|
ri1 = AddRequest(Request::LINE_SEGMENT, false);
|
||||||
|
@ -242,8 +239,6 @@ hEntity GraphicsWindow::SplitLine(hEntity he, Vector pinter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
hEntity GraphicsWindow::SplitCircle(hEntity he, Vector pinter) {
|
hEntity GraphicsWindow::SplitCircle(hEntity he, Vector pinter) {
|
||||||
SS.UndoRemember();
|
|
||||||
|
|
||||||
Entity *circle = SK.GetEntity(he);
|
Entity *circle = SK.GetEntity(he);
|
||||||
if(circle->type == Entity::CIRCLE) {
|
if(circle->type == Entity::CIRCLE) {
|
||||||
// Start with an unbroken circle, split it into a 360 degree arc.
|
// Start with an unbroken circle, split it into a 360 degree arc.
|
||||||
|
@ -294,20 +289,28 @@ hEntity GraphicsWindow::SplitCircle(hEntity he, Vector pinter) {
|
||||||
hEntity GraphicsWindow::SplitCubic(hEntity he, Vector pinter) {
|
hEntity GraphicsWindow::SplitCubic(hEntity he, Vector pinter) {
|
||||||
// Save the original endpoints, since we're about to delete this entity.
|
// Save the original endpoints, since we're about to delete this entity.
|
||||||
Entity *e01 = SK.GetEntity(he);
|
Entity *e01 = SK.GetEntity(he);
|
||||||
|
SBezierList sbl;
|
||||||
|
ZERO(&sbl);
|
||||||
|
e01->GenerateBezierCurves(&sbl);
|
||||||
|
|
||||||
hEntity hep0 = e01->point[0],
|
hEntity hep0 = e01->point[0],
|
||||||
hep1 = e01->point[1],
|
hep1 = e01->point[3+e01->extraPoints],
|
||||||
hep2 = e01->point[2],
|
hep0n = Entity::NO_ENTITY, // the new start point
|
||||||
hep3 = e01->point[3];
|
hep1n = Entity::NO_ENTITY, // the new finish point
|
||||||
Vector p0 = SK.GetEntity(hep0)->PointGetNum(),
|
hepin = Entity::NO_ENTITY; // the intersection point
|
||||||
p1 = SK.GetEntity(hep1)->PointGetNum(),
|
|
||||||
p2 = SK.GetEntity(hep2)->PointGetNum(),
|
|
||||||
p3 = SK.GetEntity(hep3)->PointGetNum();
|
|
||||||
|
|
||||||
SS.UndoRemember();
|
// The curve may consist of multiple cubic segments. So find which one
|
||||||
|
// contains the intersection point.
|
||||||
SBezier b0i, bi1, b01 = SBezier::From(p0, p1, p2, p3);
|
|
||||||
double t;
|
double t;
|
||||||
b01.ClosestPointTo(pinter, &t, true);
|
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);
|
b01.SplitAt(t, &b0i, &bi1);
|
||||||
|
|
||||||
// Add the two cubic segments this one gets split into.
|
// Add the two cubic segments this one gets split into.
|
||||||
|
@ -318,20 +321,35 @@ hEntity GraphicsWindow::SplitCubic(hEntity he, Vector pinter) {
|
||||||
Entity *e0i = SK.GetEntity(r0i.entity(0)),
|
Entity *e0i = SK.GetEntity(r0i.entity(0)),
|
||||||
*ei1 = SK.GetEntity(ri1.entity(0));
|
*ei1 = SK.GetEntity(ri1.entity(0));
|
||||||
|
|
||||||
SK.GetEntity(e0i->point[0])->PointForceTo(b0i.ctrl[0]);
|
for(j = 0; j <= 3; j++) {
|
||||||
SK.GetEntity(e0i->point[1])->PointForceTo(b0i.ctrl[1]);
|
SK.GetEntity(e0i->point[j])->PointForceTo(b0i.ctrl[j]);
|
||||||
SK.GetEntity(e0i->point[2])->PointForceTo(b0i.ctrl[2]);
|
}
|
||||||
SK.GetEntity(e0i->point[3])->PointForceTo(b0i.ctrl[3]);
|
for(j = 0; j <= 3; j++) {
|
||||||
|
SK.GetEntity(ei1->point[j])->PointForceTo(bi1.ctrl[j]);
|
||||||
|
}
|
||||||
|
|
||||||
SK.GetEntity(ei1->point[0])->PointForceTo(bi1.ctrl[0]);
|
|
||||||
SK.GetEntity(ei1->point[1])->PointForceTo(bi1.ctrl[1]);
|
|
||||||
SK.GetEntity(ei1->point[2])->PointForceTo(bi1.ctrl[2]);
|
|
||||||
SK.GetEntity(ei1->point[3])->PointForceTo(bi1.ctrl[3]);
|
|
||||||
|
|
||||||
ReplacePointInConstraints(hep0, e0i->point[0]);
|
|
||||||
ReplacePointInConstraints(hep3, ei1->point[3]);
|
|
||||||
Constraint::ConstrainCoincident(e0i->point[3], ei1->point[0]);
|
Constraint::ConstrainCoincident(e0i->point[3], ei1->point[0]);
|
||||||
return e0i->point[3];
|
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));
|
||||||
|
|
||||||
|
for(j = 0; j <= 3; j++) {
|
||||||
|
SK.GetEntity(e->point[j])->PointForceTo(sb->ctrl[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i == 0) hep0n = e->point[0];
|
||||||
|
hep1n = e->point[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sbl.Clear();
|
||||||
|
|
||||||
|
ReplacePointInConstraints(hep0, hep0n);
|
||||||
|
ReplacePointInConstraints(hep1, hep1n);
|
||||||
|
return hepin;
|
||||||
}
|
}
|
||||||
|
|
||||||
hEntity GraphicsWindow::SplitEntity(hEntity he, Vector pinter) {
|
hEntity GraphicsWindow::SplitEntity(hEntity he, Vector pinter) {
|
||||||
|
@ -343,7 +361,7 @@ hEntity GraphicsWindow::SplitEntity(hEntity he, Vector pinter) {
|
||||||
ret = SplitCircle(he, pinter);
|
ret = SplitCircle(he, pinter);
|
||||||
} else if(e->type == Entity::LINE_SEGMENT) {
|
} else if(e->type == Entity::LINE_SEGMENT) {
|
||||||
ret = SplitLine(he, pinter);
|
ret = SplitLine(he, pinter);
|
||||||
} else if(e->type == Entity::CUBIC && e->extraPoints == 0) {
|
} else if(e->type == Entity::CUBIC || e->type == Entity::CUBIC_PERIODIC) {
|
||||||
ret = SplitCubic(he, pinter);
|
ret = SplitCubic(he, pinter);
|
||||||
} else {
|
} else {
|
||||||
Error("Couldn't split this entity; lines, circles, or cubics only.");
|
Error("Couldn't split this entity; lines, circles, or cubics only.");
|
||||||
|
@ -377,7 +395,11 @@ void GraphicsWindow::SplitLinesOrCurves(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupSelection();
|
GroupSelection();
|
||||||
if(!(gs.n == 2 && (gs.lineSegments + gs.circlesOrArcs + gs.cubics) == 2)) {
|
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 "
|
Error("Select two entities that intersect each other (e.g. two lines "
|
||||||
"or two circles or a circle and a line).");
|
"or two circles or a circle and a line).");
|
||||||
return;
|
return;
|
||||||
|
@ -402,6 +424,7 @@ void GraphicsWindow::SplitLinesOrCurves(void) {
|
||||||
// If there's multiple points, then just take the first one.
|
// If there's multiple points, then just take the first one.
|
||||||
if(inters.l.n > 0) {
|
if(inters.l.n > 0) {
|
||||||
Vector pi = inters.l.elem[0].p;
|
Vector pi = inters.l.elem[0].p;
|
||||||
|
SS.UndoRemember();
|
||||||
hEntity hia = SplitEntity(ha, pi),
|
hEntity hia = SplitEntity(ha, pi),
|
||||||
hib = SplitEntity(hb, pi);
|
hib = SplitEntity(hb, pi);
|
||||||
// SplitEntity adds the coincident constraints to join the split halves
|
// SplitEntity adds the coincident constraints to join the split halves
|
||||||
|
|
33
mouse.cpp
33
mouse.cpp
|
@ -79,6 +79,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
if(GraphicsEditControlIsVisible()) return;
|
if(GraphicsEditControlIsVisible()) return;
|
||||||
if(context.active) return;
|
if(context.active) return;
|
||||||
|
|
||||||
|
pending.drawLine = false;
|
||||||
|
|
||||||
if(!orig.mouseDown) {
|
if(!orig.mouseDown) {
|
||||||
// If someone drags the mouse into our window with the left button
|
// If someone drags the mouse into our window with the left button
|
||||||
// already depressed, then we don't have our starting point; so
|
// already depressed, then we don't have our starting point; so
|
||||||
|
@ -131,6 +133,9 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
} else if(ctrlDown) {
|
} else if(ctrlDown) {
|
||||||
double theta = atan2(orig.mouse.y, orig.mouse.x);
|
double theta = atan2(orig.mouse.y, orig.mouse.x);
|
||||||
theta -= atan2(y, x);
|
theta -= atan2(y, x);
|
||||||
|
pending.drawLine = true;
|
||||||
|
pending.lnA = UnProjectPoint(Point2d::From(0, 0));
|
||||||
|
pending.lnB = UnProjectPoint(mp);
|
||||||
|
|
||||||
Vector normal = orig.projRight.Cross(orig.projUp);
|
Vector normal = orig.projRight.Cross(orig.projUp);
|
||||||
projRight = orig.projRight.RotatedAbout(normal, theta);
|
projRight = orig.projRight.RotatedAbout(normal, theta);
|
||||||
|
@ -226,7 +231,14 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
// We're currently dragging something; so do that. But if we haven't
|
// We're currently dragging something; so do that. But if we haven't
|
||||||
// painted since the last time we solved, do nothing, because there's
|
// painted since the last time we solved, do nothing, because there's
|
||||||
// no sense solving a frame and not displaying it.
|
// no sense solving a frame and not displaying it.
|
||||||
if(!havePainted) return;
|
if(!havePainted) {
|
||||||
|
if(pending.operation == DRAGGING_POINTS && ctrlDown) {
|
||||||
|
pending.lnA = UnProjectPoint(orig.mouseOnButtonDown);
|
||||||
|
pending.lnB = UnProjectPoint(mp);
|
||||||
|
pending.drawLine = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch(pending.operation) {
|
switch(pending.operation) {
|
||||||
case DRAGGING_CONSTRAINT: {
|
case DRAGGING_CONSTRAINT: {
|
||||||
Constraint *c = SK.constraint.FindById(pending.constraint);
|
Constraint *c = SK.constraint.FindById(pending.constraint);
|
||||||
|
@ -267,6 +279,10 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
|
|
||||||
Vector gn = projRight.Cross(projUp);
|
Vector gn = projRight.Cross(projUp);
|
||||||
qt = Quaternion::From(gn, -theta);
|
qt = Quaternion::From(gn, -theta);
|
||||||
|
|
||||||
|
pending.drawLine = true;
|
||||||
|
pending.lnA = UnProjectPoint(orig.mouseOnButtonDown);
|
||||||
|
pending.lnB = UnProjectPoint(mp);
|
||||||
} else {
|
} else {
|
||||||
double dx = -(x - orig.mouse.x);
|
double dx = -(x - orig.mouse.x);
|
||||||
double dy = -(y - orig.mouse.y);
|
double dy = -(y - orig.mouse.y);
|
||||||
|
@ -280,7 +296,17 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
List<hEntity> *lhe = &(pending.points);
|
List<hEntity> *lhe = &(pending.points);
|
||||||
for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) {
|
for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) {
|
||||||
Entity *e = SK.GetEntity(*he);
|
Entity *e = SK.GetEntity(*he);
|
||||||
if(e->type != Entity::POINT_N_ROT_TRANS) continue;
|
if(e->type != Entity::POINT_N_ROT_TRANS) {
|
||||||
|
if(ctrlDown) {
|
||||||
|
Vector p = e->PointGetNum();
|
||||||
|
p = p.Minus(pending.lnA);
|
||||||
|
p = qt.Rotate(p);
|
||||||
|
p = p.Plus(pending.lnA);
|
||||||
|
e->PointForceTo(p);
|
||||||
|
SS.MarkGroupDirtyByEntity(e->h);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Quaternion q = e->PointGetQuaternion();
|
Quaternion q = e->PointGetQuaternion();
|
||||||
Vector p = e->PointGetNum();
|
Vector p = e->PointGetNum();
|
||||||
|
@ -439,6 +465,9 @@ void GraphicsWindow::ContextMenuListStyles(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::MouseRightUp(double x, double y) {
|
void GraphicsWindow::MouseRightUp(double x, double y) {
|
||||||
|
pending.drawLine = false;
|
||||||
|
InvalidateGraphics();
|
||||||
|
|
||||||
// Don't show a context menu if the user is right-clicking the toolbar,
|
// Don't show a context menu if the user is right-clicking the toolbar,
|
||||||
// or if they are finishing a pan.
|
// or if they are finishing a pan.
|
||||||
if(ToolbarMouseMoved((int)x, (int)y)) return;
|
if(ToolbarMouseMoved((int)x, (int)y)) return;
|
||||||
|
|
5
ui.h
5
ui.h
|
@ -242,6 +242,7 @@ public:
|
||||||
MNU_CUT,
|
MNU_CUT,
|
||||||
MNU_COPY,
|
MNU_COPY,
|
||||||
MNU_PASTE,
|
MNU_PASTE,
|
||||||
|
MNU_PASTE_TRANSFORM,
|
||||||
MNU_DELETE,
|
MNU_DELETE,
|
||||||
MNU_SELECT_CHAIN,
|
MNU_SELECT_CHAIN,
|
||||||
MNU_INVERT_SEL,
|
MNU_INVERT_SEL,
|
||||||
|
@ -388,6 +389,9 @@ public:
|
||||||
hEntity normal;
|
hEntity normal;
|
||||||
hConstraint constraint;
|
hConstraint constraint;
|
||||||
|
|
||||||
|
bool drawLine;
|
||||||
|
Vector lnA, lnB;
|
||||||
|
|
||||||
char *description;
|
char *description;
|
||||||
} pending;
|
} pending;
|
||||||
void ClearPending(void);
|
void ClearPending(void);
|
||||||
|
@ -448,6 +452,7 @@ public:
|
||||||
int circlesOrArcs;
|
int circlesOrArcs;
|
||||||
int arcs;
|
int arcs;
|
||||||
int cubics;
|
int cubics;
|
||||||
|
int periodicCubics;
|
||||||
int anyNormals;
|
int anyNormals;
|
||||||
int vectors;
|
int vectors;
|
||||||
int constraints;
|
int constraints;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
copy and paste
|
copy and paste
|
||||||
spline splitting
|
de-select after left-clicking nothing, keep sel on drag?
|
||||||
|
|
||||||
-----
|
-----
|
||||||
associative entities from solid model, as a special group
|
associative entities from solid model, as a special group
|
||||||
|
|
Loading…
Reference in New Issue
Block a user