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