From bda2835e9fb943f4752c7ab3a653426dbaab197b Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 5 Mar 2016 15:09:11 +0000 Subject: [PATCH] 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 --- src/draw.cpp | 14 ++++++++++++++ src/drawentity.cpp | 25 ++++++++++++++----------- src/sketch.h | 7 +++++-- src/ui.h | 8 ++++++++ 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/draw.cpp b/src/draw.cpp index 31ab9985..8bfda6e2 100644 --- a/src/draw.cpp +++ b/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++) { diff --git a/src/drawentity.cpp b/src/drawentity.cpp index 10a7c1d9..bbec8d2d 100644 --- a/src/drawentity.cpp +++ b/src/drawentity.cpp @@ -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; } diff --git a/src/sketch.h b/src/sketch.h index fe920743..3e53087f 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -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(); diff --git a/src/ui.h b/src/ui.h index 6cacaf25..5b3156ca 100644 --- a/src/ui.h +++ b/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;