Cache bounding boxes.

This results in massive performance improvements for hit testing.
Files with very large amounts of entities (e.g. [1]) inflict
a delay of several seconds between moving the pointer and
highlighting an entity in commit HEAD^^^, whereas in this commit
the delay is barely perceptible.

[1]: http://solvespace.com/forum.pl?action=viewthread&parent=872
pull/4/head
whitequark 2016-03-05 15:09:11 +00:00
parent e99eedd7a3
commit bda2835e9f
4 changed files with 41 additions and 13 deletions

View File

@ -318,6 +318,20 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
double d, dmin = 1e12;
Selection s = {};
// Did the view projection change? If so, invalidate bounding boxes.
if(!offset.EqualsExactly(cached.offset) ||
!projRight.EqualsExactly(cached.projRight) ||
!projUp.EqualsExactly(cached.projUp) ||
EXACT(scale != cached.scale)) {
cached.offset = offset;
cached.projRight = projRight;
cached.projUp = projUp;
cached.scale = scale;
for(Entity *e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
e->screenBBoxValid = false;
}
}
// Always do the entities; we might be dragging something that should
// be auto-constrained, and we need the hover for that.
for(i = 0; i < SK.entity.n; i++) {

View File

@ -152,35 +152,38 @@ SEdgeList *Entity::GetOrGenerateEdges() {
return &edges;
}
BBox Entity::GetScreenBBox(bool *hasBBox) {
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);
if(!*hasBBox) return {};
BBox result = {};
if(screenBBoxValid)
return screenBBox;
if(IsPoint()) {
Vector proj = SS.GW.ProjectPoint3(PointGetNum());
result = BBox::From(proj, proj);
screenBBox = BBox::From(proj, proj);
} else if(sbl->l.n > 0) {
Vector first = SS.GW.ProjectPoint3(sbl->l.elem[0].ctrl[0]);
result = BBox::From(first, first);
screenBBox = BBox::From(first, first);
for(int i = 0; i < sbl->l.n; i++) {
SBezier *sb = &sbl->l.elem[i];
for(int i = 0; i <= sb->deg; i++) {
result.Include(SS.GW.ProjectPoint3(sb->ctrl[i]));
screenBBox.Include(SS.GW.ProjectPoint3(sb->ctrl[i]));
}
}
} else oops();
// Enlarge the bounding box to consider selection radius.
result.minp.x -= SELECTION_RADIUS;
result.minp.y -= SELECTION_RADIUS;
result.maxp.x += SELECTION_RADIUS;
result.maxp.y += SELECTION_RADIUS;
screenBBox.minp.x -= SELECTION_RADIUS;
screenBBox.minp.y -= SELECTION_RADIUS;
screenBBox.maxp.x += SELECTION_RADIUS;
screenBBox.maxp.y += SELECTION_RADIUS;
return result;
screenBBoxValid = true;
return screenBBox;
}
double Entity::GetDistance(Point2d mp) {
@ -508,7 +511,7 @@ void Entity::DrawOrGetDistance(void) {
// whether the pointer is inside its bounding box first.
if(!dogd.drawing) {
bool hasBBox;
BBox box = GetScreenBBox(&hasBBox);
BBox box = GetOrGenerateScreenBBox(&hasBBox);
if(hasBBox && !box.Contains(dogd.mp))
return;
}

View File

@ -455,7 +455,8 @@ public:
// POD members with indeterminate value.
Entity() : EntityBase({}), forceHidden(), actPoint(), actNormal(),
actDistance(), actVisible(), style(), construction(),
dogd(), beziers(), edges(), edgesChordTol() {};
dogd(), beziers(), edges(), edgesChordTol(), screenBBox(),
screenBBoxValid() {};
// An imported entity that was hidden in the source file ends up hidden
// here too.
@ -476,6 +477,8 @@ public:
SBezierList beziers;
SEdgeList edges;
double edgesChordTol;
BBox screenBBox;
bool screenBBoxValid;
// Routines to draw and hit-test the representation of the entity
// on-screen.
@ -509,7 +512,7 @@ public:
SBezierList *GetOrGenerateBezierCurves();
SEdgeList *GetOrGenerateEdges();
BBox GetScreenBBox(bool *hasBBox);
BBox GetOrGenerateScreenBBox(bool *hasBBox);
void Clear() {
beziers.l.Clear();

View File

@ -487,6 +487,14 @@ public:
Vector marqueePoint;
bool startedMoving;
} orig;
// We need to detect when the projection is changed to invalidate
// caches for drawn items.
struct {
Vector offset;
Vector projRight;
Vector projUp;
double scale;
} cached;
// Most recent mouse position, updated every time the mouse moves.
Point2d currentMousePosition;