Pārlūkot izejas kodu

Improved edge segment construction

Chlumsky 1 gadu atpakaļ
vecāks
revīzija
2357140784

+ 0 - 10
core/EdgeHolder.cpp

@@ -9,16 +9,6 @@ void EdgeHolder::swap(EdgeHolder &a, EdgeHolder &b) {
     b.edgeSegment = tmp;
 }
 
-EdgeHolder::EdgeHolder() : edgeSegment(NULL) { }
-
-EdgeHolder::EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
-
-EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor) : edgeSegment(new LinearSegment(p0, p1, edgeColor)) { }
-
-EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : edgeSegment(new QuadraticSegment(p0, p1, p2, edgeColor)) { }
-
-EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : edgeSegment(new CubicSegment(p0, p1, p2, p3, edgeColor)) { }
-
 EdgeHolder::EdgeHolder(const EdgeHolder &orig) : edgeSegment(orig.edgeSegment ? orig.edgeSegment->clone() : NULL) { }
 
 #ifdef MSDFGEN_USE_CPP11

+ 5 - 5
core/EdgeHolder.h

@@ -12,11 +12,11 @@ public:
     /// Swaps the edges held by a and b.
     static void swap(EdgeHolder &a, EdgeHolder &b);
 
-    EdgeHolder();
-    EdgeHolder(EdgeSegment *segment);
-    EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
-    EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE);
-    EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE);
+    inline EdgeHolder() : edgeSegment() { }
+    inline EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
+    inline EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE) : edgeSegment(EdgeSegment::create(p0, p1, edgeColor)) { }
+    inline EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE) : edgeSegment(EdgeSegment::create(p0, p1, p2, edgeColor)) { }
+    inline EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE) : edgeSegment(EdgeSegment::create(p0, p1, p2, p3, edgeColor)) { }
     EdgeHolder(const EdgeHolder &orig);
 #ifdef MSDFGEN_USE_CPP11
     EdgeHolder(EdgeHolder &&orig);

+ 31 - 18
core/edge-segments.cpp

@@ -6,6 +6,25 @@
 
 namespace msdfgen {
 
+EdgeSegment *EdgeSegment::create(Point2 p0, Point2 p1, EdgeColor edgeColor) {
+    return new LinearSegment(p0, p1, edgeColor);
+}
+
+EdgeSegment *EdgeSegment::create(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) {
+    if (!crossProduct(p1-p0, p2-p1))
+        return new LinearSegment(p0, p2, edgeColor);
+    return new QuadraticSegment(p0, p1, p2, edgeColor);
+}
+
+EdgeSegment *EdgeSegment::create(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) {
+    Vector2 p12 = p2-p1;
+    if (!crossProduct(p1-p0, p12) && !crossProduct(p12, p3-p2))
+        return new LinearSegment(p0, p3, edgeColor);
+    if ((p12 = 1.5*p1-.5*p0) == 1.5*p2-.5*p3)
+        return new QuadraticSegment(p0, p12, p3, edgeColor);
+    return new CubicSegment(p0, p1, p2, p3, edgeColor);
+}
+
 void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const {
     if (param < 0) {
         Vector2 dir = direction(0).normalize();
@@ -38,18 +57,12 @@ LinearSegment::LinearSegment(Point2 p0, Point2 p1, EdgeColor edgeColor) : EdgeSe
 }
 
 QuadraticSegment::QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
-    if (p1 == p0 || p1 == p2)
-        p1 = 0.5*(p0+p2);
     p[0] = p0;
     p[1] = p1;
     p[2] = p2;
 }
 
 CubicSegment::CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
-    if ((p1 == p0 || p1 == p3) && (p2 == p0 || p2 == p3)) {
-        p1 = mix(p0, p3, 1/3.);
-        p2 = mix(p0, p3, 2/3.);
-    }
     p[0] = p0;
     p[1] = p1;
     p[2] = p2;
@@ -486,25 +499,25 @@ void CubicSegment::moveEndPoint(Point2 to) {
     p[3] = to;
 }
 
-void LinearSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
-    part1 = new LinearSegment(p[0], point(1/3.), color);
-    part2 = new LinearSegment(point(1/3.), point(2/3.), color);
-    part3 = new LinearSegment(point(2/3.), p[1], color);
+void LinearSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
+    part0 = new LinearSegment(p[0], point(1/3.), color);
+    part1 = new LinearSegment(point(1/3.), point(2/3.), color);
+    part2 = new LinearSegment(point(2/3.), p[1], color);
 }
 
