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=872pull/4/head
parent
e99eedd7a3
commit
bda2835e9f
14
src/draw.cpp
14
src/draw.cpp
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
8
src/ui.h
8
src/ui.h
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue