Improved edge segment construction

This commit is contained in:
Chlumsky 2023-12-17 00:39:45 +01:00
parent 51f6b5b053
commit 2357140784
7 changed files with 70 additions and 57 deletions

View File

@ -9,16 +9,6 @@ void EdgeHolder::swap(EdgeHolder &a, EdgeHolder &b) {
b.edgeSegment = tmp; 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) { } EdgeHolder::EdgeHolder(const EdgeHolder &orig) : edgeSegment(orig.edgeSegment ? orig.edgeSegment->clone() : NULL) { }
#ifdef MSDFGEN_USE_CPP11 #ifdef MSDFGEN_USE_CPP11

View File

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

View File

@ -6,6 +6,25 @@
namespace msdfgen { 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 { void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const {
if (param < 0) { if (param < 0) {
Vector2 dir = direction(0).normalize(); 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) { 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[0] = p0;
p[1] = p1; p[1] = p1;
p[2] = p2; p[2] = p2;
} }
CubicSegment::CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : EdgeSegment(edgeColor) { 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[0] = p0;
p[1] = p1; p[1] = p1;
p[2] = p2; p[2] = p2;
@ -486,25 +499,25 @@ void CubicSegment::moveEndPoint(Point2 to) {
p[3] = to; p[3] = to;
} }
void LinearSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const { void LinearSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
part1 = new LinearSegment(p[0], point(1/3.), color); part0 = new LinearSegment(p[0], point(1/3.), color);
part2 = new LinearSegment(point(1/3.), point(2/3.), color); part1 = new LinearSegment(point(1/3.), point(2/3.), color);
part3 = new LinearSegment(point(2/3.), p[1], color); part2 = new LinearSegment(point(2/3.), p[1], color);
} }
void QuadraticSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const { void QuadraticSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const {
part1 = new QuadraticSegment(p[0], mix(p[0], p[1], 1/3.), point(1/3.), color); part0 = 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); 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);
part3 = new QuadraticSegment(point(2/3.), mix(p[1], p[2], 2/3.), p[2], 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 { void CubicSegment::splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) 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); 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);
part2 = new CubicSegment(point(1/3.), 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], 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.), 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); 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 { EdgeSegment *QuadraticSegment::convertToCubic() const {

View File

@ -17,6 +17,10 @@ class EdgeSegment {
public: public:
EdgeColor color; 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) { } EdgeSegment(EdgeColor edgeColor = WHITE) : color(edgeColor) { }
virtual ~EdgeSegment() { } virtual ~EdgeSegment() { }
/// Creates a copy of the edge segment. /// Creates a copy of the edge segment.
@ -47,7 +51,7 @@ public:
/// Moves the end point of the edge segment. /// Moves the end point of the edge segment.
virtual void moveEndPoint(Point2 to) = 0; virtual void moveEndPoint(Point2 to) = 0;
/// Splits the edge segments into thirds which together represent the original edge. /// 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 reverse();
void moveStartPoint(Point2 to); void moveStartPoint(Point2 to);
void moveEndPoint(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 reverse();
void moveStartPoint(Point2 to); void moveStartPoint(Point2 to);
void moveEndPoint(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; EdgeSegment *convertToCubic() const;
@ -135,7 +139,7 @@ public:
void reverse(); void reverse();
void moveStartPoint(Point2 to); void moveStartPoint(Point2 to);
void moveEndPoint(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); void deconverge(int param, double amount);

View File

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

View File

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

View File

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