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
parent
d99a133982
commit
25631d4fb2
|
@ -14,6 +14,12 @@ New sketch features:
|
||||||
constraining the width of text.
|
constraining the width of text.
|
||||||
* Irrelevant points (e.g. arc center point) are not counted when estimating
|
* Irrelevant points (e.g. arc center point) are not counted when estimating
|
||||||
the bounding box used to compute chord tolerance.
|
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:
|
New export/import features:
|
||||||
* Three.js: allow configuring projection for exported model, and initially
|
* Three.js: allow configuring projection for exported model, and initially
|
||||||
|
|
75
src/draw.cpp
75
src/draw.cpp
|
@ -326,8 +326,48 @@ Lighting GraphicsWindow::GetLighting() const {
|
||||||
return lighting;
|
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) {
|
void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
|
||||||
Selection s = {};
|
hoverList = {};
|
||||||
|
Selection sel = {};
|
||||||
|
|
||||||
// Did the view projection change? If so, invalidate bounding boxes.
|
// Did the view projection change? If so, invalidate bounding boxes.
|
||||||
if(!offset.EqualsExactly(cached.offset) ||
|
if(!offset.EqualsExactly(cached.offset) ||
|
||||||
|
@ -368,8 +408,11 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(canvas.Pick([&]{ e.Draw(Entity::DrawAs::DEFAULT, &canvas); })) {
|
if(canvas.Pick([&]{ e.Draw(Entity::DrawAs::DEFAULT, &canvas); })) {
|
||||||
s = {};
|
Hover hov = {};
|
||||||
s.entity = e.h;
|
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) {
|
if(pending.operation == Pending::NONE) {
|
||||||
// Constraints
|
// Constraints
|
||||||
for(Constraint &c : SK.constraint) {
|
for(Constraint &c : SK.constraint) {
|
||||||
if(!c.IsVisible()) continue;
|
|
||||||
|
|
||||||
if(canvas.Pick([&]{ c.Draw(Constraint::DrawAs::DEFAULT, &canvas); })) {
|
if(canvas.Pick([&]{ c.Draw(Constraint::DrawAs::DEFAULT, &canvas); })) {
|
||||||
s = {};
|
Hover hov = {};
|
||||||
s.constraint = c.h;
|
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
|
// 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);
|
Group *g = SK.GetGroup(activeGroup);
|
||||||
SMesh *m = &(g->displayMesh);
|
SMesh *m = &(g->displayMesh);
|
||||||
|
|
||||||
uint32_t v = m->FirstIntersectionWith(mp);
|
uint32_t v = m->FirstIntersectionWith(mp);
|
||||||
if(v) {
|
if(v) {
|
||||||
s.entity.v = v;
|
sel.entity.v = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.Clear();
|
canvas.Clear();
|
||||||
|
|
||||||
if(!s.Equals(&hover)) {
|
if(!sel.Equals(&hover)) {
|
||||||
hover = s;
|
hover = sel;
|
||||||
PaintGraphics();
|
PaintGraphics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -446,7 +446,9 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
||||||
if(!IsVisible()) return;
|
if(!IsVisible()) return;
|
||||||
|
|
||||||
int zIndex;
|
int zIndex;
|
||||||
if(how == DrawAs::HIDDEN) {
|
if(IsPoint()) {
|
||||||
|
zIndex = 5;
|
||||||
|
} else if(how == DrawAs::HIDDEN) {
|
||||||
zIndex = 2;
|
zIndex = 2;
|
||||||
} else if(group.v != SS.GW.activeGroup.v) {
|
} else if(group.v != SS.GW.activeGroup.v) {
|
||||||
zIndex = 3;
|
zIndex = 3;
|
||||||
|
@ -494,7 +496,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
||||||
|
|
||||||
Canvas::Stroke pointStroke = {};
|
Canvas::Stroke pointStroke = {};
|
||||||
pointStroke.layer = stroke.layer;
|
pointStroke.layer = stroke.layer;
|
||||||
pointStroke.zIndex = IsPoint() ? zIndex + 1 : 0;
|
pointStroke.zIndex = stroke.zIndex;
|
||||||
pointStroke.color = stroke.color;
|
pointStroke.color = stroke.color;
|
||||||
pointStroke.width = 7.0;
|
pointStroke.width = 7.0;
|
||||||
pointStroke.unit = Canvas::Unit::PX;
|
pointStroke.unit = Canvas::Unit::PX;
|
||||||
|
|
|
@ -72,7 +72,7 @@ void GraphicsWindow::StartDraggingBySelection() {
|
||||||
// The user might select a point, and then click it again to start
|
// 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
|
// dragging; but the point just got unselected by that click. So drag
|
||||||
// the hovered item too, and they'll always have it.
|
// 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,
|
void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
|
|
|
@ -439,6 +439,8 @@ void ObjectPicker::DrawPixmap(std::shared_ptr<const Pixmap> pm,
|
||||||
|
|
||||||
bool ObjectPicker::Pick(std::function<void()> drawFn) {
|
bool ObjectPicker::Pick(std::function<void()> drawFn) {
|
||||||
minDistance = VERY_POSITIVE;
|
minDistance = VERY_POSITIVE;
|
||||||
|
maxZIndex = INT_MIN;
|
||||||
|
|
||||||
drawFn();
|
drawFn();
|
||||||
return minDistance < selRadius;
|
return minDistance < selRadius;
|
||||||
}
|
}
|
||||||
|
|
15
src/ui.h
15
src/ui.h
|
@ -645,7 +645,7 @@ public:
|
||||||
void FixConstraintsForRequestBeingDeleted(hRequest hr);
|
void FixConstraintsForRequestBeingDeleted(hRequest hr);
|
||||||
void FixConstraintsForPointBeingDeleted(hEntity hpt);
|
void FixConstraintsForPointBeingDeleted(hEntity hpt);
|
||||||
|
|
||||||
// The current selection.
|
// A selected entity.
|
||||||
class Selection {
|
class Selection {
|
||||||
public:
|
public:
|
||||||
int tag;
|
int tag;
|
||||||
|
@ -661,9 +661,22 @@ public:
|
||||||
bool Equals(Selection *b);
|
bool Equals(Selection *b);
|
||||||
bool HasEndpoints();
|
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;
|
Selection hover;
|
||||||
bool hoverWasSelectedOnMousedown;
|
bool hoverWasSelectedOnMousedown;
|
||||||
List<Selection> selection;
|
List<Selection> selection;
|
||||||
|
|
||||||
|
Selection ChooseFromHoverToSelect();
|
||||||
|
Selection ChooseFromHoverToDrag();
|
||||||
void HitTestMakeSelection(Point2d mp);
|
void HitTestMakeSelection(Point2d mp);
|
||||||
void ClearSelection();
|
void ClearSelection();
|
||||||
void ClearNonexistentSelectionItems();
|
void ClearNonexistentSelectionItems();
|
||||||
|
|
Loading…
Reference in New Issue