xref: /aosp_15_r20/external/skia/src/pathops/SkPathOpsCurve.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2015 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathOpsCurve.h"
8*c8dee2aaSAndroid Build Coastguard Worker 
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathOpsBounds.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathOpsRect.h"
12*c8dee2aaSAndroid Build Coastguard Worker 
13*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
14*c8dee2aaSAndroid Build Coastguard Worker #include <cfloat>
15*c8dee2aaSAndroid Build Coastguard Worker 
16*c8dee2aaSAndroid Build Coastguard Worker  // this cheats and assumes that the perpendicular to the point is the closest ray to the curve
17*c8dee2aaSAndroid Build Coastguard Worker  // this case (where the line and the curve are nearly coincident) may be the only case that counts
nearPoint(SkPath::Verb verb,const SkDPoint & xy,const SkDPoint & opp) const18*c8dee2aaSAndroid Build Coastguard Worker double SkDCurve::nearPoint(SkPath::Verb verb, const SkDPoint& xy, const SkDPoint& opp) const {
19*c8dee2aaSAndroid Build Coastguard Worker     int count = SkPathOpsVerbToPoints(verb);
20*c8dee2aaSAndroid Build Coastguard Worker     double minX = fCubic.fPts[0].fX;
21*c8dee2aaSAndroid Build Coastguard Worker     double maxX = minX;
22*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 1; index <= count; ++index) {
23*c8dee2aaSAndroid Build Coastguard Worker         minX = std::min(minX, fCubic.fPts[index].fX);
24*c8dee2aaSAndroid Build Coastguard Worker         maxX = std::max(maxX, fCubic.fPts[index].fX);
25*c8dee2aaSAndroid Build Coastguard Worker     }
26*c8dee2aaSAndroid Build Coastguard Worker     if (!AlmostBetweenUlps(minX, xy.fX, maxX)) {
27*c8dee2aaSAndroid Build Coastguard Worker         return -1;
28*c8dee2aaSAndroid Build Coastguard Worker     }
29*c8dee2aaSAndroid Build Coastguard Worker     double minY = fCubic.fPts[0].fY;
30*c8dee2aaSAndroid Build Coastguard Worker     double maxY = minY;
31*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 1; index <= count; ++index) {
32*c8dee2aaSAndroid Build Coastguard Worker         minY = std::min(minY, fCubic.fPts[index].fY);
33*c8dee2aaSAndroid Build Coastguard Worker         maxY = std::max(maxY, fCubic.fPts[index].fY);
34*c8dee2aaSAndroid Build Coastguard Worker     }
35*c8dee2aaSAndroid Build Coastguard Worker     if (!AlmostBetweenUlps(minY, xy.fY, maxY)) {
36*c8dee2aaSAndroid Build Coastguard Worker         return -1;
37*c8dee2aaSAndroid Build Coastguard Worker     }
38*c8dee2aaSAndroid Build Coastguard Worker     SkIntersections i;
39*c8dee2aaSAndroid Build Coastguard Worker     SkDLine perp = {{ xy, { xy.fX + opp.fY - xy.fY, xy.fY + xy.fX - opp.fX }}};
40*c8dee2aaSAndroid Build Coastguard Worker     (*CurveDIntersectRay[verb])(*this, perp, &i);
41*c8dee2aaSAndroid Build Coastguard Worker     int minIndex = -1;
42*c8dee2aaSAndroid Build Coastguard Worker     double minDist = FLT_MAX;
43*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < i.used(); ++index) {
44*c8dee2aaSAndroid Build Coastguard Worker         double dist = xy.distance(i.pt(index));
45*c8dee2aaSAndroid Build Coastguard Worker         if (minDist > dist) {
46*c8dee2aaSAndroid Build Coastguard Worker             minDist = dist;
47*c8dee2aaSAndroid Build Coastguard Worker             minIndex = index;
48*c8dee2aaSAndroid Build Coastguard Worker         }
49*c8dee2aaSAndroid Build Coastguard Worker     }
50*c8dee2aaSAndroid Build Coastguard Worker     if (minIndex < 0) {
51*c8dee2aaSAndroid Build Coastguard Worker         return -1;
52*c8dee2aaSAndroid Build Coastguard Worker     }
53*c8dee2aaSAndroid Build Coastguard Worker     double largest = std::max(std::max(maxX, maxY), -std::min(minX, minY));
54*c8dee2aaSAndroid Build Coastguard Worker     if (!AlmostEqualUlps_Pin(largest, largest + minDist)) { // is distance within ULPS tolerance?
55*c8dee2aaSAndroid Build Coastguard Worker         return -1;
56*c8dee2aaSAndroid Build Coastguard Worker     }
57*c8dee2aaSAndroid Build Coastguard Worker     return SkPinT(i[0][minIndex]);
58*c8dee2aaSAndroid Build Coastguard Worker }
59*c8dee2aaSAndroid Build Coastguard Worker 
setConicBounds(const SkPoint curve[3],SkScalar curveWeight,double tStart,double tEnd,SkPathOpsBounds * bounds)60*c8dee2aaSAndroid Build Coastguard Worker void SkDCurve::setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
61*c8dee2aaSAndroid Build Coastguard Worker         double tStart, double tEnd, SkPathOpsBounds* bounds) {
62*c8dee2aaSAndroid Build Coastguard Worker     SkDConic dCurve;
63*c8dee2aaSAndroid Build Coastguard Worker     dCurve.set(curve, curveWeight);
64*c8dee2aaSAndroid Build Coastguard Worker     SkDRect dRect;
65*c8dee2aaSAndroid Build Coastguard Worker     dRect.setBounds(dCurve, fConic, tStart, tEnd);
66*c8dee2aaSAndroid Build Coastguard Worker     bounds->setLTRB(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
67*c8dee2aaSAndroid Build Coastguard Worker                     SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
68*c8dee2aaSAndroid Build Coastguard Worker }
69*c8dee2aaSAndroid Build Coastguard Worker 
setCubicBounds(const SkPoint curve[4],SkScalar,double tStart,double tEnd,SkPathOpsBounds * bounds)70*c8dee2aaSAndroid Build Coastguard Worker void SkDCurve::setCubicBounds(const SkPoint curve[4], SkScalar ,
71*c8dee2aaSAndroid Build Coastguard Worker         double tStart, double tEnd, SkPathOpsBounds* bounds) {
72*c8dee2aaSAndroid Build Coastguard Worker     SkDCubic dCurve;
73*c8dee2aaSAndroid Build Coastguard Worker     dCurve.set(curve);
74*c8dee2aaSAndroid Build Coastguard Worker     SkDRect dRect;
75*c8dee2aaSAndroid Build Coastguard Worker     dRect.setBounds(dCurve, fCubic, tStart, tEnd);
76*c8dee2aaSAndroid Build Coastguard Worker     bounds->setLTRB(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
77*c8dee2aaSAndroid Build Coastguard Worker                     SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
78*c8dee2aaSAndroid Build Coastguard Worker }
79*c8dee2aaSAndroid Build Coastguard Worker 
setQuadBounds(const SkPoint curve[3],SkScalar,double tStart,double tEnd,SkPathOpsBounds * bounds)80*c8dee2aaSAndroid Build Coastguard Worker void SkDCurve::setQuadBounds(const SkPoint curve[3], SkScalar ,
81*c8dee2aaSAndroid Build Coastguard Worker         double tStart, double tEnd, SkPathOpsBounds* bounds) {
82*c8dee2aaSAndroid Build Coastguard Worker     SkDQuad dCurve;
83*c8dee2aaSAndroid Build Coastguard Worker     dCurve.set(curve);
84*c8dee2aaSAndroid Build Coastguard Worker     SkDRect dRect;
85*c8dee2aaSAndroid Build Coastguard Worker     dRect.setBounds(dCurve, fQuad, tStart, tEnd);
86*c8dee2aaSAndroid Build Coastguard Worker     bounds->setLTRB(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
87*c8dee2aaSAndroid Build Coastguard Worker                     SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
88*c8dee2aaSAndroid Build Coastguard Worker }
89*c8dee2aaSAndroid Build Coastguard Worker 
setCurveHullSweep(SkPath::Verb verb)90*c8dee2aaSAndroid Build Coastguard Worker void SkDCurveSweep::setCurveHullSweep(SkPath::Verb verb) {
91*c8dee2aaSAndroid Build Coastguard Worker     fOrdered = true;
92*c8dee2aaSAndroid Build Coastguard Worker     fSweep[0] = fCurve[1] - fCurve[0];
93*c8dee2aaSAndroid Build Coastguard Worker     if (SkPath::kLine_Verb == verb) {
94*c8dee2aaSAndroid Build Coastguard Worker         fSweep[1] = fSweep[0];
95*c8dee2aaSAndroid Build Coastguard Worker         fIsCurve = false;
96*c8dee2aaSAndroid Build Coastguard Worker         return;
97*c8dee2aaSAndroid Build Coastguard Worker     }
98*c8dee2aaSAndroid Build Coastguard Worker     fSweep[1] = fCurve[2] - fCurve[0];
99*c8dee2aaSAndroid Build Coastguard Worker     // OPTIMIZE: I do the following float check a lot -- probably need a
100*c8dee2aaSAndroid Build Coastguard Worker     // central place for this val-is-small-compared-to-curve check
101*c8dee2aaSAndroid Build Coastguard Worker     double maxVal = 0;
102*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index <= SkPathOpsVerbToPoints(verb); ++index) {
103*c8dee2aaSAndroid Build Coastguard Worker         maxVal = std::max(maxVal, std::max(SkTAbs(fCurve[index].fX),
104*c8dee2aaSAndroid Build Coastguard Worker                 SkTAbs(fCurve[index].fY)));
105*c8dee2aaSAndroid Build Coastguard Worker     }
106*c8dee2aaSAndroid Build Coastguard Worker     {
107*c8dee2aaSAndroid Build Coastguard Worker         if (SkPath::kCubic_Verb != verb) {
108*c8dee2aaSAndroid Build Coastguard Worker             if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
109*c8dee2aaSAndroid Build Coastguard Worker                     && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
110*c8dee2aaSAndroid Build Coastguard Worker                 fSweep[0] = fSweep[1];
111*c8dee2aaSAndroid Build Coastguard Worker             }
112*c8dee2aaSAndroid Build Coastguard Worker             goto setIsCurve;
113*c8dee2aaSAndroid Build Coastguard Worker         }
114*c8dee2aaSAndroid Build Coastguard Worker         SkDVector thirdSweep = fCurve[3] - fCurve[0];
115*c8dee2aaSAndroid Build Coastguard Worker         if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
116*c8dee2aaSAndroid Build Coastguard Worker             fSweep[0] = fSweep[1];
117*c8dee2aaSAndroid Build Coastguard Worker             fSweep[1] = thirdSweep;
118*c8dee2aaSAndroid Build Coastguard Worker             if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
119*c8dee2aaSAndroid Build Coastguard Worker                     && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
120*c8dee2aaSAndroid Build Coastguard Worker                 fSweep[0] = fSweep[1];
121*c8dee2aaSAndroid Build Coastguard Worker                 fCurve[1] = fCurve[3];
122*c8dee2aaSAndroid Build Coastguard Worker             }
123*c8dee2aaSAndroid Build Coastguard Worker             goto setIsCurve;
124*c8dee2aaSAndroid Build Coastguard Worker         }
125*c8dee2aaSAndroid Build Coastguard Worker         double s1x3 = fSweep[0].crossCheck(thirdSweep);
126*c8dee2aaSAndroid Build Coastguard Worker         double s3x2 = thirdSweep.crossCheck(fSweep[1]);
127*c8dee2aaSAndroid Build Coastguard Worker         if (s1x3 * s3x2 >= 0) {  // if third vector is on or between first two vectors
128*c8dee2aaSAndroid Build Coastguard Worker             goto setIsCurve;
129*c8dee2aaSAndroid Build Coastguard Worker         }
130*c8dee2aaSAndroid Build Coastguard Worker         double s2x1 = fSweep[1].crossCheck(fSweep[0]);
131*c8dee2aaSAndroid Build Coastguard Worker         // FIXME: If the sweep of the cubic is greater than 180 degrees, we're in trouble
132*c8dee2aaSAndroid Build Coastguard Worker         // probably such wide sweeps should be artificially subdivided earlier so that never happens
133*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(s1x3 * s2x1 < 0 || s1x3 * s3x2 < 0);
134*c8dee2aaSAndroid Build Coastguard Worker         if (s3x2 * s2x1 < 0) {
135*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(s2x1 * s1x3 > 0);
136*c8dee2aaSAndroid Build Coastguard Worker             fSweep[0] = fSweep[1];
137*c8dee2aaSAndroid Build Coastguard Worker             fOrdered = false;
138*c8dee2aaSAndroid Build Coastguard Worker         }
139*c8dee2aaSAndroid Build Coastguard Worker         fSweep[1] = thirdSweep;
140*c8dee2aaSAndroid Build Coastguard Worker     }
141*c8dee2aaSAndroid Build Coastguard Worker setIsCurve:
142*c8dee2aaSAndroid Build Coastguard Worker     fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0;
143*c8dee2aaSAndroid Build Coastguard Worker }
144