From fd50406d67269875ee6ed13c9d0349ff0ff90121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Chlumsk=C3=BD?= Date: Fri, 6 Mar 2020 17:24:10 +0100 Subject: [PATCH] Shape bounds refactor --- core/Contour.cpp | 22 ++++++++++++---------- core/Contour.h | 4 ++-- core/Shape.cpp | 21 +++++++++++++++++---- core/Shape.h | 10 ++++++++-- core/edge-segments.cpp | 6 +++--- core/edge-segments.h | 8 ++++---- main.cpp | 9 ++------- 7 files changed, 48 insertions(+), 32 deletions(-) diff --git a/core/Contour.cpp b/core/Contour.cpp index 38a0e0af..ae0a7c39 100644 --- a/core/Contour.cpp +++ b/core/Contour.cpp @@ -24,30 +24,32 @@ EdgeHolder & Contour::addEdge() { return edges.back(); } -static void pointBounds(Point2 p, double &l, double &b, double &r, double &t) { +static void boundPoint(double &l, double &b, double &r, double &t, Point2 p) { if (p.x < l) l = p.x; if (p.y < b) b = p.y; if (p.x > r) r = p.x; if (p.y > t) t = p.y; } -void Contour::bounds(double &l, double &b, double &r, double &t) const { +void Contour::bound(double &l, double &b, double &r, double &t) const { for (std::vector::const_iterator edge = edges.begin(); edge != edges.end(); ++edge) - (*edge)->bounds(l, b, r, t); + (*edge)->bound(l, b, r, t); } -void Contour::miterBounds(double &l, double &b, double &r, double &t, double border, double miterLimit) const { +void Contour::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const { if (edges.empty()) return; Vector2 prevDir = edges.back()->direction(1).normalize(true); for (std::vector::const_iterator edge = edges.begin(); edge != edges.end(); ++edge) { Vector2 dir = -(*edge)->direction(0).normalize(true); - double miterLength = miterLimit; - double q = .5*(1-dotProduct(prevDir, dir)); - if (q > 0) - miterLength = min(1/sqrt(q), miterLimit); - Point2 miter = (*edge)->point(0)+border*miterLength*(prevDir+dir).normalize(true); - pointBounds(miter, l, b, r, t); + if (polarity*crossProduct(prevDir, dir) >= 0) { + double miterLength = miterLimit; + double q = .5*(1-dotProduct(prevDir, dir)); + if (q > 0) + miterLength = min(1/sqrt(q), miterLimit); + Point2 miter = (*edge)->point(0)+border*miterLength*(prevDir+dir).normalize(true); + boundPoint(l, b, r, t, miter); + } prevDir = (*edge)->direction(1).normalize(true); } } diff --git a/core/Contour.h b/core/Contour.h index 5d818fdc..c0b82b41 100644 --- a/core/Contour.h +++ b/core/Contour.h @@ -21,9 +21,9 @@ class Contour { /// Creates a new edge in the contour and returns its reference. EdgeHolder & addEdge(); /// Adjusts the bounding box to fit the contour. - void bounds(double &l, double &b, double &r, double &t) const; + void bound(double &l, double &b, double &r, double &t) const; /// Adjusts the bounding box to fit the contour border's mitered corners. - void miterBounds(double &l, double &b, double &r, double &t, double border, double miterLimit) const; + void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const; /// Computes the winding of the contour. Returns 1 if positive, -1 if negative. int winding() const; diff --git a/core/Shape.cpp b/core/Shape.cpp index 6bd2ba80..07f3ade6 100644 --- a/core/Shape.cpp +++ b/core/Shape.cpp @@ -48,14 +48,27 @@ void Shape::normalize() { } } -void Shape::bounds(double &l, double &b, double &r, double &t) const { +void Shape::bound(double &l, double &b, double &r, double &t) const { for (std::vector::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) - contour->bounds(l, b, r, t); + contour->bound(l, b, r, t); } -void Shape::miterBounds(double &l, double &b, double &r, double &t, double border, double miterLimit) const { +void Shape::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const { for (std::vector::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) - contour->miterBounds(l, b, r, t, border, miterLimit); + contour->boundMiters(l, b, r, t, border, miterLimit, polarity); +} + +Shape::Bounds Shape::getBounds(double border, double miterLimit, int polarity) const { + static const double LARGE_VALUE = 1e240; + Shape::Bounds bounds = { +LARGE_VALUE, +LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE }; + bound(bounds.l, bounds.b, bounds.r, bounds.t); + if (border > 0) { + bounds.l -= border, bounds.b -= border; + bounds.r += border, bounds.t += border; + if (miterLimit > 0) + boundMiters(bounds.l, bounds.b, bounds.r, bounds.t, border, miterLimit, polarity); + } + return bounds; } void Shape::scanline(Scanline &line, double y) const { diff --git a/core/Shape.h b/core/Shape.h index 1806e7aa..4381382c 100644 --- a/core/Shape.h +++ b/core/Shape.h @@ -11,6 +11,10 @@ namespace msdfgen { class Shape { public: + struct Bounds { + double l, b, r, t; + }; + /// The list of contours the shape consists of. std::vector contours; /// Specifies whether the shape uses bottom-to-top (false) or top-to-bottom (true) Y coordinates. @@ -29,9 +33,11 @@ class Shape { /// Performs basic checks to determine if the object represents a valid shape. bool validate() const; /// Adjusts the bounding box to fit the shape. - void bounds(double &l, double &b, double &r, double &t) const; + void bound(double &l, double &b, double &r, double &t) const; /// Adjusts the bounding box to fit the shape border's mitered corners. - void miterBounds(double &l, double &b, double &r, double &t, double border, double miterLimit) const; + void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const; + /// Computes the minimum bounding box that fits the shape, optionally with a (mitered) border. + Bounds getBounds(double border = 0, double miterLimit = 0, int polarity = 0) const; /// Outputs the scanline that intersects the shape at y. void scanline(Scanline &line, double y) const; diff --git a/core/edge-segments.cpp b/core/edge-segments.cpp index 4c369bf6..c57d783f 100644 --- a/core/edge-segments.cpp +++ b/core/edge-segments.cpp @@ -325,12 +325,12 @@ static void pointBounds(Point2 p, double &l, double &b, double &r, double &t) { if (p.y > t) t = p.y; } -void LinearSegment::bounds(double &l, double &b, double &r, double &t) const { +void LinearSegment::bound(double &l, double &b, double &r, double &t) const { pointBounds(p[0], l, b, r, t); pointBounds(p[1], l, b, r, t); } -void QuadraticSegment::bounds(double &l, double &b, double &r, double &t) const { +void QuadraticSegment::bound(double &l, double &b, double &r, double &t) const { pointBounds(p[0], l, b, r, t); pointBounds(p[2], l, b, r, t); Vector2 bot = (p[1]-p[0])-(p[2]-p[1]); @@ -346,7 +346,7 @@ void QuadraticSegment::bounds(double &l, double &b, double &r, double &t) const } } -void CubicSegment::bounds(double &l, double &b, double &r, double &t) const { +void CubicSegment::bound(double &l, double &b, double &r, double &t) const { pointBounds(p[0], l, b, r, t); pointBounds(p[3], l, b, r, t); Vector2 a0 = p[1]-p[0]; diff --git a/core/edge-segments.h b/core/edge-segments.h index 1e9572b2..539ce715 100644 --- a/core/edge-segments.h +++ b/core/edge-segments.h @@ -32,7 +32,7 @@ class EdgeSegment { /// Outputs a list of (at most three) intersections (their X coordinates) with an infinite horizontal scanline at y and returns how many there are. virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0; /// Adjusts the bounding box to fit the edge segment. - virtual void bounds(double &l, double &b, double &r, double &t) const = 0; + virtual void bound(double &l, double &b, double &r, double &t) const = 0; /// Moves the start point of the edge segment. virtual void moveStartPoint(Point2 to) = 0; @@ -55,7 +55,7 @@ class LinearSegment : public EdgeSegment { Vector2 direction(double param) const; SignedDistance signedDistance(Point2 origin, double ¶m) const; int scanlineIntersections(double x[3], int dy[3], double y) const; - void bounds(double &l, double &b, double &r, double &t) const; + void bound(double &l, double &b, double &r, double &t) const; void moveStartPoint(Point2 to); void moveEndPoint(Point2 to); @@ -75,7 +75,7 @@ class QuadraticSegment : public EdgeSegment { Vector2 direction(double param) const; SignedDistance signedDistance(Point2 origin, double ¶m) const; int scanlineIntersections(double x[3], int dy[3], double y) const; - void bounds(double &l, double &b, double &r, double &t) const; + void bound(double &l, double &b, double &r, double &t) const; void moveStartPoint(Point2 to); void moveEndPoint(Point2 to); @@ -95,7 +95,7 @@ class CubicSegment : public EdgeSegment { Vector2 direction(double param) const; SignedDistance signedDistance(Point2 origin, double ¶m) const; int scanlineIntersections(double x[3], int dy[3], double y) const; - void bounds(double &l, double &b, double &r, double &t) const; + void bound(double &l, double &b, double &r, double &t) const; void moveStartPoint(Point2 to); void moveEndPoint(Point2 to); diff --git a/main.cpp b/main.cpp index 2b210627..ed672baa 100644 --- a/main.cpp +++ b/main.cpp @@ -20,7 +20,6 @@ #pragma warning(disable:4996) #endif -#define LARGE_VALUE 1e240 #define SDF_ERROR_ESTIMATE_PRECISION 19 using namespace msdfgen; @@ -730,13 +729,9 @@ int main(int argc, const char * const *argv) { shape.inverseYAxis = !shape.inverseYAxis; double avgScale = .5*(scale.x+scale.y); - struct { - double l, b, r, t; - } bounds = { - LARGE_VALUE, LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE - }; + Shape::Bounds bounds = { }; if (autoFrame || mode == METRICS || printMetrics || orientation == GUESS) - shape.bounds(bounds.l, bounds.b, bounds.r, bounds.t); + bounds = shape.getBounds(); // Auto-frame if (autoFrame) {