-void QuadraticSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
-    part1 = new QuadraticSegment(p[0], mix(p[0], p[1], 1/3.), point(1/3.), color);
-    part2 = new QuadraticSegment(point(1/3.), mix(mix(p[0], p[1], 5/9.), mix(p[1], p[2], 4/9.), .5), point(2/3.), color);
-    part3 = new QuadraticSegment(point(2/3.), mix(p[1], p[2], 2/3.), p[2], color);
+void QuadraticSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
+    part0 = new QuadraticSegment(p[0], mix(p[0], p[1], 1/3.), point(1/3.), color);
+    part1 = new QuadraticSegment(point(1/3.), mix(mix(p[0], p[1], 5/9.), mix(p[1], p[2], 4/9.), .5), point(2/3.), color);
+    part2 = new QuadraticSegment(point(2/3.), mix(p[1], p[2], 2/3.), p[2], color);
 }
 
-void CubicSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
-    part1 = new CubicSegment(p[0], p[0] == p[1] ? p[0] : mix(p[0], p[1], 1/3.), mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), point(1/3.), color);
-    part2 = new CubicSegment(point(1/3.),
+void CubicSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
+    part0 = new CubicSegment(p[0], p[0] == p[1] ? p[0] : mix(p[0], p[1], 1/3.), mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), point(1/3.), color);
+    part1 = new CubicSegment(point(1/3.),
         mix(mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), mix(mix(p[1], p[2], 1/3.), mix(p[2], p[3], 1/3.), 1/3.), 2/3.),
         mix(mix(mix(p[0], p[1], 2/3.), mix(p[1], p[2], 2/3.), 2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), 1/3.),
         point(2/3.), color);
