From 68c4d6f704bee45c8ac96953d666ac28092e81e2 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 24 May 2016 03:44:38 +0000 Subject: [PATCH] Use entity bounding boxes in SelectByMarquee. Also, fix an insidious typo in BBox::GetOrigin that made BBox::Overlap return nonsensical results. --- src/draw.cpp | 49 ++++++++-------------------------------------- src/drawentity.cpp | 17 +++++++--------- src/dsc.h | 2 +- src/util.cpp | 10 ++++++---- 4 files changed, 22 insertions(+), 56 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index 8c9fb2d3..bc6c26cf 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -187,16 +187,12 @@ void GraphicsWindow::MakeSelected(Selection *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. +// Select everything that lies within the marquee view-aligned rectangle. //----------------------------------------------------------------------------- void GraphicsWindow::SelectByMarquee() { - 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); + Point2d marqueePoint = ProjectPoint(orig.marqueePoint); + BBox marqueeBBox = BBox::From(Vector::From(marqueePoint.x, marqueePoint.y, -1), + Vector::From(orig.mouse.x, orig.mouse.y, 1)); Entity *e; for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { @@ -204,39 +200,10 @@ void GraphicsWindow::SelectByMarquee() { if(e->IsFace() || e->IsDistance()) continue; if(!e->IsVisible()) 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) - { - 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(); + bool entityHasBBox; + BBox entityBBox = e->GetOrGenerateScreenBBox(&entityHasBBox); + if(entityHasBBox && entityBBox.Overlaps(marqueeBBox)) { + MakeSelected(e->h); } } } diff --git a/src/drawentity.cpp b/src/drawentity.cpp index 6de97ce8..bad9244b 100644 --- a/src/drawentity.cpp +++ b/src/drawentity.cpp @@ -160,8 +160,8 @@ SEdgeList *Entity::GetOrGenerateEdges() { BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) { SBezierList *sbl = GetOrGenerateBezierCurves(); - // We don't bother with bounding boxes for normals, workplanes, etc. - *hasBBox = (IsPoint() || sbl->l.n > 0); + // We don't bother with bounding boxes for workplanes, etc. + *hasBBox = (IsPoint() || IsNormal() || sbl->l.n > 0); if(!*hasBBox) return {}; if(screenBBoxValid) @@ -170,6 +170,9 @@ BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) { if(IsPoint()) { Vector proj = SS.GW.ProjectPoint3(PointGetNum()); 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) { Vector first = SS.GW.ProjectPoint3(sbl->l.elem[0].ctrl[0]); 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"); - // 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; return screenBBox; } @@ -521,10 +518,10 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) const { void Entity::DrawOrGetDistance() { // If we're about to perform hit testing on an entity, consider // whether the pointer is inside its bounding box first. - if(!dogd.drawing) { + if(!dogd.drawing && !IsNormal()) { bool hasBBox; BBox box = GetOrGenerateScreenBBox(&hasBBox); - if(hasBBox && !box.Contains(dogd.mp)) + if(hasBBox && !box.Contains(dogd.mp, SELECTION_RADIUS)) return; } diff --git a/src/dsc.h b/src/dsc.h index 0ab01fd5..74fa205b 100644 --- a/src/dsc.h +++ b/src/dsc.h @@ -528,7 +528,7 @@ public: void Include(const Vector &v, double r = 0.0); bool Overlaps(const BBox &b1) const; - bool Contains(const Point2d &p) const; + bool Contains(const Point2d &p, double r = 0.0) const; }; #endif diff --git a/src/util.cpp b/src/util.cpp index dec49d58..ab649ff7 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1036,7 +1036,7 @@ BBox BBox::From(const Vector &p0, const Vector &p1) { 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); } 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 { - Vector t = b1.GetOrigin().Minus(GetOrigin()); Vector e = b1.GetExtents().Plus(GetExtents()); return fabs(t.x) < e.x && fabs(t.y) < e.y && fabs(t.z) < e.z; } -bool BBox::Contains(const Point2d &p) const { - return p.x >= minp.x && p.y >= minp.y && p.x <= maxp.x && p.y <= maxp.y; +bool BBox::Contains(const Point2d &p, double r) const { + return p.x >= (minp.x - r) && + p.y >= (minp.y - r) && + p.x <= (maxp.x + r) && + p.y <= (maxp.y + r); }