Use entity bounding boxes in SelectByMarquee.
Also, fix an insidious typo in BBox::GetOrigin that made BBox::Overlap return nonsensical results.pull/10/head
parent
e2916e3e4a
commit
68c4d6f704
47
src/draw.cpp
47
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,40 +200,11 @@ 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)
|
||||
{
|
||||
bool entityHasBBox;
|
||||
BBox entityBBox = e->GetOrGenerateScreenBBox(&entityHasBBox);
|
||||
if(entityHasBBox && entityBBox.Overlaps(marqueeBBox)) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
10
src/util.cpp
10
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue