//----------------------------------------------------------------------------- // Select everything that lies within the marquee view-aligned rectangle. For // points, we test by the point location. For normals, we test by the normal's // associated point. For anything else, we test by any piecewise linear edge. //----------------------------------------------------------------------------- void GraphicsWindow::SelectByMarquee(void) { Point2d begin = ProjectPoint(orig.marqueePoint); double xmin = min(orig.mouse.x, begin.x), xmax = max(orig.mouse.x, begin.x), ymin = min(orig.mouse.y, begin.y), ymax = max(orig.mouse.y, begin.y); Entity *e; for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { if(e->group.v != SS.GW.activeGroup.v) continue; if(e->IsFace() || e->IsDistance()) continue; if(!e->IsVisible()) continue; if(e->IsPoint() || e->IsNormal()) { Vector p = e->IsPoint() ? e->PointGetNum() : SK.GetEntity(e->point[0])->PointGetNum(); Point2d pp = ProjectPoint(p); if(pp.x >= xmin && pp.x <= xmax && pp.y >= ymin && pp.y <= ymax) { MakeSelected(e->h); } } else { // Use the 3d bounding box test routines, to avoid duplication; // so let our bounding square become a bounding box that certainly // includes the z = 0 plane. Vector ptMin = Vector::From(xmin, ymin, -1), ptMax = Vector::From(xmax, ymax, 1); SEdgeList sel; ZERO(&sel); e->GenerateEdges(&sel, true); SEdge *se; for(se = sel.l.First(); se; se = sel.l.NextAfter(se)) { Point2d ppa = ProjectPoint(se->a), ppb = ProjectPoint(se->b); Vector ptA = Vector::From(ppa.x, ppa.y, 0), ptB = Vector::From(ppb.x, ppb.y, 0); if(Vector::BoundingBoxIntersectsLine(ptMax, ptMin, ptA, ptB, true) || !ptA.OutsideAndNotOn(ptMax, ptMin) || !ptB.OutsideAndNotOn(ptMax, ptMin)) { MakeSelected(e->h); break; } } sel.Clear(); } } }
void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown) { if(GraphicsEditControlIsVisible()) return; if(context.active) return; SS.extraLine.draw = 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 // don't try. leftDown = false; } if(rightDown) { middleDown = true; shiftDown = !shiftDown; } if(SS.showToolbar) { if(ToolbarMouseMoved((int)x, (int)y)) { hover.Clear(); return; } } if(!leftDown && (pending.operation == DRAGGING_POINTS || pending.operation == DRAGGING_MARQUEE)) { ClearPending(); InvalidateGraphics(); } Point2d mp = Point2d::From(x, y); currentMousePosition = mp; if(rightDown && orig.mouse.DistanceTo(mp) < 5 && !orig.startedMoving) { // Avoid accidentally panning (or rotating if shift is down) if the // user wants a context menu. return; } orig.startedMoving = true; // If the middle button is down, then mouse movement is used to pan and // rotate our view. This wins over everything else. if(middleDown) { hover.Clear(); double dx = (x - orig.mouse.x) / scale; double dy = (y - orig.mouse.y) / scale; if(!(shiftDown || ctrlDown)) { double s = 0.3*(PI/180)*scale; // degrees per pixel projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx); projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy); NormalizeProjectionVectors(); } else if(ctrlDown) { double theta = atan2(orig.mouse.y, orig.mouse.x); theta -= atan2(y, x); SS.extraLine.draw = true; SS.extraLine.ptA = UnProjectPoint(Point2d::From(0, 0)); SS.extraLine.ptB = UnProjectPoint(mp); Vector normal = orig.projRight.Cross(orig.projUp); projRight = orig.projRight.RotatedAbout(normal, theta); projUp = orig.projUp.RotatedAbout(normal, theta); NormalizeProjectionVectors(); } else { offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x; offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y; offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z; } orig.projRight = projRight; orig.projUp = projUp; orig.offset = offset; orig.mouse.x = x; orig.mouse.y = y; if(SS.TW.shown.screen == TextWindow::SCREEN_EDIT_VIEW) { if(havePainted) { SS.ScheduleShowTW(); } } InvalidateGraphics(); havePainted = false; return; } if(pending.operation == 0) { double dm = orig.mouse.DistanceTo(mp); // If we're currently not doing anything, then see if we should // start dragging something. if(leftDown && dm > 3) { Entity *e = NULL; if(hover.entity.v) e = SK.GetEntity(hover.entity); if(e && e->type != Entity::WORKPLANE) { Entity *e = SK.GetEntity(hover.entity); if(e->type == Entity::CIRCLE && selection.n <= 1) { // Drag the radius. ClearSelection(); pending.circle = hover.entity; pending.operation = DRAGGING_RADIUS; } else if(e->IsNormal()) { ClearSelection(); pending.normal = hover.entity; pending.operation = DRAGGING_NORMAL; } else { if(!hoverWasSelectedOnMousedown) { // The user clicked an unselected entity, which // means they're dragging just the hovered thing, // not the full selection. So clear all the selection // except that entity. ClearSelection(); MakeSelected(e->h); } StartDraggingBySelection(); if(!hoverWasSelectedOnMousedown) { // And then clear the selection again, since they // probably didn't want that selected if they just // were dragging it. ClearSelection(); } hover.Clear(); pending.operation = DRAGGING_POINTS; } } else if(hover.constraint.v && SK.GetConstraint(hover.constraint)->HasLabel()) { ClearSelection(); pending.constraint = hover.constraint; pending.operation = DRAGGING_CONSTRAINT; } if(pending.operation != 0) { // We just started a drag, so remember for the undo before // the drag changes anything. SS.UndoRemember(); } else { if(!hover.constraint.v) { // That's just marquee selection, which should not cause // an undo remember. if(dm > 10) { if(hover.entity.v) { // Avoid accidentally selecting workplanes when // starting drags. MakeUnselected(hover.entity, false); hover.Clear(); } pending.operation = DRAGGING_MARQUEE; orig.marqueePoint = UnProjectPoint(orig.mouseOnButtonDown); } } } } else { // Otherwise, just hit test and give up; but don't hit test // if the mouse is down, because then the user could hover // a point, mouse down (thus selecting it), and drag, in an // effort to drag the point, but instead hover a different // entity before we move far enough to start the drag. if(!leftDown) HitTestMakeSelection(mp); } return; } // If the user has started an operation from the menu, but not // completed it, then just do the selection. if(pending.operation < FIRST_PENDING) { HitTestMakeSelection(mp); return; } // 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) { if(pending.operation == DRAGGING_POINTS && ctrlDown) { SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown); SS.extraLine.ptB = UnProjectPoint(mp); SS.extraLine.draw = true; } return; } switch(pending.operation) { case DRAGGING_CONSTRAINT: { Constraint *c = SK.constraint.FindById(pending.constraint); UpdateDraggedNum(&(c->disp.offset), x, y); orig.mouse = mp; InvalidateGraphics(); break; } case DRAGGING_NEW_LINE_POINT: case DRAGGING_NEW_POINT: UpdateDraggedPoint(pending.point, x, y); HitTestMakeSelection(mp); SS.MarkGroupDirtyByEntity(pending.point); orig.mouse = mp; InvalidateGraphics(); break; case DRAGGING_POINTS: if(shiftDown || ctrlDown) { // Edit the rotation associated with a POINT_N_ROT_TRANS, // either within (ctrlDown) or out of (shiftDown) the plane // of the screen. So first get the rotation to apply, in qt. Quaternion qt; if(ctrlDown) { double d = mp.DistanceTo(orig.mouseOnButtonDown); if(d < 25) { // Don't start dragging the position about the normal // until we're a little ways out, to get a reasonable // reference pos orig.mouse = mp; break; } double theta = atan2(orig.mouse.y-orig.mouseOnButtonDown.y, orig.mouse.x-orig.mouseOnButtonDown.x); theta -= atan2(y-orig.mouseOnButtonDown.y, x-orig.mouseOnButtonDown.x); Vector gn = projRight.Cross(projUp); qt = Quaternion::From(gn, -theta); SS.extraLine.draw = true; SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown); SS.extraLine.ptB = UnProjectPoint(mp); } else { double dx = -(x - orig.mouse.x); double dy = -(y - orig.mouse.y); double s = 0.3*(PI/180); // degrees per pixel qt = Quaternion::From(projUp, -s*dx).Times( Quaternion::From(projRight, s*dy)); } orig.mouse = mp; // Now apply this rotation to the points being dragged. List<hEntity> *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) { if(ctrlDown) { Vector p = e->PointGetNum(); p = p.Minus(SS.extraLine.ptA); p = qt.Rotate(p); p = p.Plus(SS.extraLine.ptA); e->PointForceTo(p); SS.MarkGroupDirtyByEntity(e->h); } continue; } Quaternion q = e->PointGetQuaternion(); Vector p = e->PointGetNum(); q = qt.Times(q); e->PointForceQuaternionTo(q); // Let's rotate about the selected point; so fix up the // translation so that that point didn't move. e->PointForceTo(p); SS.MarkGroupDirtyByEntity(e->h); } } else { List<hEntity> *lhe = &(pending.points); for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) { UpdateDraggedPoint(*he, x, y); SS.MarkGroupDirtyByEntity(*he); } orig.mouse = mp; } break; case DRAGGING_NEW_CUBIC_POINT: { UpdateDraggedPoint(pending.point, x, y); HitTestMakeSelection(mp); hRequest hr = pending.point.request(); if(pending.point.v == hr.entity(4).v) { // The very first segment; dragging final point drags both // tangent points. Vector p0 = SK.GetEntity(hr.entity(1))->PointGetNum(), p3 = SK.GetEntity(hr.entity(4))->PointGetNum(), p1 = p0.ScaledBy(2.0/3).Plus(p3.ScaledBy(1.0/3)), p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3)); SK.GetEntity(hr.entity(1+1))->PointForceTo(p1); SK.GetEntity(hr.entity(1+2))->PointForceTo(p2); } else { // A subsequent segment; dragging point drags only final // tangent point. int i = SK.GetEntity(hr.entity(0))->extraPoints; Vector pn = SK.GetEntity(hr.entity(4+i))->PointGetNum(), pnm2 = SK.GetEntity(hr.entity(2+i))->PointGetNum(), pnm1 = (pn.Plus(pnm2)).ScaledBy(0.5); SK.GetEntity(hr.entity(3+i))->PointForceTo(pnm1); } orig.mouse = mp; SS.MarkGroupDirtyByEntity(pending.point); break; } case DRAGGING_NEW_ARC_POINT: { UpdateDraggedPoint(pending.point, x, y); HitTestMakeSelection(mp); hRequest hr = pending.point.request(); Vector ona = SK.GetEntity(hr.entity(2))->PointGetNum(); Vector onb = SK.GetEntity(hr.entity(3))->PointGetNum(); Vector center = (ona.Plus(onb)).ScaledBy(0.5); SK.GetEntity(hr.entity(1))->PointForceTo(center); orig.mouse = mp; SS.MarkGroupDirtyByEntity(pending.point); break; } case DRAGGING_NEW_RADIUS: case DRAGGING_RADIUS: { Entity *circle = SK.GetEntity(pending.circle); Vector center = SK.GetEntity(circle->point[0])->PointGetNum(); Point2d c2 = ProjectPoint(center); double r = c2.DistanceTo(mp)/scale; SK.GetEntity(circle->distance)->DistanceForceTo(r); SS.MarkGroupDirtyByEntity(pending.circle); break; } case DRAGGING_NORMAL: { Entity *normal = SK.GetEntity(pending.normal); Vector p = SK.GetEntity(normal->point[0])->PointGetNum(); Point2d p2 = ProjectPoint(p); Quaternion q = normal->NormalGetNum(); Vector u = q.RotationU(), v = q.RotationV(); if(ctrlDown) { double theta = atan2(orig.mouse.y-p2.y, orig.mouse.x-p2.x); theta -= atan2(y-p2.y, x-p2.x); Vector normal = projRight.Cross(projUp); u = u.RotatedAbout(normal, -theta); v = v.RotatedAbout(normal, -theta); } else { double dx = -(x - orig.mouse.x); double dy = -(y - orig.mouse.y); double s = 0.3*(PI/180); // degrees per pixel u = u.RotatedAbout(projUp, -s*dx); u = u.RotatedAbout(projRight, s*dy); v = v.RotatedAbout(projUp, -s*dx); v = v.RotatedAbout(projRight, s*dy); } orig.mouse = mp; normal->NormalForceTo(Quaternion::From(u, v)); SS.MarkGroupDirtyByEntity(pending.normal); break; } case DRAGGING_MARQUEE: orig.mouse = mp; InvalidateGraphics(); break; default: oops(); } if(pending.operation != 0 && pending.operation != DRAGGING_CONSTRAINT && pending.operation != DRAGGING_MARQUEE) { SS.GenerateAll(); } havePainted = false; }
void GraphicsWindow::MouseLeftDown(double mx, double my) { orig.mouseDown = true; if(GraphicsEditControlIsVisible()) { orig.mouse = Point2d::From(mx, my); orig.mouseOnButtonDown = orig.mouse; HideGraphicsEditControl(); return; } SS.TW.HideEditControl(); if(SS.showToolbar) { if(ToolbarMouseDown((int)mx, (int)my)) return; } // Make sure the hover is up to date. MouseMoved(mx, my, false, false, false, false, false); orig.mouse.x = mx; orig.mouse.y = my; orig.mouseOnButtonDown = orig.mouse; // The current mouse location Vector v = offset.ScaledBy(-1); v = v.Plus(projRight.ScaledBy(mx/scale)); v = v.Plus(projUp.ScaledBy(my/scale)); hRequest hr; switch(pending.operation) { case MNU_DATUM_POINT: hr = AddRequest(Request::DATUM_POINT); SK.GetEntity(hr.entity(0))->PointForceTo(v); ConstrainPointByHovered(hr.entity(0)); ClearSuper(); break; case MNU_LINE_SEGMENT: hr = AddRequest(Request::LINE_SEGMENT); SK.GetEntity(hr.entity(1))->PointForceTo(v); ConstrainPointByHovered(hr.entity(1)); ClearSuper(); pending.operation = DRAGGING_NEW_LINE_POINT; pending.point = hr.entity(2); pending.description = "click next point of line, or press Esc"; SK.GetEntity(pending.point)->PointForceTo(v); break; case MNU_RECTANGLE: { if(!SS.GW.LockedInWorkplane()) { Error("Can't draw rectangle in 3d; select a workplane first."); ClearSuper(); break; } hRequest lns[4]; int i; SS.UndoRemember(); for(i = 0; i < 4; i++) { lns[i] = AddRequest(Request::LINE_SEGMENT, false); } for(i = 0; i < 4; i++) { Constraint::ConstrainCoincident( lns[i].entity(1), lns[(i+1)%4].entity(2)); SK.GetEntity(lns[i].entity(1))->PointForceTo(v); SK.GetEntity(lns[i].entity(2))->PointForceTo(v); } for(i = 0; i < 4; i++) { Constraint::Constrain( (i % 2) ? Constraint::HORIZONTAL : Constraint::VERTICAL, Entity::NO_ENTITY, Entity::NO_ENTITY, lns[i].entity(0)); } ConstrainPointByHovered(lns[2].entity(1)); pending.operation = DRAGGING_NEW_POINT; pending.point = lns[1].entity(2); pending.description = "click to place other corner of rectangle"; break; } case MNU_CIRCLE: hr = AddRequest(Request::CIRCLE); // Centered where we clicked SK.GetEntity(hr.entity(1))->PointForceTo(v); // Normal to the screen SK.GetEntity(hr.entity(32))->NormalForceTo( Quaternion::From(SS.GW.projRight, SS.GW.projUp)); // Initial radius zero SK.GetEntity(hr.entity(64))->DistanceForceTo(0); ConstrainPointByHovered(hr.entity(1)); ClearSuper(); pending.operation = DRAGGING_NEW_RADIUS; pending.circle = hr.entity(0); pending.description = "click to set radius"; break; case MNU_ARC: { if(!SS.GW.LockedInWorkplane()) { Error("Can't draw arc in 3d; select a workplane first."); ClearPending(); break; } hr = AddRequest(Request::ARC_OF_CIRCLE); // This fudge factor stops us from immediately failing to solve // because of the arc's implicit (equal radius) tangent. Vector adj = SS.GW.projRight.WithMagnitude(2/SS.GW.scale); SK.GetEntity(hr.entity(1))->PointForceTo(v.Minus(adj)); SK.GetEntity(hr.entity(2))->PointForceTo(v); SK.GetEntity(hr.entity(3))->PointForceTo(v); ConstrainPointByHovered(hr.entity(2)); ClearSuper(); pending.operation = DRAGGING_NEW_ARC_POINT; pending.point = hr.entity(3); pending.description = "click to place point"; break; } case MNU_CUBIC: hr = AddRequest(Request::CUBIC); SK.GetEntity(hr.entity(1))->PointForceTo(v); SK.GetEntity(hr.entity(2))->PointForceTo(v); SK.GetEntity(hr.entity(3))->PointForceTo(v); SK.GetEntity(hr.entity(4))->PointForceTo(v); ConstrainPointByHovered(hr.entity(1)); ClearSuper(); pending.operation = DRAGGING_NEW_CUBIC_POINT; pending.point = hr.entity(4); pending.description = "click next point of cubic, or press Esc"; break; case MNU_WORKPLANE: if(LockedInWorkplane()) { Error("Sketching in a workplane already; sketch in 3d before " "creating new workplane."); ClearSuper(); break; } hr = AddRequest(Request::WORKPLANE); SK.GetEntity(hr.entity(1))->PointForceTo(v); SK.GetEntity(hr.entity(32))->NormalForceTo( Quaternion::From(SS.GW.projRight, SS.GW.projUp)); ConstrainPointByHovered(hr.entity(1)); ClearSuper(); break; case MNU_TTF_TEXT: { if(!SS.GW.LockedInWorkplane()) { Error("Can't draw text in 3d; select a workplane first."); ClearSuper(); break; } hr = AddRequest(Request::TTF_TEXT); Request *r = SK.GetRequest(hr); r->str.strcpy("Abc"); r->font.strcpy("arial.ttf"); SK.GetEntity(hr.entity(1))->PointForceTo(v); SK.GetEntity(hr.entity(2))->PointForceTo(v); pending.operation = DRAGGING_NEW_POINT; pending.point = hr.entity(2); pending.description = "click to place bottom left of text"; break; } case MNU_COMMENT: { ClearSuper(); Constraint c; ZERO(&c); c.group = SS.GW.activeGroup; c.workplane = SS.GW.ActiveWorkplane(); c.type = Constraint::COMMENT; c.disp.offset = v; c.comment.strcpy("NEW COMMENT -- DOUBLE-CLICK TO EDIT"); Constraint::AddConstraint(&c); break; } case DRAGGING_RADIUS: case DRAGGING_NEW_POINT: // The MouseMoved event has already dragged it as desired. ClearPending(); break; case DRAGGING_NEW_ARC_POINT: ConstrainPointByHovered(pending.point); ClearPending(); break; case DRAGGING_NEW_CUBIC_POINT: { hRequest hr = pending.point.request(); Request *r = SK.GetRequest(hr); if(hover.entity.v == hr.entity(1).v && r->extraPoints >= 2) { // They want the endpoints coincident, which means a periodic // spline instead. r->type = Request::CUBIC_PERIODIC; // Remove the off-curve control points, which are no longer // needed here; so move [2,ep+1] down, skipping first pt. int i; for(i = 2; i <= r->extraPoints+1; i++) { SK.GetEntity(hr.entity((i-1)+1))->PointForceTo( SK.GetEntity(hr.entity(i+1))->PointGetNum()); } // and move ep+3 down by two, skipping both SK.GetEntity(hr.entity((r->extraPoints+1)+1))->PointForceTo( SK.GetEntity(hr.entity((r->extraPoints+3)+1))->PointGetNum()); r->extraPoints -= 2; // And we're done. SS.MarkGroupDirty(r->group); SS.ScheduleGenerateAll(); ClearPending(); break; } if(ConstrainPointByHovered(pending.point)) { ClearPending(); break; } Entity e; if(r->extraPoints >= (int)arraylen(e.point) - 4) { ClearPending(); break; } (SK.GetRequest(hr)->extraPoints)++; SS.GenerateAll(-1, -1); int ep = r->extraPoints; Vector last = SK.GetEntity(hr.entity(3+ep))->PointGetNum(); SK.GetEntity(hr.entity(2+ep))->PointForceTo(last); SK.GetEntity(hr.entity(3+ep))->PointForceTo(v); SK.GetEntity(hr.entity(4+ep))->PointForceTo(v); pending.point = hr.entity(4+ep); break; } case DRAGGING_NEW_LINE_POINT: { if(hover.entity.v) { Entity *e = SK.GetEntity(hover.entity); if(e->IsPoint()) { hRequest hrl = pending.point.request(); Entity *sp = SK.GetEntity(hrl.entity(1)); if(( e->PointGetNum()).Equals( (sp->PointGetNum()))) { // If we constrained by the hovered point, then we // would create a zero-length line segment. That's // not good, so just stop drawing. ClearPending(); break; } } } if(ConstrainPointByHovered(pending.point)) { ClearPending(); break; } // Create a new line segment, so that we continue drawing. hRequest hr = AddRequest(Request::LINE_SEGMENT); SK.GetEntity(hr.entity(1))->PointForceTo(v); // Displace the second point of the new line segment slightly, // to avoid creating zero-length edge warnings. SK.GetEntity(hr.entity(2))->PointForceTo( v.Plus(projRight.ScaledBy(0.5/scale))); // Constrain the line segments to share an endpoint Constraint::ConstrainCoincident(pending.point, hr.entity(1)); // And drag an endpoint of the new line segment pending.operation = DRAGGING_NEW_LINE_POINT; pending.point = hr.entity(2); pending.description = "click next point of line, or press Esc"; break; } case 0: default: ClearPending(); if(!hover.IsEmpty()) { hoverWasSelectedOnMousedown = IsSelected(&hover); MakeSelected(&hover); } break; } SS.ScheduleShowTW(); InvalidateGraphics(); }
void GraphicsWindow::MouseRightUp(double x, double y) { SS.extraLine.draw = 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; if(orig.startedMoving) return; if(context.active) return; if(pending.operation == DRAGGING_NEW_LINE_POINT || pending.operation == DRAGGING_NEW_CUBIC_POINT) { // Special case; use a right click to stop drawing lines, since // a left click would draw another one. This is quicker and more // intuitive than hitting escape. Likewise for new cubic segments. ClearPending(); return; } context.active = true; if(!hover.IsEmpty()) { MakeSelected(&hover); SS.ScheduleShowTW(); } GroupSelection(); bool itemsSelected = (gs.n > 0 || gs.constraints > 0); if(itemsSelected) { if(gs.stylables > 0) { ContextMenuListStyles(); AddContextMenuItem("Assign to Style", CONTEXT_SUBMENU); } if(gs.n + gs.constraints == 1) { AddContextMenuItem("Group Info", CMNU_GROUP_INFO); } if(gs.n + gs.constraints == 1 && gs.stylables == 1) { AddContextMenuItem("Style Info", CMNU_STYLE_INFO); } if(gs.withEndpoints > 0) { AddContextMenuItem("Select Edge Chain", CMNU_SELECT_CHAIN); } if(gs.constraints == 1 && gs.n == 0) { Constraint *c = SK.GetConstraint(gs.constraint[0]); if(c->HasLabel() && c->type != Constraint::COMMENT) { AddContextMenuItem("Toggle Reference Dimension", CMNU_REFERENCE_DIM); } if(c->type == Constraint::ANGLE || c->type == Constraint::EQUAL_ANGLE) { AddContextMenuItem("Other Supplementary Angle", CMNU_OTHER_ANGLE); } } if(gs.comments > 0 || gs.points > 0) { AddContextMenuItem("Snap to Grid", CMNU_SNAP_TO_GRID); } if(gs.points == 1) { Entity *p = SK.GetEntity(gs.point[0]); Constraint *c; IdList<Constraint,hConstraint> *lc = &(SK.constraint); for(c = lc->First(); c; c = lc->NextAfter(c)) { if(c->type != Constraint::POINTS_COINCIDENT) continue; if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) { break; } } if(c) { AddContextMenuItem("Delete Point-Coincident Constraint", CMNU_DEL_COINCIDENT); } } AddContextMenuItem(NULL, CONTEXT_SEPARATOR); if(LockedInWorkplane()) { AddContextMenuItem("Cut", CMNU_CUT_SEL); AddContextMenuItem("Copy", CMNU_COPY_SEL); } } if(SS.clipboard.r.n > 0 && LockedInWorkplane()) { AddContextMenuItem("Paste", CMNU_PASTE_SEL); } if(itemsSelected) { AddContextMenuItem("Delete", CMNU_DELETE_SEL); AddContextMenuItem(NULL, CONTEXT_SEPARATOR); AddContextMenuItem("Unselect All", CMNU_UNSELECT_ALL); } // If only one item is selected, then it must be the one that we just // selected from the hovered item; in which case unselect all and hovered // are equivalent. if(!hover.IsEmpty() && selection.n > 1) { AddContextMenuItem("Unselect Hovered", CMNU_UNSELECT_HOVERED); } int ret = ShowContextMenu(); switch(ret) { case CMNU_UNSELECT_ALL: MenuEdit(MNU_UNSELECT_ALL); break; case CMNU_UNSELECT_HOVERED: if(!hover.IsEmpty()) { MakeUnselected(&hover, true); } break; case CMNU_SELECT_CHAIN: MenuEdit(MNU_SELECT_CHAIN); break; case CMNU_CUT_SEL: MenuClipboard(MNU_CUT); break; case CMNU_COPY_SEL: MenuClipboard(MNU_COPY); break; case CMNU_PASTE_SEL: MenuClipboard(MNU_PASTE); break; case CMNU_DELETE_SEL: MenuClipboard(MNU_DELETE); break; case CMNU_REFERENCE_DIM: Constraint::MenuConstrain(MNU_REFERENCE); break; case CMNU_OTHER_ANGLE: Constraint::MenuConstrain(MNU_OTHER_ANGLE); break; case CMNU_DEL_COINCIDENT: { SS.UndoRemember(); if(!gs.point[0].v) break; Entity *p = SK.GetEntity(gs.point[0]); if(!p->IsPoint()) break; SK.constraint.ClearTags(); Constraint *c; for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) { if(c->type != Constraint::POINTS_COINCIDENT) continue; if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) { c->tag = 1; } } SK.constraint.RemoveTagged(); ClearSelection(); break; } case CMNU_SNAP_TO_GRID: MenuEdit(MNU_SNAP_TO_GRID); break; case CMNU_GROUP_INFO: { hGroup hg; if(gs.entities == 1) { hg = SK.GetEntity(gs.entity[0])->group; } else if(gs.points == 1) { hg = SK.GetEntity(gs.point[0])->group; } else if(gs.constraints == 1) { hg = SK.GetConstraint(gs.constraint[0])->group; } else { break; } ClearSelection(); SS.TW.GoToScreen(TextWindow::SCREEN_GROUP_INFO); SS.TW.shown.group = hg; SS.ScheduleShowTW(); break; } case CMNU_STYLE_INFO: { hStyle hs; if(gs.entities == 1) { hs = Style::ForEntity(gs.entity[0]); } else if(gs.points == 1) { hs = Style::ForEntity(gs.point[0]); } else if(gs.constraints == 1) { hs = SK.GetConstraint(gs.constraint[0])->disp.style; if(!hs.v) hs.v = Style::CONSTRAINT; } else { break; } ClearSelection(); SS.TW.GoToScreen(TextWindow::SCREEN_STYLE_INFO); SS.TW.shown.style = hs; SS.ScheduleShowTW(); break; } case CMNU_NEW_CUSTOM_STYLE: { uint32_t v = Style::CreateCustomStyle(); Style::AssignSelectionToStyle(v); break; } case CMNU_NO_STYLE: Style::AssignSelectionToStyle(0); break; default: if(ret >= CMNU_FIRST_STYLE) { Style::AssignSelectionToStyle(ret - CMNU_FIRST_STYLE); } else { // otherwise it was cancelled, so do nothing contextMenuCancelTime = GetMilliseconds(); } break; } context.active = false; SS.ScheduleShowTW(); }
void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) { Entity *wrkpl = SK.GetEntity(ActiveWorkplane()); Entity *wrkpln = SK.GetEntity(wrkpl->normal); Vector u = wrkpln->NormalU(), v = wrkpln->NormalV(), n = wrkpln->NormalN(), p = SK.GetEntity(wrkpl->point[0])->PointGetNum(); ClipboardRequest *cr; for(cr = SS.clipboard.r.First(); cr; cr = SS.clipboard.r.NextAfter(cr)) { hRequest hr = AddRequest(cr->type, false); Request *r = SK.GetRequest(hr); r->extraPoints = cr->extraPoints; r->style = cr->style; r->str.strcpy( cr->str.str); r->font.strcpy( cr->font.str); r->construction = cr->construction; // Need to regen to get the right number of points, if extraPoints // changed. SS.GenerateAll(-1, -1); SS.MarkGroupDirty(r->group); bool hasDistance; int i, pts; EntReqTable::GetRequestInfo(r->type, r->extraPoints, NULL, &pts, NULL, &hasDistance); for(i = 0; i < pts; i++) { Vector pt = cr->point[i]; // We need the reflection to occur within the workplane; it may // otherwise correspond to just a rotation as projected. if(scale < 0) { pt.x *= -1; } // Likewise the scale, which could otherwise take us out of the // workplane. pt = pt.ScaledBy(fabs(scale)); pt = pt.ScaleOutOfCsys(u, v, Vector::From(0, 0, 0)); pt = pt.Plus(p); pt = pt.RotatedAbout(n, theta); pt = pt.Plus(trans); SK.GetEntity(hr.entity(i+1))->PointForceTo(pt); } if(hasDistance) { SK.GetEntity(hr.entity(64))->DistanceForceTo( cr->distance*fabs(scale)); } cr->newReq = hr; MakeSelected(hr.entity(0)); for(i = 0; i < pts; i++) { MakeSelected(hr.entity(i+1)); } } Constraint *c; for(c = SS.clipboard.c.First(); c; c = SS.clipboard.c.NextAfter(c)) { if(c->type == Constraint::POINTS_COINCIDENT) { Constraint::ConstrainCoincident(SS.clipboard.NewEntityFor(c->ptA), SS.clipboard.NewEntityFor(c->ptB)); } } SS.later.generateAll = true; }
void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) { Entity *wrkpl = SK.GetEntity(ActiveWorkplane()); Entity *wrkpln = SK.GetEntity(wrkpl->normal); Vector u = wrkpln->NormalU(), v = wrkpln->NormalV(), n = wrkpln->NormalN(), p = SK.GetEntity(wrkpl->point[0])->PointGetNum(); ClipboardRequest *cr; int requestCount = 0; for(cr = SS.clipboard.r.First(); cr; cr = SS.clipboard.r.NextAfter(cr)) { hRequest hr = AddRequest(cr->type, false); Request *r = SK.GetRequest(hr); r->extraPoints = cr->extraPoints; r->style = cr->style; r->str.strcpy( cr->str.str); r->font.strcpy( cr->font.str); r->construction = cr->construction; // Need to regen to get the right number of points, if extraPoints // changed. SS.GenerateAll(-1, -1); SS.MarkGroupDirty(r->group); bool hasDistance; int i, pts; EntReqTable::GetRequestInfo(r->type, r->extraPoints, NULL, &pts, NULL, &hasDistance); for(i = 0; i < pts; i++) { Vector pt = cr->point[i]; // We need the reflection to occur within the workplane; it may // otherwise correspond to just a rotation as projected. if(scale < 0) { pt.x *= -1; } // Likewise the scale, which could otherwise take us out of the // workplane. pt = pt.ScaledBy(fabs(scale)); pt = pt.ScaleOutOfCsys(u, v, Vector::From(0, 0, 0)); pt = pt.Plus(p); pt = pt.RotatedAbout(n, theta); pt = pt.Plus(trans); SK.GetEntity(hr.entity(i+1))->PointForceTo(pt); } if(hasDistance) { SK.GetEntity(hr.entity(64))->DistanceForceTo( cr->distance*fabs(scale)); } cr->newReq = hr; MakeSelected(hr.entity(0)); for(i = 0; i < pts; i++) { MakeSelected(hr.entity(i+1)); } ++requestCount; } Constraint *c; for(c = SS.clipboard.c.First(); c; c = SS.clipboard.c.NextAfter(c)) { if(c->type == Constraint::POINTS_COINCIDENT) { Constraint::ConstrainCoincident(SS.clipboard.NewEntityFor(c->ptA), SS.clipboard.NewEntityFor(c->ptB)); } if (c->type == Constraint::HORIZONTAL || c->type == Constraint::VERTICAL) { //mv horizontally constrainted object is either a line segment or 2 points if (!SS.copyConstraints) continue; Entity *ea = SK.GetEntityNoOops(c->entityA); if(ea && ea->type == Entity::LINE_SEGMENT) { if (!SS.copyConstraints) continue; Constraint::Constrain(c->type, Entity::NO_ENTITY, Entity::NO_ENTITY, SS.clipboard.NewEntityFor(c->entityA)); } else { Constraint::Constrain(c->type, SS.clipboard.NewEntityFor(c->ptA), SS.clipboard.NewEntityFor(c->ptB), Entity::NO_ENTITY); } } if (c->type == Constraint::DIAMETER) { if (!SS.copyConstraints) continue; Entity *ea = SK.GetEntityNoOops(c->entityA); if (ea && (ea->type == Entity::CIRCLE || ea->type == Entity::ARC_OF_CIRCLE)) { Constraint::ConstrainValue(c->type, Entity::NO_ENTITY, Entity::NO_ENTITY, SS.clipboard.NewEntityFor(c->entityA), Entity::NO_ENTITY, c->valA); } } if (c->type == Constraint::PT_PT_DISTANCE) { if (!SS.copyConstraints) continue; Constraint::ConstrainValue(c->type, SS.clipboard.NewEntityFor(c->ptA), SS.clipboard.NewEntityFor(c->ptB), Entity::NO_ENTITY, Entity::NO_ENTITY, c->valA); } if (c->type == Constraint::PT_LINE_DISTANCE) { if (!SS.copyConstraints) continue; Constraint::ConstrainValue(c->type, SS.clipboard.NewEntityFor(c->ptA), Entity::NO_ENTITY, SS.clipboard.NewEntityFor(c->entityA), Entity::NO_ENTITY, c->valA); } if (c->type == Constraint::EQUAL_RADIUS /*&& requestCount == 1*/) { if (!SS.copyConstraints) continue; ConstrainSameSomething(c); } if (c->type == Constraint::EQUAL_LENGTH_LINES) { //eMVe6. Dangerous. Can make solvespace unresponsive. if (!SS.copyConstraints) continue; ConstrainSameSomething(c); } if (c->type == Constraint::ANGLE) { if (!SS.copyConstraints) continue; /* UNRELIABLE & SLOW AS HELL if (SS.clipboard.ContainsEntity(c->entityA) && SS.clipboard.ContainsEntity(c->entityB)) { Constraint::ConstrainValue(c->type, Entity::NO_ENTITY, Entity::NO_ENTITY, SS.clipboard.NewEntityFor(c->entityA), SS.clipboard.NewEntityFor(c->entityB), c->valA); } */ } if (c->type == Constraint::PARALLEL) { if (!SS.copyConstraints) continue; ConstrainSameSomething(c); } if (c->type == Constraint::SYMMETRIC_LINE) { if (SS.clipboard.ContainsEntity(c->ptA) && SS.clipboard.ContainsEntity(c->ptB) && SS.clipboard.ContainsEntity(c->entityA)) { Constraint::Constrain(c->type, SS.clipboard.NewEntityFor(c->ptA), SS.clipboard.NewEntityFor(c->ptB), SS.clipboard.NewEntityFor(c->entityA), Entity::NO_ENTITY, false, false); } } } SS.later.generateAll = true; }
//----------------------------------------------------------------------------- // Select an item, if it isn't selected already. //----------------------------------------------------------------------------- void GraphicsWindow::MakeSelected(hEntity he) { Selection stog; ZERO(&stog); stog.entity = he; MakeSelected(&stog); }