-    part3 = new CubicSegment(point(2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), p[2] == p[3] ? p[3] : mix(p[2], p[3], 2/3.), p[3], color);
+    part2 = new CubicSegment(point(2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), p[2] == p[3] ? p[3] : mix(p[2], p[3], 2/3.), p[3], color);
 }
 
 EdgeSegment *QuadraticSegment::convertToCubic() const {

+ 8 - 4
core/edge-segments.h

@@ -17,6 +17,10 @@ class EdgeSegment {
 public:
     EdgeColor color;
 
+    static EdgeSegment *create(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
+    static EdgeSegment *create(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE);
+    static EdgeSegment *create(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE);
+
     EdgeSegment(EdgeColor edgeColor = WHITE) : color(edgeColor) { }
     virtual ~EdgeSegment() { }
     /// Creates a copy of the edge segment.
@@ -47,7 +51,7 @@ public:
     /// Moves the end point of the edge segment.
     virtual void moveEndPoint(Point2 to) = 0;
     /// Splits the edge segments into thirds which together represent the original edge.
-    virtual void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const = 0;
+    virtual void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const = 0;
 
 };
 
@@ -76,7 +80,7 @@ public:
     void reverse();
     void moveStartPoint(Point2 to);
     void moveEndPoint(Point2 to);
-    void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
+    void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const;
 
 };
 
@@ -105,7 +109,7 @@ public:
     void reverse();
     void moveStartPoint(Point2 to);
     void moveEndPoint(Point2 to);
-    void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
+    void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const;
 
     EdgeSegment *convertToCubic() const;
 
@@ -135,7 +139,7 @@ public:
     void reverse();
     void moveStartPoint(Point2 to);
     void moveEndPoint(Point2 to);
-    void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
+    void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const;
 
     void deconverge(int param, double amount);
 

+ 11 - 5
ext/import-font.cpp

@@ -74,7 +74,7 @@ static int ftLineTo(const FT_Vector *to, void *user) {
     FtContext *context = reinterpret_cast<FtContext *>(user);
     Point2 endpoint = ftPoint2(*to);
     if (endpoint != context->position) {
-        context->contour->addEdge(new LinearSegment(context->position, endpoint));
+        context->contour->addEdge(EdgeHolder(context->position, endpoint));
         context->position = endpoint;
     }
     return 0;
@@ -82,15 +82,21 @@ static int ftLineTo(const FT_Vector *to, void *user) {
 
 static int ftConicTo(const FT_Vector *control, const FT_Vector *to, void *user) {
     FtContext *context = reinterpret_cast<FtContext *>(user);
-    context->contour->addEdge(new QuadraticSegment(context->position, ftPoint2(*control), ftPoint2(*to)));
-    context->position = ftPoint2(*to);
+    Point2 endpoint = ftPoint2(*to);
+    if (endpoint != context->position) {
+        context->contour->addEdge(EdgeHolder(context->position, ftPoint2(*control), endpoint));
+        context->position = endpoint;
+    }
     return 0;
 }
 
 static int ftCubicTo(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) {
     FtContext *context = reinterpret_cast<FtContext *>(user);
-    context->contour->addEdge(new CubicSegment(context->position, ftPoint2(*control1), ftPoint2(*control2), ftPoint2(*to)));
-    context->position = ftPoint2(*to);
+    Point2 endpoint = ftPoint2(*to);
+    if (endpoint != context->position || crossProduct(ftPoint2(*control1)-endpoint, ftPoint2(*control2)-endpoint)) {
+        context->contour->addEdge(EdgeHolder(context->position, ftPoint2(*control1), ftPoint2(*control2), endpoint));
+        context->position = endpoint;
+    }
     return 0;
 }
 

+ 10 - 10
ext/import-svg.cpp

@@ -101,7 +101,7 @@ static void addArcApproximate(Contour &contour, Point2 startPoint, Point2 endPoi
     if (endPoint == startPoint)
         return;
     if (radius.x == 0 || radius.y == 0)
-        return contour.addEdge(new LinearSegment(startPoint, endPoint));
+        return contour.addEdge(EdgeHolder(startPoint, endPoint));
 
     radius.x = fabs(radius.x);
     radius.y = fabs(radius.y);
@@ -142,7 +142,7 @@ static void addArcApproximate(Contour &contour, Point2 startPoint, Point2 endPoi
         d.set(cos(angle), sin(angle));
         controlPoint[1] = center+rotateVector(Vector2(d.x+cl*d.y, d.y-cl*d.x)*radius, axis);
         Point2 node = i == segments-1 ? endPoint : center+rotateVector(d*radius, axis);
-        contour.addEdge(new CubicSegment(prevNode, controlPoint[0], controlPoint[1], node));
+        contour.addEdge(EdgeHolder(prevNode, controlPoint[0], controlPoint[1], node));
         prevNode = node;
     }
 }
@@ -221,19 +221,19 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
                     REQUIRE(readCoord(node, pathDef));
                     if (nodeType == 'l')
                         node += prevNode;
-                    contour.addEdge(new LinearSegment(prevNode, node));
+                    contour.addEdge(EdgeHolder(prevNode, node));
                     break;
                 case 'H': case 'h':
                     REQUIRE(readDouble(node.x, pathDef));
                     if (nodeType == 'h')
                         node.x += prevNode.x;
-                    contour.addEdge(new LinearSegment(prevNode, node));
+                    contour.addEdge(EdgeHolder(prevNode, node));
                     break;
                 case 'V': case 'v':
                     REQUIRE(readDouble(node.y, pathDef));
                     if (nodeType == 'v')
                         node.y += prevNode.y;
-                    contour.addEdge(new LinearSegment(prevNode, node));
+                    contour.addEdge(EdgeHolder(prevNode, node));
                     break;
                 case 'Q': case 'q':
                     REQUIRE(readCoord(controlPoint[0], pathDef));
@@ -242,7 +242,7 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
                         controlPoint[0] += prevNode;
                         node += prevNode;
                     }
-                    contour.addEdge(new QuadraticSegment(prevNode, controlPoint[0], node));
+                    contour.addEdge(EdgeHolder(prevNode, controlPoint[0], node));
                     break;
                 case 'T': case 't':
                     if (prevNodeType == 'Q' || prevNodeType == 'q' || prevNodeType == 'T' || prevNodeType == 't')
@@ -252,7 +252,7 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
                     REQUIRE(readCoord(node, pathDef));
                     if (nodeType == 't')
                         node += prevNode;
-                    contour.addEdge(new QuadraticSegment(prevNode, controlPoint[0], node));
+                    contour.addEdge(EdgeHolder(prevNode, controlPoint[0], node));
                     break;
                 case 'C': case 'c':
                     REQUIRE(readCoord(controlPoint[0], pathDef));
@@ -263,7 +263,7 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
                         controlPoint[1] += prevNode;
                         node += prevNode;
                     }
-                    contour.addEdge(new CubicSegment(prevNode, controlPoint[0], controlPoint[1], node));
+                    contour.addEdge(EdgeHolder(prevNode, controlPoint[0], controlPoint[1], node));
                     break;
                 case 'S': case 's':
                     if (prevNodeType == 'C' || prevNodeType == 'c' || prevNodeType == 'S' || prevNodeType == 's')
@@ -276,7 +276,7 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
                         controlPoint[1] += prevNode;
                         node += prevNode;
                     }
