Path: blob/master/thirdparty/msdfgen/core/convergent-curve-ordering.cpp
14710 views
1#include "convergent-curve-ordering.h"23#include "arithmetics.hpp"4#include "Vector2.hpp"56/*7* For non-degenerate curves A(t), B(t) (ones where all control points are distinct) both originating at P = A(0) = B(0) = *corner,8* we are computing the limit of9*10* sign(crossProduct( A(t / |A'(0)|) - P, B(t / |B'(0)|) - P ))11*12* for t -> 0 from 1. Of note is that the curves' parameter has to be normed by the first derivative at P,13* which ensures that the limit approaches P at the same rate along both curves - omitting this was the main error of earlier versions of deconverge.14*15* For degenerate cubic curves (ones where the first control point equals the origin point), the denominator |A'(0)| is zero,16* so to address that, we approach with the square root of t and use the derivative of A(sqrt(t)), which at t = 0 equals A''(0)/217* Therefore, in these cases, we replace one factor of the cross product with A(sqrt(2*t / |A''(0)|)) - P18*19* The cross product results in a polynomial (in respect to t or t^2 in the degenerate case),20* the limit of sign of which at zero can be determined by the lowest order non-zero derivative,21* which equals to the sign of the first non-zero polynomial coefficient in the order of increasing exponents.22*23* The polynomial's constant and linear terms are zero, so the first derivative is definitely zero as well.24* The second derivative is assumed to be zero (or near zero) due to the curves being convergent - this is an input requirement25* (otherwise the correct result is the sign of the cross product of their directions at t = 0).26* Therefore, we skip the first and second derivatives.27*/2829namespace msdfgen {3031static void simplifyDegenerateCurve(Point2 *controlPoints, int &order) {32if (order == 3 && (controlPoints[1] == controlPoints[0] || controlPoints[1] == controlPoints[3]) && (controlPoints[2] == controlPoints[0] || controlPoints[2] == controlPoints[3])) {33controlPoints[1] = controlPoints[3];34order = 1;35}36if (order == 2 && (controlPoints[1] == controlPoints[0] || controlPoints[1] == controlPoints[2])) {37controlPoints[1] = controlPoints[2];38order = 1;39}40if (order == 1 && controlPoints[0] == controlPoints[1])41order = 0;42}4344int convergentCurveOrdering(const Point2 *corner, int controlPointsBefore, int controlPointsAfter) {45if (!(controlPointsBefore > 0 && controlPointsAfter > 0))46return 0;47Vector2 a1, a2, a3, b1, b2, b3;48a1 = *(corner-1)-*corner;49b1 = *(corner+1)-*corner;50if (controlPointsBefore >= 2)51a2 = *(corner-2)-*(corner-1)-a1;52if (controlPointsAfter >= 2)53b2 = *(corner+2)-*(corner+1)-b1;54if (controlPointsBefore >= 3) {55a3 = *(corner-3)-*(corner-2)-(*(corner-2)-*(corner-1))-a2;56a2 *= 3;57}58if (controlPointsAfter >= 3) {59b3 = *(corner+3)-*(corner+2)-(*(corner+2)-*(corner+1))-b2;60b2 *= 3;61}62a1 *= controlPointsBefore;63b1 *= controlPointsAfter;64// Non-degenerate case65if (a1 && b1) {66double as = a1.length();67double bs = b1.length();68// Third derivative69if (double d = as*crossProduct(a1, b2) + bs*crossProduct(a2, b1))70return sign(d);71// Fourth derivative72if (double d = as*as*crossProduct(a1, b3) + as*bs*crossProduct(a2, b2) + bs*bs*crossProduct(a3, b1))73return sign(d);74// Fifth derivative75if (double d = as*crossProduct(a2, b3) + bs*crossProduct(a3, b2))76return sign(d);77// Sixth derivative78return sign(crossProduct(a3, b3));79}80// Degenerate curve after corner (control point after corner equals corner)81int s = 1;82if (a1) { // !b183// Swap aN <-> bN and handle in if (b1)84b1 = a1;85a1 = b2, b2 = a2, a2 = a1;86a1 = b3, b3 = a3, a3 = a1;87s = -1; // make sure to also flip output88}89// Degenerate curve before corner (control point before corner equals corner)90if (b1) { // !a191// Two-and-a-half-th derivative92if (double d = crossProduct(a3, b1))93return s*sign(d);94// Third derivative95if (double d = crossProduct(a2, b2))96return s*sign(d);97// Three-and-a-half-th derivative98if (double d = crossProduct(a3, b2))99return s*sign(d);100// Fourth derivative101if (double d = crossProduct(a2, b3))102return s*sign(d);103// Four-and-a-half-th derivative104return s*sign(crossProduct(a3, b3));105}106// Degenerate curves on both sides of the corner (control point before and after corner equals corner)107{ // !a1 && !b1108// Two-and-a-half-th derivative109if (double d = sqrt(a2.length())*crossProduct(a2, b3) + sqrt(b2.length())*crossProduct(a3, b2))110return sign(d);111// Third derivative112return sign(crossProduct(a3, b3));113}114}115116int convergentCurveOrdering(const EdgeSegment *a, const EdgeSegment *b) {117Point2 controlPoints[12];118Point2 *corner = controlPoints+4;119Point2 *aCpTmp = controlPoints+8;120int aOrder = int(a->type());121int bOrder = int(b->type());122if (!(aOrder >= 1 && aOrder <= 3 && bOrder >= 1 && bOrder <= 3)) {123// Not implemented - only linear, quadratic, and cubic curves supported124return 0;125}126for (int i = 0; i <= aOrder; ++i)127aCpTmp[i] = a->controlPoints()[i];128for (int i = 0; i <= bOrder; ++i)129corner[i] = b->controlPoints()[i];130if (aCpTmp[aOrder] != *corner)131return 0;132simplifyDegenerateCurve(aCpTmp, aOrder);133simplifyDegenerateCurve(corner, bOrder);134for (int i = 0; i < aOrder; ++i)135corner[i-aOrder] = aCpTmp[i];136return convergentCurveOrdering(corner, aOrder, bOrder);137}138139}140141142