diff --git a/CHANGELOG.md b/CHANGELOG.md index de85b8e7..df3fb798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,12 @@ New sketch features: constraining the width of text. * Irrelevant points (e.g. arc center point) are not counted when estimating the bounding box used to compute chord tolerance. + * When clicking on an entity that shares a place with other entities, + the entity from the current group is selected. + * When dragging an entity that shares a place with other entities, + the entity from a request is selected. For example, dragging a point on + a face of an extrusion coincident with the source sketch plane will + drag the point from the source sketch. New export/import features: * Three.js: allow configuring projection for exported model, and initially diff --git a/src/draw.cpp b/src/draw.cpp index 548df024..00adf7a6 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -326,8 +326,48 @@ Lighting GraphicsWindow::GetLighting() const { return lighting; } +GraphicsWindow::Selection GraphicsWindow::ChooseFromHoverToSelect() { + Selection sel = {}; + if(hoverList.n == 0) return sel; + + Group *activeGroup = SK.GetGroup(SS.GW.activeGroup); + int bestOrder = -1; + int bestZIndex; + for(const Hover &hov : hoverList) { + hGroup hg = {}; + if(hov.selection.entity.v != 0) { + hg = SK.GetEntity(hov.selection.entity)->group; + } else if(hov.selection.constraint.v != 0) { + hg = SK.GetConstraint(hov.selection.constraint)->group; + } + + Group *g = SK.GetGroup(hg); + if(g->order > activeGroup->order) continue; + if(bestOrder != -1 && (bestOrder >= g->order || bestZIndex > hov.zIndex)) continue; + bestOrder = g->order; + bestZIndex = hov.zIndex; + sel = hov.selection; + } + return sel; +} + +GraphicsWindow::Selection GraphicsWindow::ChooseFromHoverToDrag() { + Selection sel = {}; + for(const Hover &hov : hoverList) { + if(hov.selection.entity.v == 0) continue; + if(!hov.selection.entity.isFromRequest()) continue; + sel = hov.selection; + break; + } + if(!sel.IsEmpty()) { + return sel; + } + return ChooseFromHoverToSelect(); +} + void GraphicsWindow::HitTestMakeSelection(Point2d mp) { - Selection s = {}; + hoverList = {}; + Selection sel = {}; // Did the view projection change? If so, invalidate bounding boxes. if(!offset.EqualsExactly(cached.offset) || @@ -368,8 +408,11 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) { } if(canvas.Pick([&]{ e.Draw(Entity::DrawAs::DEFAULT, &canvas); })) { - s = {}; - s.entity = e.h; + Hover hov = {}; + hov.distance = canvas.minDistance; + hov.zIndex = canvas.maxZIndex; + hov.selection.entity = e.h; + hoverList.Add(&hov); } } @@ -377,30 +420,40 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) { if(pending.operation == Pending::NONE) { // Constraints for(Constraint &c : SK.constraint) { - if(!c.IsVisible()) continue; - if(canvas.Pick([&]{ c.Draw(Constraint::DrawAs::DEFAULT, &canvas); })) { - s = {}; - s.constraint = c.h; + Hover hov = {}; + hov.distance = canvas.minDistance; + hov.zIndex = canvas.maxZIndex; + hov.selection.constraint = c.h; + hoverList.Add(&hov); } } + } + std::sort(hoverList.begin(), hoverList.end(), + [](const Hover &a, const Hover &b) { + if(a.zIndex == b.zIndex) return a.distance < b.distance; + return a.zIndex > b.zIndex; + }); + sel = ChooseFromHoverToSelect(); + + if(pending.operation == Pending::NONE) { // Faces, from the triangle mesh; these are lowest priority - if(s.constraint.v == 0 && s.entity.v == 0 && showShaded && showFaces) { + if(sel.constraint.v == 0 && sel.entity.v == 0 && showShaded && showFaces) { Group *g = SK.GetGroup(activeGroup); SMesh *m = &(g->displayMesh); uint32_t v = m->FirstIntersectionWith(mp); if(v) { - s.entity.v = v; + sel.entity.v = v; } } } canvas.Clear(); - if(!s.Equals(&hover)) { - hover = s; + if(!sel.Equals(&hover)) { + hover = sel; PaintGraphics(); } } diff --git a/src/drawentity.cpp b/src/drawentity.cpp index 8242a1c9..cc93c82d 100644 --- a/src/drawentity.cpp +++ b/src/drawentity.cpp @@ -446,7 +446,9 @@ void Entity::Draw(DrawAs how, Canvas *canvas) { if(!IsVisible()) return; int zIndex; - if(how == DrawAs::HIDDEN) { + if(IsPoint()) { + zIndex = 5; + } else if(how == DrawAs::HIDDEN) { zIndex = 2; } else if(group.v != SS.GW.activeGroup.v) { zIndex = 3; @@ -494,7 +496,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) { Canvas::Stroke pointStroke = {}; pointStroke.layer = stroke.layer; - pointStroke.zIndex = IsPoint() ? zIndex + 1 : 0; + pointStroke.zIndex = stroke.zIndex; pointStroke.color = stroke.color; pointStroke.width = 7.0; pointStroke.unit = Canvas::Unit::PX; diff --git a/src/mouse.cpp b/src/mouse.cpp index e0cf7d6b..eea3207e 100644 --- a/src/mouse.cpp +++ b/src/mouse.cpp @@ -72,7 +72,7 @@ void GraphicsWindow::StartDraggingBySelection() { // The user might select a point, and then click it again to start // dragging; but the point just got unselected by that click. So drag // the hovered item too, and they'll always have it. - if(hover.entity.v) StartDraggingByEntity(hover.entity); + if(hover.entity.v) StartDraggingByEntity(ChooseFromHoverToDrag().entity); } void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, diff --git a/src/render/render.cpp b/src/render/render.cpp index b3327ec0..40e1f027 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -439,6 +439,8 @@ void ObjectPicker::DrawPixmap(std::shared_ptr pm, bool ObjectPicker::Pick(std::function drawFn) { minDistance = VERY_POSITIVE; + maxZIndex = INT_MIN; + drawFn(); return minDistance < selRadius; } diff --git a/src/ui.h b/src/ui.h index 2ad10b5c..687a5a7b 100644 --- a/src/ui.h +++ b/src/ui.h @@ -645,7 +645,7 @@ public: void FixConstraintsForRequestBeingDeleted(hRequest hr); void FixConstraintsForPointBeingDeleted(hEntity hpt); - // The current selection. + // A selected entity. class Selection { public: int tag; @@ -661,9 +661,22 @@ public: bool Equals(Selection *b); bool HasEndpoints(); }; + + // A hovered entity, with its location relative to the cursor. + class Hover { + public: + int zIndex; + double distance; + Selection selection; + }; + + List hoverList; Selection hover; bool hoverWasSelectedOnMousedown; List selection; + + Selection ChooseFromHoverToSelect(); + Selection ChooseFromHoverToDrag(); void HitTestMakeSelection(Point2d mp); void ClearSelection(); void ClearNonexistentSelectionItems();