Use entity bounding boxes in SelectByMarquee.

Also, fix an insidious typo in BBox::GetOrigin that made BBox::Overlap
return nonsensical results.
pull/10/head
whitequark 2016-05-24 03:44:38 +00:00
parent e2916e3e4a
commit 68c4d6f704
4 changed files with 22 additions and 56 deletions

View File

@ -187,16 +187,12 @@ void GraphicsWindow::MakeSelected(Selection *stog) {
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Select everything that lies within the marquee view-aligned rectangle. For // Select everything that lies within the marquee view-aligned rectangle.
// 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 GraphicsWindow::SelectByMarquee() {
Point2d begin = ProjectPoint(orig.marqueePoint); Point2d marqueePoint = ProjectPoint(orig.marqueePoint);
double xmin = min(orig.mouse.x, begin.x), BBox marqueeBBox = BBox::From(Vector::From(marqueePoint.x, marqueePoint.y, -1),
xmax = max(orig.mouse.x, begin.x), Vector::From(orig.mouse.x, orig.mouse.y, 1));
ymin = min(orig.mouse.y, begin.y),
ymax = max(orig.mouse.y, begin.y);
Entity *e; Entity *e;
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
@ -204,40 +200,11 @@ void GraphicsWindow::SelectByMarquee() {
if(e->IsFace() || e->IsDistance()) continue; if(e->IsFace() || e->IsDistance()) continue;
if(!e->IsVisible()) continue; if(!e->IsVisible()) continue;
if(e->IsPoint() || e->IsNormal()) { bool entityHasBBox;
Vector p = e->IsPoint() ? e->PointGetNum() : BBox entityBBox = e->GetOrGenerateScreenBBox(&entityHasBBox);
SK.GetEntity(e->point[0])->PointGetNum(); if(entityHasBBox && entityBBox.Overlaps(marqueeBBox)) {
Point2d pp = ProjectPoint(p);
if(pp.x >= xmin && pp.x <= xmax &&
pp.y >= ymin && pp.y <= ymax)
{
MakeSelected(e->h); MakeSelected(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 = {};
e->GenerateEdges(&sel, true);
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))
{
MakeSelected(e->h);
break;
}
}
sel.Clear();
}
} }
} }

View File

@ -160,8 +160,8 @@ SEdgeList *Entity::GetOrGenerateEdges() {
BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) { BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) {
SBezierList *sbl = GetOrGenerateBezierCurves(); SBezierList *sbl = GetOrGenerateBezierCurves();
// We don't bother with bounding boxes for normals, workplanes, etc. // We don't bother with bounding boxes for workplanes, etc.
*hasBBox = (IsPoint() || sbl->l.n > 0); *hasBBox = (IsPoint() || IsNormal() || sbl->l.n > 0);
if(!*hasBBox) return {}; if(!*hasBBox) return {};
if(screenBBoxValid) if(screenBBoxValid)
@ -170,6 +170,9 @@ BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) {
if(IsPoint()) { if(IsPoint()) {
Vector proj = SS.GW.ProjectPoint3(PointGetNum()); Vector proj = SS.GW.ProjectPoint3(PointGetNum());
screenBBox = BBox::From(proj, proj); screenBBox = BBox::From(proj, proj);
} else if(IsNormal()) {
Vector proj = SK.GetEntity(point[0])->PointGetNum();
screenBBox = BBox::From(proj, proj);
} else if(sbl->l.n > 0) { } else if(sbl->l.n > 0) {
Vector first = SS.GW.ProjectPoint3(sbl->l.elem[0].ctrl[0]); Vector first = SS.GW.ProjectPoint3(sbl->l.elem[0].ctrl[0]);
screenBBox = BBox::From(first, first); screenBBox = BBox::From(first, first);
@ -181,12 +184,6 @@ BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) {
} }
} else ssassert(false, "Expected entity to be a point or have beziers"); } else ssassert(false, "Expected entity to be a point or have beziers");
// Enlarge the bounding box to consider selection radius.
screenBBox.minp.x -= SELECTION_RADIUS;
screenBBox.minp.y -= SELECTION_RADIUS;
screenBBox.maxp.x += SELECTION_RADIUS;
screenBBox.maxp.y += SELECTION_RADIUS;
screenBBoxValid = true; screenBBoxValid = true;
return screenBBox; return screenBBox;
} }
@ -521,10 +518,10 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) const {
void Entity::DrawOrGetDistance() { void Entity::DrawOrGetDistance() {
// If we're about to perform hit testing on an entity, consider // If we're about to perform hit testing on an entity, consider
// whether the pointer is inside its bounding box first. // whether the pointer is inside its bounding box first.
if(!dogd.drawing) { if(!dogd.drawing && !IsNormal()) {
bool hasBBox; bool hasBBox;
BBox box = GetOrGenerateScreenBBox(&hasBBox); BBox box = GetOrGenerateScreenBBox(&hasBBox);
if(hasBBox && !box.Contains(dogd.mp)) if(hasBBox && !box.Contains(dogd.mp, SELECTION_RADIUS))
return; return;
} }

View File

@ -528,7 +528,7 @@ public:
void Include(const Vector &v, double r = 0.0); void Include(const Vector &v, double r = 0.0);
bool Overlaps(const BBox &b1) const; bool Overlaps(const BBox &b1) const;
bool Contains(const Point2d &p) const; bool Contains(const Point2d &p, double r = 0.0) const;
}; };
#endif #endif

View File

@ -1036,7 +1036,7 @@ BBox BBox::From(const Vector &p0, const Vector &p1) {
return bbox; return bbox;
} }
Vector BBox::GetOrigin() const { return minp.Plus(maxp.Minus(minp)).ScaledBy(0.5); } Vector BBox::GetOrigin() const { return minp.Plus(maxp.Minus(minp).ScaledBy(0.5)); }
Vector BBox::GetExtents() const { return maxp.Minus(minp).ScaledBy(0.5); } Vector BBox::GetExtents() const { return maxp.Minus(minp).ScaledBy(0.5); }
void BBox::Include(const Vector &v, double r) { void BBox::Include(const Vector &v, double r) {
@ -1050,13 +1050,15 @@ void BBox::Include(const Vector &v, double r) {
} }
bool BBox::Overlaps(const BBox &b1) const { bool BBox::Overlaps(const BBox &b1) const {
Vector t = b1.GetOrigin().Minus(GetOrigin()); Vector t = b1.GetOrigin().Minus(GetOrigin());
Vector e = b1.GetExtents().Plus(GetExtents()); Vector e = b1.GetExtents().Plus(GetExtents());
return fabs(t.x) < e.x && fabs(t.y) < e.y && fabs(t.z) < e.z; return fabs(t.x) < e.x && fabs(t.y) < e.y && fabs(t.z) < e.z;
} }
bool BBox::Contains(const Point2d &p) const { bool BBox::Contains(const Point2d &p, double r) const {
return p.x >= minp.x && p.y >= minp.y && p.x <= maxp.x && p.y <= maxp.y; return p.x >= (minp.x - r) &&
p.y >= (minp.y - r) &&
p.x <= (maxp.x + r) &&
p.y <= (maxp.y + r);
} }