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;
|
double d, dmin = 1e12;
|
||||||
Selection s = {};
|
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
|
// Always do the entities; we might be dragging something that should
|
||||||
// be auto-constrained, and we need the hover for that.
|
// be auto-constrained, and we need the hover for that.
|
||||||
for(i = 0; i < SK.entity.n; i++) {
|
for(i = 0; i < SK.entity.n; i++) {
|
||||||
|
|
|
@ -152,35 +152,38 @@ SEdgeList *Entity::GetOrGenerateEdges() {
|
||||||
return &edges;
|
return &edges;
|
||||||
}
|
}
|
||||||
|
|
||||||
BBox Entity::GetScreenBBox(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 normals, workplanes, etc.
|
||||||
*hasBBox = (IsPoint() || sbl->l.n > 0);
|
*hasBBox = (IsPoint() || sbl->l.n > 0);
|
||||||
if(!*hasBBox) return {};
|
if(!*hasBBox) return {};
|
||||||
|
|
||||||
BBox result = {};
|
if(screenBBoxValid)
|
||||||
|
return screenBBox;
|
||||||
|
|
||||||
if(IsPoint()) {
|
if(IsPoint()) {
|
||||||
Vector proj = SS.GW.ProjectPoint3(PointGetNum());
|
Vector proj = SS.GW.ProjectPoint3(PointGetNum());
|
||||||
result = BBox::From(proj, proj);
|
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]);
|
||||||
result = BBox::From(first, first);
|
screenBBox = BBox::From(first, first);
|
||||||
for(int i = 0; i < sbl->l.n; i++) {
|
for(int i = 0; i < sbl->l.n; i++) {
|
||||||
SBezier *sb = &sbl->l.elem[i];
|
SBezier *sb = &sbl->l.elem[i];
|
||||||
for(int i = 0; i <= sb->deg; 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();
|
} else oops();
|
||||||
|
|
||||||
// Enlarge the bounding box to consider selection radius.
|
// Enlarge the bounding box to consider selection radius.
|
||||||
result.minp.x -= SELECTION_RADIUS;
|
screenBBox.minp.x -= SELECTION_RADIUS;
|
||||||
result.minp.y -= SELECTION_RADIUS;
|
screenBBox.minp.y -= SELECTION_RADIUS;
|
||||||
result.maxp.x += SELECTION_RADIUS;
|
screenBBox.maxp.x += SELECTION_RADIUS;
|
||||||
result.maxp.y += SELECTION_RADIUS;
|
screenBBox.maxp.y += SELECTION_RADIUS;
|
||||||
|
|
||||||
return result;
|
screenBBoxValid = true;
|
||||||
|
return screenBBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
double Entity::GetDistance(Point2d mp) {
|
double Entity::GetDistance(Point2d mp) {
|
||||||
|
@ -508,7 +511,7 @@ void Entity::DrawOrGetDistance(void) {
|
||||||
// whether the pointer is inside its bounding box first.
|
// whether the pointer is inside its bounding box first.
|
||||||
if(!dogd.drawing) {
|
if(!dogd.drawing) {
|
||||||
bool hasBBox;
|
bool hasBBox;
|
||||||
BBox box = GetScreenBBox(&hasBBox);
|
BBox box = GetOrGenerateScreenBBox(&hasBBox);
|
||||||
if(hasBBox && !box.Contains(dogd.mp))
|
if(hasBBox && !box.Contains(dogd.mp))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -455,7 +455,8 @@ public:
|
||||||
// POD members with indeterminate value.
|
// POD members with indeterminate value.
|
||||||
Entity() : EntityBase({}), forceHidden(), actPoint(), actNormal(),
|
Entity() : EntityBase({}), forceHidden(), actPoint(), actNormal(),
|
||||||
actDistance(), actVisible(), style(), construction(),
|
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
|
// An imported entity that was hidden in the source file ends up hidden
|
||||||
// here too.
|
// here too.
|
||||||
|
@ -476,6 +477,8 @@ public:
|
||||||
SBezierList beziers;
|
SBezierList beziers;
|
||||||
SEdgeList edges;
|
SEdgeList edges;
|
||||||
double edgesChordTol;
|
double edgesChordTol;
|
||||||
|
BBox screenBBox;
|
||||||
|
bool screenBBoxValid;
|
||||||
|
|
||||||
// Routines to draw and hit-test the representation of the entity
|
// Routines to draw and hit-test the representation of the entity
|
||||||
// on-screen.
|
// on-screen.
|
||||||
|
@ -509,7 +512,7 @@ public:
|
||||||
|
|
||||||
SBezierList *GetOrGenerateBezierCurves();
|
SBezierList *GetOrGenerateBezierCurves();
|
||||||
SEdgeList *GetOrGenerateEdges();
|
SEdgeList *GetOrGenerateEdges();
|
||||||
BBox GetScreenBBox(bool *hasBBox);
|
BBox GetOrGenerateScreenBBox(bool *hasBBox);
|
||||||
|
|
||||||
void Clear() {
|
void Clear() {
|
||||||
beziers.l.Clear();
|
beziers.l.Clear();
|
||||||
|
|
8
src/ui.h
8
src/ui.h
|
@ -487,6 +487,14 @@ public:
|
||||||
Vector marqueePoint;
|
Vector marqueePoint;
|
||||||
bool startedMoving;
|
bool startedMoving;
|
||||||
} orig;
|
} 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.
|
// Most recent mouse position, updated every time the mouse moves.
|
||||||
Point2d currentMousePosition;
|
Point2d currentMousePosition;
|
||||||
|
|
Loading…
Reference in New Issue