diff --git a/draw.cpp b/draw.cpp index 798c562..2ec08a4 100644 --- a/draw.cpp +++ b/draw.cpp @@ -261,10 +261,10 @@ void GraphicsWindow::GroupSelection(void) { // And some aux counts too switch(e->type) { - case Entity::WORKPLANE: (gs.workplanes)++; break; - case Entity::LINE_SEGMENT: (gs.lineSegments)++; break; - case Entity::CUBIC: (gs.cubics)++; break; - // but don't count periodic cubics + case Entity::WORKPLANE: (gs.workplanes)++; break; + case Entity::LINE_SEGMENT: (gs.lineSegments)++; break; + case Entity::CUBIC: (gs.cubics)++; break; + case Entity::CUBIC_PERIODIC: (gs.periodicCubics)++; break; case Entity::ARC_OF_CIRCLE: (gs.circlesOrArcs)++; @@ -727,6 +727,15 @@ nogrid:; 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. if(SS.showToolbar) { ToolbarDraw(); diff --git a/graphicswin.cpp b/graphicswin.cpp index 4b198f4..49726a0 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -39,6 +39,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 1, "Cu&t\tCtrl+X", MNU_CUT, 'X'|C, mEdit }, { 1, "&Copy\tCtrl+C", MNU_COPY, 'C'|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, NULL, 0, NULL }, { 1, "Select Edge Cha&in\tCtrl+I", MNU_SELECT_CHAIN, 'I'|C, mEdit }, diff --git a/modify.cpp b/modify.cpp index 1958cef..3d251e3 100644 --- a/modify.cpp +++ b/modify.cpp @@ -16,7 +16,6 @@ void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) { } } - void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) { Request *r = SK.GetRequest(hr); 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(), p1 = SK.GetEntity(hep1)->PointGetNum(); - SS.UndoRemember(); - // Add the two line segments this one gets split into. hRequest r0i = 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) { - SS.UndoRemember(); - Entity *circle = SK.GetEntity(he); if(circle->type == Entity::CIRCLE) { // Start with an unbroken circle, split it into a 360 degree arc. @@ -294,44 +289,67 @@ hEntity GraphicsWindow::SplitCircle(hEntity he, Vector pinter) { hEntity GraphicsWindow::SplitCubic(hEntity he, Vector pinter) { // Save the original endpoints, since we're about to delete this entity. Entity *e01 = SK.GetEntity(he); + SBezierList sbl; + ZERO(&sbl); + e01->GenerateBezierCurves(&sbl); + hEntity hep0 = e01->point[0], - hep1 = e01->point[1], - hep2 = e01->point[2], - hep3 = e01->point[3]; - Vector p0 = SK.GetEntity(hep0)->PointGetNum(), - p1 = SK.GetEntity(hep1)->PointGetNum(), - p2 = SK.GetEntity(hep2)->PointGetNum(), - p3 = SK.GetEntity(hep3)->PointGetNum(); + 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 - SS.UndoRemember(); - - SBezier b0i, bi1, b01 = SBezier::From(p0, p1, p2, p3); + // The curve may consist of multiple cubic segments. So find which one + // contains the intersection point. double t; - b01.ClosestPointTo(pinter, &t, true); - b01.SplitAt(t, &b0i, &bi1); + int i, j; + for(i = 0; i < sbl.l.n; i++) { + SBezier *sb = &(sbl.l.elem[i]); + if(sb->deg != 3) oops(); - // 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 + 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); - Entity *e0i = SK.GetEntity(r0i.entity(0)), - *ei1 = SK.GetEntity(ri1.entity(0)); + // 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 - SK.GetEntity(e0i->point[0])->PointForceTo(b0i.ctrl[0]); - SK.GetEntity(e0i->point[1])->PointForceTo(b0i.ctrl[1]); - SK.GetEntity(e0i->point[2])->PointForceTo(b0i.ctrl[2]); - SK.GetEntity(e0i->point[3])->PointForceTo(b0i.ctrl[3]); + Entity *e0i = SK.GetEntity(r0i.entity(0)), + *ei1 = SK.GetEntity(ri1.entity(0)); - 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]); + 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]); + } - ReplacePointInConstraints(hep0, e0i->point[0]); - ReplacePointInConstraints(hep3, ei1->point[3]); - Constraint::ConstrainCoincident(e0i->point[3], ei1->point[0]); - return e0i->point[3]; + 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)); + + 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) { @@ -343,7 +361,7 @@ hEntity GraphicsWindow::SplitEntity(hEntity he, Vector pinter) { ret = SplitCircle(he, pinter); } else if(e->type == Entity::LINE_SEGMENT) { 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); } else { Error("Couldn't split this entity; lines, circles, or cubics only."); @@ -377,7 +395,11 @@ void GraphicsWindow::SplitLinesOrCurves(void) { } 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 " "or two circles or a circle and a line)."); return; @@ -402,6 +424,7 @@ void GraphicsWindow::SplitLinesOrCurves(void) { // If there's multiple points, then just take the first one. if(inters.l.n > 0) { Vector pi = inters.l.elem[0].p; + SS.UndoRemember(); hEntity hia = SplitEntity(ha, pi), hib = SplitEntity(hb, pi); // SplitEntity adds the coincident constraints to join the split halves diff --git a/mouse.cpp b/mouse.cpp index e0453be..05bcb9a 100644 --- a/mouse.cpp +++ b/mouse.cpp @@ -79,6 +79,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, if(GraphicsEditControlIsVisible()) return; if(context.active) return; + pending.drawLine = false; + if(!orig.mouseDown) { // If someone drags the mouse into our window with the left button // 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) { double theta = atan2(orig.mouse.y, orig.mouse.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); 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 // painted since the last time we solved, do nothing, because there's // 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) { case DRAGGING_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); qt = Quaternion::From(gn, -theta); + + pending.drawLine = true; + pending.lnA = UnProjectPoint(orig.mouseOnButtonDown); + pending.lnB = UnProjectPoint(mp); } else { double dx = -(x - orig.mouse.x); double dy = -(y - orig.mouse.y); @@ -280,7 +296,17 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, List *lhe = &(pending.points); for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(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(); Vector p = e->PointGetNum(); @@ -439,6 +465,9 @@ void GraphicsWindow::ContextMenuListStyles(void) { } 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, // or if they are finishing a pan. if(ToolbarMouseMoved((int)x, (int)y)) return; diff --git a/ui.h b/ui.h index e652e8a..8b6e862 100644 --- a/ui.h +++ b/ui.h @@ -242,6 +242,7 @@ public: MNU_CUT, MNU_COPY, MNU_PASTE, + MNU_PASTE_TRANSFORM, MNU_DELETE, MNU_SELECT_CHAIN, MNU_INVERT_SEL, @@ -387,6 +388,9 @@ public: hEntity circle; hEntity normal; hConstraint constraint; + + bool drawLine; + Vector lnA, lnB; char *description; } pending; @@ -448,6 +452,7 @@ public: int circlesOrArcs; int arcs; int cubics; + int periodicCubics; int anyNormals; int vectors; int constraints; diff --git a/wishlist.txt b/wishlist.txt index 87fcc42..a815d77 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,6 +1,6 @@ copy and paste -spline splitting +de-select after left-clicking nothing, keep sel on drag? ----- associative entities from solid model, as a special group