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;
|
||||
}
|
||||
}
|
||||
// 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();
|
||||
if(wasSelected) return;
|
||||
|
||||
|
@ -141,6 +159,61 @@ void GraphicsWindow::ToggleSelectionStateOf(Selection *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
|
||||
// constraints separately, counts of certain types of entities (circles,
|
||||
|
@ -307,12 +380,28 @@ Vector GraphicsWindow::ProjectPoint4(Vector p, double *w) {
|
|||
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) {
|
||||
Vector norm = projRight.Cross(projUp);
|
||||
projUp = norm.Cross(projRight);
|
||||
|
||||
projUp = projUp.ScaledBy(1/projUp.Magnitude());
|
||||
projRight = projRight.ScaledBy(1/projRight.Magnitude());
|
||||
projUp = projUp.WithMagnitude(1);
|
||||
projRight = projRight.WithMagnitude(1);
|
||||
}
|
||||
|
||||
Vector GraphicsWindow::VectorFromProjs(Vector rightUpForward) {
|
||||
|
@ -557,6 +646,37 @@ nogrid:;
|
|||
|
||||
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.
|
||||
if(SS.showToolbar) {
|
||||
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();
|
||||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
Point2d mp = { x, y };
|
||||
Point2d mp = Point2d::From(x, y);
|
||||
|
||||
if(rightDown && orig.mouse.DistanceTo(mp) < 5 && !orig.startedMoving) {
|
||||
// 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
|
||||
// start dragging something.
|
||||
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);
|
||||
if(e->type == Entity::CIRCLE) {
|
||||
if(e->type == Entity::CIRCLE && selection.n <= 1) {
|
||||
// Drag the radius.
|
||||
ClearSelection();
|
||||
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
|
||||
// the drag changes anything.
|
||||
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 {
|
||||
// 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;
|
||||
}
|
||||
|
||||
case DRAGGING_MARQUEE:
|
||||
orig.mouse = mp;
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
if(pending.operation != 0 && pending.operation != DRAGGING_CONSTRAINT) {
|
||||
|
||||
if(pending.operation != 0 &&
|
||||
pending.operation != DRAGGING_CONSTRAINT &&
|
||||
pending.operation != DRAGGING_MARQUEE)
|
||||
{
|
||||
SS.GenerateAll();
|
||||
}
|
||||
havePainted = false;
|
||||
|
@ -950,6 +980,13 @@ void GraphicsWindow::MouseLeftUp(double mx, double my) {
|
|||
case DRAGGING_NORMAL:
|
||||
case DRAGGING_RADIUS:
|
||||
ClearPending();
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
|
||||
case DRAGGING_MARQUEE:
|
||||
SelectByMarquee();
|
||||
ClearPending();
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -216,7 +216,7 @@ void TextWindow::Show(void) {
|
|||
Printf(false, "%s", SS.GW.pending.description);
|
||||
Printf(true, "%Fl%f%Ll(cancel operation)%E",
|
||||
&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();
|
||||
ShowHeader(false);
|
||||
DescribeSelection();
|
||||
|
@ -519,7 +519,7 @@ void TextWindow::DescribeSelection(void) {
|
|||
double d = (p1.Minus(p0)).Dot(n0);
|
||||
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");
|
||||
} else {
|
||||
int n = SS.GW.selection.n;
|
||||
|
@ -527,7 +527,7 @@ void TextWindow::DescribeSelection(void) {
|
|||
}
|
||||
|
||||
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
|
||||
// option to assign our selected entities to that style.
|
||||
|
|
4
ui.h
4
ui.h
|
@ -327,6 +327,7 @@ public:
|
|||
Vector projUp;
|
||||
Point2d mouse;
|
||||
Point2d mouseOnButtonDown;
|
||||
Vector marqueePoint;
|
||||
bool startedMoving;
|
||||
} orig;
|
||||
|
||||
|
@ -344,6 +345,7 @@ public:
|
|||
Point2d ProjectPoint(Vector p);
|
||||
Vector ProjectPoint3(Vector p);
|
||||
Vector ProjectPoint4(Vector p, double *w);
|
||||
Vector UnProjectPoint(Point2d p);
|
||||
void AnimateOnto(Quaternion quatf, Vector offsetf);
|
||||
void AnimateOntoWorkplane(void);
|
||||
Vector VectorFromProjs(Vector rightUpForward);
|
||||
|
@ -372,6 +374,7 @@ public:
|
|||
static const int DRAGGING_RADIUS = 0x0f000006;
|
||||
static const int DRAGGING_NORMAL = 0x0f000007;
|
||||
static const int DRAGGING_NEW_RADIUS = 0x0f000008;
|
||||
static const int DRAGGING_MARQUEE = 0x0f000009;
|
||||
struct {
|
||||
int operation;
|
||||
|
||||
|
@ -452,6 +455,7 @@ public:
|
|||
void GroupSelection(void);
|
||||
void ToggleSelectionStateOf(hEntity he);
|
||||
void ToggleSelectionStateOf(Selection *s);
|
||||
void SelectByMarquee(void);
|
||||
void ClearSuper(void);
|
||||
|
||||
static const int CMNU_UNSELECT_ALL = 0x100;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
marquee selection
|
||||
copy and paste
|
||||
background image
|
||||
associative entities from solid model, as a special group
|
||||
|
|
Loading…
Reference in New Issue