-                    contour.addEdge(new CubicSegment(prevNode, controlPoint[0], controlPoint[1], node));
+                    contour.addEdge(EdgeHolder(prevNode, controlPoint[0], controlPoint[1], node));
                     break;
                 case 'A': case 'a':
                     {
@@ -309,7 +309,7 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
             if ((contour.edges.back()->point(1)-contour.edges[0]->point(0)).length() < endpointSnapRange)
                 contour.edges.back()->moveEndPoint(contour.edges[0]->point(0));
             else
-                contour.addEdge(new LinearSegment(prevNode, startPoint));
+                contour.addEdge(EdgeHolder(prevNode, startPoint));
         }
         prevNode = startPoint;
         prevNodeType = '\0';

+ 5 - 5
ext/resolve-shape-geometry.cpp

@@ -53,20 +53,20 @@ void shapeFromSkiaPath(Shape &shape, const SkPath &skPath) {
                     contour = &shape.addContour();
                 break;
             case SkPath::kLine_Verb:
-                contour->addEdge(new LinearSegment(pointFromSkiaPoint(edgePoints[0]), pointFromSkiaPoint(edgePoints[1])));
+                contour->addEdge(EdgeHolder(pointFromSkiaPoint(edgePoints[0]), pointFromSkiaPoint(edgePoints[1])));
                 break;
             case SkPath::kQuad_Verb:
-                contour->addEdge(new QuadraticSegment(pointFromSkiaPoint(edgePoints[0]), pointFromSkiaPoint(edgePoints[1]), pointFromSkiaPoint(edgePoints[2])));
+                contour->addEdge(EdgeHolder(pointFromSkiaPoint(edgePoints[0]), pointFromSkiaPoint(edgePoints[1]), pointFromSkiaPoint(edgePoints[2])));
                 break;
             case SkPath::kCubic_Verb:
-                contour->addEdge(new CubicSegment(pointFromSkiaPoint(edgePoints[0]), pointFromSkiaPoint(edgePoints[1]), pointFromSkiaPoint(edgePoints[2]), pointFromSkiaPoint(edgePoints[3])));
+                contour->addEdge(EdgeHolder(pointFromSkiaPoint(edgePoints[0]), pointFromSkiaPoint(edgePoints[1]), pointFromSkiaPoint(edgePoints[2]), pointFromSkiaPoint(edgePoints[3])));
                 break;
             case SkPath::kConic_Verb:
                 {
                     SkPoint quadPoints[5];
                     SkPath::ConvertConicToQuads(edgePoints[0], edgePoints[1], edgePoints[2], pathIterator.conicWeight(), quadPoints, 1);
-                    contour->addEdge(new QuadraticSegment(pointFromSkiaPoint(quadPoints[0]), pointFromSkiaPoint(quadPoints[1]), pointFromSkiaPoint(quadPoints[2])));
-                    contour->addEdge(new QuadraticSegment(pointFromSkiaPoint(quadPoints[2]), pointFromSkiaPoint(quadPoints[3]), pointFromSkiaPoint(quadPoints[4])));
+                    contour->addEdge(EdgeHolder(pointFromSkiaPoint(quadPoints[0]), pointFromSkiaPoint(quadPoints[1]), pointFromSkiaPoint(quadPoints[2])));
+                    contour->addEdge(EdgeHolder(pointFromSkiaPoint(quadPoints[2]), pointFromSkiaPoint(quadPoints[3]), pointFromSkiaPoint(quadPoints[4])));
                 }
                 break;
             case SkPath::kClose_Verb: