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.
pull/106/head
EvilSpirit 2016-07-21 23:58:18 +06:00 committed by whitequark
parent d99a133982
commit 25631d4fb2
6 changed files with 91 additions and 15 deletions

View File

@ -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

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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,

View File

@ -439,6 +439,8 @@ void ObjectPicker::DrawPixmap(std::shared_ptr<const Pixmap> pm,
bool ObjectPicker::Pick(std::function<void()> drawFn) {
minDistance = VERY_POSITIVE;
maxZIndex = INT_MIN;
drawFn();
return minDistance < selRadius;
}

View File

@ -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<Hover> hoverList;
Selection hover;
bool hoverWasSelectedOnMousedown;
List<Selection> selection;
Selection ChooseFromHoverToSelect();
Selection ChooseFromHoverToDrag();
void HitTestMakeSelection(Point2d mp);
void ClearSelection();
void ClearNonexistentSelectionItems();