From 25631d4fb214239a1b46f8cd6248a3796dd2a5d4 Mon Sep 17 00:00:00 2001 From: EvilSpirit Date: Thu, 21 Jul 2016 23:58:18 +0600 Subject: [PATCH] Choose entities to select in a way appropriate for the operation. Before this commit, when an entity is clicked at or dragged, and it shares a place with other entities, which of them is selected is decided more or less at random. This is particularly annoying when dragging. After this commit, when clicking, an entity from the current group is given preference, and when dragging, an entity from a request is given preference. This allows e.g. dragging points of a sketch even when an extrusion of that sketch is active. --- CHANGELOG.md | 6 ++++ src/draw.cpp | 75 ++++++++++++++++++++++++++++++++++++------- src/drawentity.cpp | 6 ++-- src/mouse.cpp | 2 +- src/render/render.cpp | 2 ++ src/ui.h | 15 ++++++++- 6 files changed, 91 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de85b8e..df3fb79 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 548df02..00adf7a 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 8242a1c..cc93c82 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 e0cf7d6..eea3207 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 b3327ec..40e1f02 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 2ad10b5..687a5a7 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();