Add marquee selection, a view-aligned rectangle such that
everything even partially within that rectangle gets selected when I release. Also make deselecting a point deselect all coincident points too; otherwise there now exist ways to select both coincident points, which meant that it was impossible to deselect the lower one. And fix text window to show selection info even if just constraints are selected, seems more consistent. [git-p4: depot-paths = "//depot/solvespace/": change = 2066]solver
parent
2ae9983f9e
commit
c79604f64d
124
draw.cpp
124
draw.cpp
|
@ -118,6 +118,24 @@ void GraphicsWindow::ToggleSelectionStateOf(Selection *stog) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If two points are coincident, then it's impossible to hover one of
|
||||||
|
// them. But make sure to deselect both, to avoid mysterious seeming
|
||||||
|
// inability to deselect if the bottom one did somehow get selected.
|
||||||
|
if(wasSelected && stog->entity.v) {
|
||||||
|
Entity *e = SK.GetEntity(stog->entity);
|
||||||
|
if(e->IsPoint()) {
|
||||||
|
Vector ep = e->PointGetNum();
|
||||||
|
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||||
|
if(!s->entity.v) continue;
|
||||||
|
if(s->entity.v == stog->entity.v) continue;
|
||||||
|
Entity *se = SK.GetEntity(s->entity);
|
||||||
|
if(!se->IsPoint()) continue;
|
||||||
|
if(ep.Equals(se->PointGetNum())) {
|
||||||
|
s->tag = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
selection.RemoveTagged();
|
selection.RemoveTagged();
|
||||||
if(wasSelected) return;
|
if(wasSelected) return;
|
||||||
|
|
||||||
|
@ -141,6 +159,61 @@ void GraphicsWindow::ToggleSelectionStateOf(Selection *stog) {
|
||||||
selection.Add(stog);
|
selection.Add(stog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// 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->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)
|
||||||
|
{
|
||||||
|
ToggleSelectionStateOf(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);
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
ToggleSelectionStateOf(e->h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sel.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Sort the selection according to various critieria: the entities and
|
// Sort the selection according to various critieria: the entities and
|
||||||
// constraints separately, counts of certain types of entities (circles,
|
// constraints separately, counts of certain types of entities (circles,
|
||||||
|
@ -307,12 +380,28 @@ Vector GraphicsWindow::ProjectPoint4(Vector p, double *w) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Return a point in the plane parallel to the screen and through the offset,
|
||||||
|
// that projects onto the specified (x, y) coordinates.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
Vector GraphicsWindow::UnProjectPoint(Point2d p) {
|
||||||
|
Vector orig = offset.ScaledBy(-1);
|
||||||
|
|
||||||
|
// Note that we ignoring the effects of perspective. Since our returned
|
||||||
|
// point has the same component normal to the screen as the offset, it
|
||||||
|
// will have z = 0 after the rotation is applied, thus w = 1. So this is
|
||||||
|
// correct.
|
||||||
|
orig = orig.Plus(projRight.ScaledBy(p.x / scale)).Plus(
|
||||||
|
projUp. ScaledBy(p.y / scale));
|
||||||
|
return orig;
|
||||||
|
}
|
||||||
|
|
||||||
void GraphicsWindow::NormalizeProjectionVectors(void) {
|
void GraphicsWindow::NormalizeProjectionVectors(void) {
|
||||||
Vector norm = projRight.Cross(projUp);
|
Vector norm = projRight.Cross(projUp);
|
||||||
projUp = norm.Cross(projRight);
|
projUp = norm.Cross(projRight);
|
||||||
|
|
||||||
projUp = projUp.ScaledBy(1/projUp.Magnitude());
|
projUp = projUp.WithMagnitude(1);
|
||||||
projRight = projRight.ScaledBy(1/projRight.Magnitude());
|
projRight = projRight.WithMagnitude(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector GraphicsWindow::VectorFromProjs(Vector rightUpForward) {
|
Vector GraphicsWindow::VectorFromProjs(Vector rightUpForward) {
|
||||||
|
@ -557,6 +646,37 @@ nogrid:;
|
||||||
|
|
||||||
glxUnlockColor();
|
glxUnlockColor();
|
||||||
|
|
||||||
|
// If a marquee selection is in progress, then draw the selection
|
||||||
|
// rectangle, as an outline and a transparent fill.
|
||||||
|
if(pending.operation == DRAGGING_MARQUEE) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
Vector tl = UnProjectPoint(Point2d::From(xmin, ymin)),
|
||||||
|
tr = UnProjectPoint(Point2d::From(xmax, ymin)),
|
||||||
|
br = UnProjectPoint(Point2d::From(xmax, ymax)),
|
||||||
|
bl = UnProjectPoint(Point2d::From(xmin, ymax));
|
||||||
|
|
||||||
|
glLineWidth((GLfloat)1.3);
|
||||||
|
glxColorRGB(Style::Color(Style::HOVERED));
|
||||||
|
glBegin(GL_LINE_LOOP);
|
||||||
|
glxVertex3v(tl);
|
||||||
|
glxVertex3v(tr);
|
||||||
|
glxVertex3v(br);
|
||||||
|
glxVertex3v(bl);
|
||||||
|
glEnd();
|
||||||
|
glxColorRGBa(Style::Color(Style::HOVERED), 0.10);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glxVertex3v(tl);
|
||||||
|
glxVertex3v(tr);
|
||||||
|
glxVertex3v(br);
|
||||||
|
glxVertex3v(bl);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
// And finally the toolbar.
|
// And finally the toolbar.
|
||||||
if(SS.showToolbar) {
|
if(SS.showToolbar) {
|
||||||
ToolbarDraw();
|
ToolbarDraw();
|
||||||
|
|
47
mouse.cpp
47
mouse.cpp
|
@ -91,11 +91,14 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!leftDown && pending.operation == DRAGGING_POINTS) {
|
if(!leftDown && (pending.operation == DRAGGING_POINTS ||
|
||||||
|
pending.operation == DRAGGING_MARQUEE))
|
||||||
|
{
|
||||||
ClearPending();
|
ClearPending();
|
||||||
|
InvalidateGraphics();
|
||||||
}
|
}
|
||||||
|
|
||||||
Point2d mp = { x, y };
|
Point2d mp = Point2d::From(x, y);
|
||||||
|
|
||||||
if(rightDown && orig.mouse.DistanceTo(mp) < 5 && !orig.startedMoving) {
|
if(rightDown && orig.mouse.DistanceTo(mp) < 5 && !orig.startedMoving) {
|
||||||
// Avoid accidentally panning (or rotating if shift is down) if the
|
// Avoid accidentally panning (or rotating if shift is down) if the
|
||||||
|
@ -148,9 +151,11 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
// If we're currently not doing anything, then see if we should
|
// If we're currently not doing anything, then see if we should
|
||||||
// start dragging something.
|
// start dragging something.
|
||||||
if(leftDown && dm > 3) {
|
if(leftDown && dm > 3) {
|
||||||
if(hover.entity.v) {
|
Entity *e = NULL;
|
||||||
|
if(hover.entity.v) e = SK.GetEntity(hover.entity);
|
||||||
|
if(e && e->type != Entity::WORKPLANE) {
|
||||||
Entity *e = SK.GetEntity(hover.entity);
|
Entity *e = SK.GetEntity(hover.entity);
|
||||||
if(e->type == Entity::CIRCLE) {
|
if(e->type == Entity::CIRCLE && selection.n <= 1) {
|
||||||
// Drag the radius.
|
// Drag the radius.
|
||||||
ClearSelection();
|
ClearSelection();
|
||||||
pending.circle = hover.entity;
|
pending.circle = hover.entity;
|
||||||
|
@ -176,6 +181,22 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
// We just started a drag, so remember for the undo before
|
// We just started a drag, so remember for the undo before
|
||||||
// the drag changes anything.
|
// the drag changes anything.
|
||||||
SS.UndoRemember();
|
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.
|
||||||
|
ToggleSelectionStateOf(hover.entity);
|
||||||
|
hover.Clear();
|
||||||
|
}
|
||||||
|
pending.operation = DRAGGING_MARQUEE;
|
||||||
|
orig.marqueePoint =
|
||||||
|
UnProjectPoint(orig.mouseOnButtonDown);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, just hit test and give up; but don't hit test
|
// Otherwise, just hit test and give up; but don't hit test
|
||||||
|
@ -359,9 +380,18 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case DRAGGING_MARQUEE:
|
||||||
|
orig.mouse = mp;
|
||||||
|
InvalidateGraphics();
|
||||||
|
break;
|
||||||
|
|
||||||
default: oops();
|
default: oops();
|
||||||
}
|
}
|
||||||
if(pending.operation != 0 && pending.operation != DRAGGING_CONSTRAINT) {
|
|
||||||
|
if(pending.operation != 0 &&
|
||||||
|
pending.operation != DRAGGING_CONSTRAINT &&
|
||||||
|
pending.operation != DRAGGING_MARQUEE)
|
||||||
|
{
|
||||||
SS.GenerateAll();
|
SS.GenerateAll();
|
||||||
}
|
}
|
||||||
havePainted = false;
|
havePainted = false;
|
||||||
|
@ -950,6 +980,13 @@ void GraphicsWindow::MouseLeftUp(double mx, double my) {
|
||||||
case DRAGGING_NORMAL:
|
case DRAGGING_NORMAL:
|
||||||
case DRAGGING_RADIUS:
|
case DRAGGING_RADIUS:
|
||||||
ClearPending();
|
ClearPending();
|
||||||
|
InvalidateGraphics();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRAGGING_MARQUEE:
|
||||||
|
SelectByMarquee();
|
||||||
|
ClearPending();
|
||||||
|
InvalidateGraphics();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -216,7 +216,7 @@ void TextWindow::Show(void) {
|
||||||
Printf(false, "%s", SS.GW.pending.description);
|
Printf(false, "%s", SS.GW.pending.description);
|
||||||
Printf(true, "%Fl%f%Ll(cancel operation)%E",
|
Printf(true, "%Fl%f%Ll(cancel operation)%E",
|
||||||
&TextWindow::ScreenUnselectAll);
|
&TextWindow::ScreenUnselectAll);
|
||||||
} else if(gs.n > 0 || gs.stylables > 0) {
|
} else if(gs.n > 0 || gs.constraints > 0) {
|
||||||
if(edit.meaning != EDIT_TTF_TEXT) HideTextEditControl();
|
if(edit.meaning != EDIT_TTF_TEXT) HideTextEditControl();
|
||||||
ShowHeader(false);
|
ShowHeader(false);
|
||||||
DescribeSelection();
|
DescribeSelection();
|
||||||
|
@ -519,7 +519,7 @@ void TextWindow::DescribeSelection(void) {
|
||||||
double d = (p1.Minus(p0)).Dot(n0);
|
double d = (p1.Minus(p0)).Dot(n0);
|
||||||
Printf(true, " distance = %Fi%s", SS.MmToString(d));
|
Printf(true, " distance = %Fi%s", SS.MmToString(d));
|
||||||
}
|
}
|
||||||
} else if(gs.n == 0) {
|
} else if(gs.n == 0 && gs.stylables > 0) {
|
||||||
Printf(true, "%FtSELECTED:%E comment text");
|
Printf(true, "%FtSELECTED:%E comment text");
|
||||||
} else {
|
} else {
|
||||||
int n = SS.GW.selection.n;
|
int n = SS.GW.selection.n;
|
||||||
|
@ -527,7 +527,7 @@ void TextWindow::DescribeSelection(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(shown.screen == SCREEN_STYLE_INFO &&
|
if(shown.screen == SCREEN_STYLE_INFO &&
|
||||||
shown.style.v >= Style::FIRST_CUSTOM)
|
shown.style.v >= Style::FIRST_CUSTOM && gs.stylables > 0)
|
||||||
{
|
{
|
||||||
// If we are showing a screen for a particular style, then offer the
|
// If we are showing a screen for a particular style, then offer the
|
||||||
// option to assign our selected entities to that style.
|
// option to assign our selected entities to that style.
|
||||||
|
|
4
ui.h
4
ui.h
|
@ -327,6 +327,7 @@ public:
|
||||||
Vector projUp;
|
Vector projUp;
|
||||||
Point2d mouse;
|
Point2d mouse;
|
||||||
Point2d mouseOnButtonDown;
|
Point2d mouseOnButtonDown;
|
||||||
|
Vector marqueePoint;
|
||||||
bool startedMoving;
|
bool startedMoving;
|
||||||
} orig;
|
} orig;
|
||||||
|
|
||||||
|
@ -344,6 +345,7 @@ public:
|
||||||
Point2d ProjectPoint(Vector p);
|
Point2d ProjectPoint(Vector p);
|
||||||
Vector ProjectPoint3(Vector p);
|
Vector ProjectPoint3(Vector p);
|
||||||
Vector ProjectPoint4(Vector p, double *w);
|
Vector ProjectPoint4(Vector p, double *w);
|
||||||
|
Vector UnProjectPoint(Point2d p);
|
||||||
void AnimateOnto(Quaternion quatf, Vector offsetf);
|
void AnimateOnto(Quaternion quatf, Vector offsetf);
|
||||||
void AnimateOntoWorkplane(void);
|
void AnimateOntoWorkplane(void);
|
||||||
Vector VectorFromProjs(Vector rightUpForward);
|
Vector VectorFromProjs(Vector rightUpForward);
|
||||||
|
@ -372,6 +374,7 @@ public:
|
||||||
static const int DRAGGING_RADIUS = 0x0f000006;
|
static const int DRAGGING_RADIUS = 0x0f000006;
|
||||||
static const int DRAGGING_NORMAL = 0x0f000007;
|
static const int DRAGGING_NORMAL = 0x0f000007;
|
||||||
static const int DRAGGING_NEW_RADIUS = 0x0f000008;
|
static const int DRAGGING_NEW_RADIUS = 0x0f000008;
|
||||||
|
static const int DRAGGING_MARQUEE = 0x0f000009;
|
||||||
struct {
|
struct {
|
||||||
int operation;
|
int operation;
|
||||||
|
|
||||||
|
@ -452,6 +455,7 @@ public:
|
||||||
void GroupSelection(void);
|
void GroupSelection(void);
|
||||||
void ToggleSelectionStateOf(hEntity he);
|
void ToggleSelectionStateOf(hEntity he);
|
||||||
void ToggleSelectionStateOf(Selection *s);
|
void ToggleSelectionStateOf(Selection *s);
|
||||||
|
void SelectByMarquee(void);
|
||||||
void ClearSuper(void);
|
void ClearSuper(void);
|
||||||
|
|
||||||
static const int CMNU_UNSELECT_ALL = 0x100;
|
static const int CMNU_UNSELECT_ALL = 0x100;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
|
||||||
marquee selection
|
|
||||||
copy and paste
|
copy and paste
|
||||||
background image
|
background image
|
||||||
associative entities from solid model, as a special group
|
associative entities from solid model, as a special group
|
||||||
|
|
Loading…
Reference in New Issue