1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2012 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci#include "src/pathops/SkIntersections.h" 8cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsLine.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include <utility> 11cb93a386Sopenharmony_ci 12cb93a386Sopenharmony_civoid SkIntersections::cleanUpParallelLines(bool parallel) { 13cb93a386Sopenharmony_ci while (fUsed > 2) { 14cb93a386Sopenharmony_ci removeOne(1); 15cb93a386Sopenharmony_ci } 16cb93a386Sopenharmony_ci if (fUsed == 2 && !parallel) { 17cb93a386Sopenharmony_ci bool startMatch = fT[0][0] == 0 || zero_or_one(fT[1][0]); 18cb93a386Sopenharmony_ci bool endMatch = fT[0][1] == 1 || zero_or_one(fT[1][1]); 19cb93a386Sopenharmony_ci if ((!startMatch && !endMatch) || approximately_equal(fT[0][0], fT[0][1])) { 20cb93a386Sopenharmony_ci SkASSERT(startMatch || endMatch); 21cb93a386Sopenharmony_ci if (startMatch && endMatch && (fT[0][0] != 0 || !zero_or_one(fT[1][0])) 22cb93a386Sopenharmony_ci && fT[0][1] == 1 && zero_or_one(fT[1][1])) { 23cb93a386Sopenharmony_ci removeOne(0); 24cb93a386Sopenharmony_ci } else { 25cb93a386Sopenharmony_ci removeOne(endMatch); 26cb93a386Sopenharmony_ci } 27cb93a386Sopenharmony_ci } 28cb93a386Sopenharmony_ci } 29cb93a386Sopenharmony_ci if (fUsed == 2) { 30cb93a386Sopenharmony_ci fIsCoincident[0] = fIsCoincident[1] = 0x03; 31cb93a386Sopenharmony_ci } 32cb93a386Sopenharmony_ci} 33cb93a386Sopenharmony_ci 34cb93a386Sopenharmony_civoid SkIntersections::computePoints(const SkDLine& line, int used) { 35cb93a386Sopenharmony_ci fPt[0] = line.ptAtT(fT[0][0]); 36cb93a386Sopenharmony_ci if ((fUsed = used) == 2) { 37cb93a386Sopenharmony_ci fPt[1] = line.ptAtT(fT[0][1]); 38cb93a386Sopenharmony_ci } 39cb93a386Sopenharmony_ci} 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ciint SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) { 42cb93a386Sopenharmony_ci fMax = 2; 43cb93a386Sopenharmony_ci SkDVector aLen = a[1] - a[0]; 44cb93a386Sopenharmony_ci SkDVector bLen = b[1] - b[0]; 45cb93a386Sopenharmony_ci /* Slopes match when denom goes to zero: 46cb93a386Sopenharmony_ci axLen / ayLen == bxLen / byLen 47cb93a386Sopenharmony_ci (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen 48cb93a386Sopenharmony_ci byLen * axLen == ayLen * bxLen 49cb93a386Sopenharmony_ci byLen * axLen - ayLen * bxLen == 0 ( == denom ) 50cb93a386Sopenharmony_ci */ 51cb93a386Sopenharmony_ci double denom = bLen.fY * aLen.fX - aLen.fY * bLen.fX; 52cb93a386Sopenharmony_ci int used; 53cb93a386Sopenharmony_ci if (!approximately_zero(denom)) { 54cb93a386Sopenharmony_ci SkDVector ab0 = a[0] - b[0]; 55cb93a386Sopenharmony_ci double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX; 56cb93a386Sopenharmony_ci double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX; 57cb93a386Sopenharmony_ci numerA /= denom; 58cb93a386Sopenharmony_ci numerB /= denom; 59cb93a386Sopenharmony_ci fT[0][0] = numerA; 60cb93a386Sopenharmony_ci fT[1][0] = numerB; 61cb93a386Sopenharmony_ci used = 1; 62cb93a386Sopenharmony_ci } else { 63cb93a386Sopenharmony_ci /* See if the axis intercepts match: 64cb93a386Sopenharmony_ci ay - ax * ayLen / axLen == by - bx * ayLen / axLen 65cb93a386Sopenharmony_ci axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen) 66cb93a386Sopenharmony_ci axLen * ay - ax * ayLen == axLen * by - bx * ayLen 67cb93a386Sopenharmony_ci */ 68cb93a386Sopenharmony_ci if (!AlmostEqualUlps(aLen.fX * a[0].fY - aLen.fY * a[0].fX, 69cb93a386Sopenharmony_ci aLen.fX * b[0].fY - aLen.fY * b[0].fX)) { 70cb93a386Sopenharmony_ci return fUsed = 0; 71cb93a386Sopenharmony_ci } 72cb93a386Sopenharmony_ci // there's no great answer for intersection points for coincident rays, but return something 73cb93a386Sopenharmony_ci fT[0][0] = fT[1][0] = 0; 74cb93a386Sopenharmony_ci fT[1][0] = fT[1][1] = 1; 75cb93a386Sopenharmony_ci used = 2; 76cb93a386Sopenharmony_ci } 77cb93a386Sopenharmony_ci computePoints(a, used); 78cb93a386Sopenharmony_ci return fUsed; 79cb93a386Sopenharmony_ci} 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ci// note that this only works if both lines are neither horizontal nor vertical 82cb93a386Sopenharmony_ciint SkIntersections::intersect(const SkDLine& a, const SkDLine& b) { 83cb93a386Sopenharmony_ci fMax = 3; // note that we clean up so that there is no more than two in the end 84cb93a386Sopenharmony_ci // see if end points intersect the opposite line 85cb93a386Sopenharmony_ci double t; 86cb93a386Sopenharmony_ci for (int iA = 0; iA < 2; ++iA) { 87cb93a386Sopenharmony_ci if ((t = b.exactPoint(a[iA])) >= 0) { 88cb93a386Sopenharmony_ci insert(iA, t, a[iA]); 89cb93a386Sopenharmony_ci } 90cb93a386Sopenharmony_ci } 91cb93a386Sopenharmony_ci for (int iB = 0; iB < 2; ++iB) { 92cb93a386Sopenharmony_ci if ((t = a.exactPoint(b[iB])) >= 0) { 93cb93a386Sopenharmony_ci insert(t, iB, b[iB]); 94cb93a386Sopenharmony_ci } 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci /* Determine the intersection point of two line segments 97cb93a386Sopenharmony_ci Return FALSE if the lines don't intersect 98cb93a386Sopenharmony_ci from: http://paulbourke.net/geometry/lineline2d/ */ 99cb93a386Sopenharmony_ci double axLen = a[1].fX - a[0].fX; 100cb93a386Sopenharmony_ci double ayLen = a[1].fY - a[0].fY; 101cb93a386Sopenharmony_ci double bxLen = b[1].fX - b[0].fX; 102cb93a386Sopenharmony_ci double byLen = b[1].fY - b[0].fY; 103cb93a386Sopenharmony_ci /* Slopes match when denom goes to zero: 104cb93a386Sopenharmony_ci axLen / ayLen == bxLen / byLen 105cb93a386Sopenharmony_ci (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen 106cb93a386Sopenharmony_ci byLen * axLen == ayLen * bxLen 107cb93a386Sopenharmony_ci byLen * axLen - ayLen * bxLen == 0 ( == denom ) 108cb93a386Sopenharmony_ci */ 109cb93a386Sopenharmony_ci double axByLen = axLen * byLen; 110cb93a386Sopenharmony_ci double ayBxLen = ayLen * bxLen; 111cb93a386Sopenharmony_ci // detect parallel lines the same way here and in SkOpAngle operator < 112cb93a386Sopenharmony_ci // so that non-parallel means they are also sortable 113cb93a386Sopenharmony_ci bool unparallel = fAllowNear ? NotAlmostEqualUlps_Pin(axByLen, ayBxLen) 114cb93a386Sopenharmony_ci : NotAlmostDequalUlps(axByLen, ayBxLen); 115cb93a386Sopenharmony_ci if (unparallel && fUsed == 0) { 116cb93a386Sopenharmony_ci double ab0y = a[0].fY - b[0].fY; 117cb93a386Sopenharmony_ci double ab0x = a[0].fX - b[0].fX; 118cb93a386Sopenharmony_ci double numerA = ab0y * bxLen - byLen * ab0x; 119cb93a386Sopenharmony_ci double numerB = ab0y * axLen - ayLen * ab0x; 120cb93a386Sopenharmony_ci double denom = axByLen - ayBxLen; 121cb93a386Sopenharmony_ci if (between(0, numerA, denom) && between(0, numerB, denom)) { 122cb93a386Sopenharmony_ci fT[0][0] = numerA / denom; 123cb93a386Sopenharmony_ci fT[1][0] = numerB / denom; 124cb93a386Sopenharmony_ci computePoints(a, 1); 125cb93a386Sopenharmony_ci } 126cb93a386Sopenharmony_ci } 127cb93a386Sopenharmony_ci/* Allow tracking that both sets of end points are near each other -- the lines are entirely 128cb93a386Sopenharmony_ci coincident -- even when the end points are not exactly the same. 129cb93a386Sopenharmony_ci Mark this as a 'wild card' for the end points, so that either point is considered totally 130cb93a386Sopenharmony_ci coincident. Then, avoid folding the lines over each other, but allow either end to mate 131cb93a386Sopenharmony_ci to the next set of lines. 132cb93a386Sopenharmony_ci */ 133cb93a386Sopenharmony_ci if (fAllowNear || !unparallel) { 134cb93a386Sopenharmony_ci double aNearB[2]; 135cb93a386Sopenharmony_ci double bNearA[2]; 136cb93a386Sopenharmony_ci bool aNotB[2] = {false, false}; 137cb93a386Sopenharmony_ci bool bNotA[2] = {false, false}; 138cb93a386Sopenharmony_ci int nearCount = 0; 139cb93a386Sopenharmony_ci for (int index = 0; index < 2; ++index) { 140cb93a386Sopenharmony_ci aNearB[index] = t = b.nearPoint(a[index], &aNotB[index]); 141cb93a386Sopenharmony_ci nearCount += t >= 0; 142cb93a386Sopenharmony_ci bNearA[index] = t = a.nearPoint(b[index], &bNotA[index]); 143cb93a386Sopenharmony_ci nearCount += t >= 0; 144cb93a386Sopenharmony_ci } 145cb93a386Sopenharmony_ci if (nearCount > 0) { 146cb93a386Sopenharmony_ci // Skip if each segment contributes to one end point. 147cb93a386Sopenharmony_ci if (nearCount != 2 || aNotB[0] == aNotB[1]) { 148cb93a386Sopenharmony_ci for (int iA = 0; iA < 2; ++iA) { 149cb93a386Sopenharmony_ci if (!aNotB[iA]) { 150cb93a386Sopenharmony_ci continue; 151cb93a386Sopenharmony_ci } 152cb93a386Sopenharmony_ci int nearer = aNearB[iA] > 0.5; 153cb93a386Sopenharmony_ci if (!bNotA[nearer]) { 154cb93a386Sopenharmony_ci continue; 155cb93a386Sopenharmony_ci } 156cb93a386Sopenharmony_ci SkASSERT(a[iA] != b[nearer]); 157cb93a386Sopenharmony_ci SkOPASSERT(iA == (bNearA[nearer] > 0.5)); 158cb93a386Sopenharmony_ci insertNear(iA, nearer, a[iA], b[nearer]); 159cb93a386Sopenharmony_ci aNearB[iA] = -1; 160cb93a386Sopenharmony_ci bNearA[nearer] = -1; 161cb93a386Sopenharmony_ci nearCount -= 2; 162cb93a386Sopenharmony_ci } 163cb93a386Sopenharmony_ci } 164cb93a386Sopenharmony_ci if (nearCount > 0) { 165cb93a386Sopenharmony_ci for (int iA = 0; iA < 2; ++iA) { 166cb93a386Sopenharmony_ci if (aNearB[iA] >= 0) { 167cb93a386Sopenharmony_ci insert(iA, aNearB[iA], a[iA]); 168cb93a386Sopenharmony_ci } 169cb93a386Sopenharmony_ci } 170cb93a386Sopenharmony_ci for (int iB = 0; iB < 2; ++iB) { 171cb93a386Sopenharmony_ci if (bNearA[iB] >= 0) { 172cb93a386Sopenharmony_ci insert(bNearA[iB], iB, b[iB]); 173cb93a386Sopenharmony_ci } 174cb93a386Sopenharmony_ci } 175cb93a386Sopenharmony_ci } 176cb93a386Sopenharmony_ci } 177cb93a386Sopenharmony_ci } 178cb93a386Sopenharmony_ci cleanUpParallelLines(!unparallel); 179cb93a386Sopenharmony_ci SkASSERT(fUsed <= 2); 180cb93a386Sopenharmony_ci return fUsed; 181cb93a386Sopenharmony_ci} 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_cistatic int horizontal_coincident(const SkDLine& line, double y) { 184cb93a386Sopenharmony_ci double min = line[0].fY; 185cb93a386Sopenharmony_ci double max = line[1].fY; 186cb93a386Sopenharmony_ci if (min > max) { 187cb93a386Sopenharmony_ci using std::swap; 188cb93a386Sopenharmony_ci swap(min, max); 189cb93a386Sopenharmony_ci } 190cb93a386Sopenharmony_ci if (min > y || max < y) { 191cb93a386Sopenharmony_ci return 0; 192cb93a386Sopenharmony_ci } 193cb93a386Sopenharmony_ci if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) { 194cb93a386Sopenharmony_ci return 2; 195cb93a386Sopenharmony_ci } 196cb93a386Sopenharmony_ci return 1; 197cb93a386Sopenharmony_ci} 198cb93a386Sopenharmony_ci 199cb93a386Sopenharmony_cidouble SkIntersections::HorizontalIntercept(const SkDLine& line, double y) { 200cb93a386Sopenharmony_ci SkASSERT(line[1].fY != line[0].fY); 201cb93a386Sopenharmony_ci return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY)); 202cb93a386Sopenharmony_ci} 203cb93a386Sopenharmony_ci 204cb93a386Sopenharmony_ciint SkIntersections::horizontal(const SkDLine& line, double left, double right, 205cb93a386Sopenharmony_ci double y, bool flipped) { 206cb93a386Sopenharmony_ci fMax = 3; // clean up parallel at the end will limit the result to 2 at the most 207cb93a386Sopenharmony_ci // see if end points intersect the opposite line 208cb93a386Sopenharmony_ci double t; 209cb93a386Sopenharmony_ci const SkDPoint leftPt = { left, y }; 210cb93a386Sopenharmony_ci if ((t = line.exactPoint(leftPt)) >= 0) { 211cb93a386Sopenharmony_ci insert(t, (double) flipped, leftPt); 212cb93a386Sopenharmony_ci } 213cb93a386Sopenharmony_ci if (left != right) { 214cb93a386Sopenharmony_ci const SkDPoint rightPt = { right, y }; 215cb93a386Sopenharmony_ci if ((t = line.exactPoint(rightPt)) >= 0) { 216cb93a386Sopenharmony_ci insert(t, (double) !flipped, rightPt); 217cb93a386Sopenharmony_ci } 218cb93a386Sopenharmony_ci for (int index = 0; index < 2; ++index) { 219cb93a386Sopenharmony_ci if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) { 220cb93a386Sopenharmony_ci insert((double) index, flipped ? 1 - t : t, line[index]); 221cb93a386Sopenharmony_ci } 222cb93a386Sopenharmony_ci } 223cb93a386Sopenharmony_ci } 224cb93a386Sopenharmony_ci int result = horizontal_coincident(line, y); 225cb93a386Sopenharmony_ci if (result == 1 && fUsed == 0) { 226cb93a386Sopenharmony_ci fT[0][0] = HorizontalIntercept(line, y); 227cb93a386Sopenharmony_ci double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX); 228cb93a386Sopenharmony_ci if (between(left, xIntercept, right)) { 229cb93a386Sopenharmony_ci fT[1][0] = (xIntercept - left) / (right - left); 230cb93a386Sopenharmony_ci if (flipped) { 231cb93a386Sopenharmony_ci // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX - [0].fX 232cb93a386Sopenharmony_ci for (int index = 0; index < result; ++index) { 233cb93a386Sopenharmony_ci fT[1][index] = 1 - fT[1][index]; 234cb93a386Sopenharmony_ci } 235cb93a386Sopenharmony_ci } 236cb93a386Sopenharmony_ci fPt[0].fX = xIntercept; 237cb93a386Sopenharmony_ci fPt[0].fY = y; 238cb93a386Sopenharmony_ci fUsed = 1; 239cb93a386Sopenharmony_ci } 240cb93a386Sopenharmony_ci } 241cb93a386Sopenharmony_ci if (fAllowNear || result == 2) { 242cb93a386Sopenharmony_ci if ((t = line.nearPoint(leftPt, nullptr)) >= 0) { 243cb93a386Sopenharmony_ci insert(t, (double) flipped, leftPt); 244cb93a386Sopenharmony_ci } 245cb93a386Sopenharmony_ci if (left != right) { 246cb93a386Sopenharmony_ci const SkDPoint rightPt = { right, y }; 247cb93a386Sopenharmony_ci if ((t = line.nearPoint(rightPt, nullptr)) >= 0) { 248cb93a386Sopenharmony_ci insert(t, (double) !flipped, rightPt); 249cb93a386Sopenharmony_ci } 250cb93a386Sopenharmony_ci for (int index = 0; index < 2; ++index) { 251cb93a386Sopenharmony_ci if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) { 252cb93a386Sopenharmony_ci insert((double) index, flipped ? 1 - t : t, line[index]); 253cb93a386Sopenharmony_ci } 254cb93a386Sopenharmony_ci } 255cb93a386Sopenharmony_ci } 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci cleanUpParallelLines(result == 2); 258cb93a386Sopenharmony_ci return fUsed; 259cb93a386Sopenharmony_ci} 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_cistatic int vertical_coincident(const SkDLine& line, double x) { 262cb93a386Sopenharmony_ci double min = line[0].fX; 263cb93a386Sopenharmony_ci double max = line[1].fX; 264cb93a386Sopenharmony_ci if (min > max) { 265cb93a386Sopenharmony_ci using std::swap; 266cb93a386Sopenharmony_ci swap(min, max); 267cb93a386Sopenharmony_ci } 268cb93a386Sopenharmony_ci if (!precisely_between(min, x, max)) { 269cb93a386Sopenharmony_ci return 0; 270cb93a386Sopenharmony_ci } 271cb93a386Sopenharmony_ci if (AlmostEqualUlps(min, max)) { 272cb93a386Sopenharmony_ci return 2; 273cb93a386Sopenharmony_ci } 274cb93a386Sopenharmony_ci return 1; 275cb93a386Sopenharmony_ci} 276cb93a386Sopenharmony_ci 277cb93a386Sopenharmony_cidouble SkIntersections::VerticalIntercept(const SkDLine& line, double x) { 278cb93a386Sopenharmony_ci SkASSERT(line[1].fX != line[0].fX); 279cb93a386Sopenharmony_ci return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX)); 280cb93a386Sopenharmony_ci} 281cb93a386Sopenharmony_ci 282cb93a386Sopenharmony_ciint SkIntersections::vertical(const SkDLine& line, double top, double bottom, 283cb93a386Sopenharmony_ci double x, bool flipped) { 284cb93a386Sopenharmony_ci fMax = 3; // cleanup parallel lines will bring this back line 285cb93a386Sopenharmony_ci // see if end points intersect the opposite line 286cb93a386Sopenharmony_ci double t; 287cb93a386Sopenharmony_ci SkDPoint topPt = { x, top }; 288cb93a386Sopenharmony_ci if ((t = line.exactPoint(topPt)) >= 0) { 289cb93a386Sopenharmony_ci insert(t, (double) flipped, topPt); 290cb93a386Sopenharmony_ci } 291cb93a386Sopenharmony_ci if (top != bottom) { 292cb93a386Sopenharmony_ci SkDPoint bottomPt = { x, bottom }; 293cb93a386Sopenharmony_ci if ((t = line.exactPoint(bottomPt)) >= 0) { 294cb93a386Sopenharmony_ci insert(t, (double) !flipped, bottomPt); 295cb93a386Sopenharmony_ci } 296cb93a386Sopenharmony_ci for (int index = 0; index < 2; ++index) { 297cb93a386Sopenharmony_ci if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) { 298cb93a386Sopenharmony_ci insert((double) index, flipped ? 1 - t : t, line[index]); 299cb93a386Sopenharmony_ci } 300cb93a386Sopenharmony_ci } 301cb93a386Sopenharmony_ci } 302cb93a386Sopenharmony_ci int result = vertical_coincident(line, x); 303cb93a386Sopenharmony_ci if (result == 1 && fUsed == 0) { 304cb93a386Sopenharmony_ci fT[0][0] = VerticalIntercept(line, x); 305cb93a386Sopenharmony_ci double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY); 306cb93a386Sopenharmony_ci if (between(top, yIntercept, bottom)) { 307cb93a386Sopenharmony_ci fT[1][0] = (yIntercept - top) / (bottom - top); 308cb93a386Sopenharmony_ci if (flipped) { 309cb93a386Sopenharmony_ci // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [0].fY 310cb93a386Sopenharmony_ci for (int index = 0; index < result; ++index) { 311cb93a386Sopenharmony_ci fT[1][index] = 1 - fT[1][index]; 312cb93a386Sopenharmony_ci } 313cb93a386Sopenharmony_ci } 314cb93a386Sopenharmony_ci fPt[0].fX = x; 315cb93a386Sopenharmony_ci fPt[0].fY = yIntercept; 316cb93a386Sopenharmony_ci fUsed = 1; 317cb93a386Sopenharmony_ci } 318cb93a386Sopenharmony_ci } 319cb93a386Sopenharmony_ci if (fAllowNear || result == 2) { 320cb93a386Sopenharmony_ci if ((t = line.nearPoint(topPt, nullptr)) >= 0) { 321cb93a386Sopenharmony_ci insert(t, (double) flipped, topPt); 322cb93a386Sopenharmony_ci } 323cb93a386Sopenharmony_ci if (top != bottom) { 324cb93a386Sopenharmony_ci SkDPoint bottomPt = { x, bottom }; 325cb93a386Sopenharmony_ci if ((t = line.nearPoint(bottomPt, nullptr)) >= 0) { 326cb93a386Sopenharmony_ci insert(t, (double) !flipped, bottomPt); 327cb93a386Sopenharmony_ci } 328cb93a386Sopenharmony_ci for (int index = 0; index < 2; ++index) { 329cb93a386Sopenharmony_ci if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) { 330cb93a386Sopenharmony_ci insert((double) index, flipped ? 1 - t : t, line[index]); 331cb93a386Sopenharmony_ci } 332cb93a386Sopenharmony_ci } 333cb93a386Sopenharmony_ci } 334cb93a386Sopenharmony_ci } 335cb93a386Sopenharmony_ci cleanUpParallelLines(result == 2); 336cb93a386Sopenharmony_ci SkASSERT(fUsed <= 2); 337cb93a386Sopenharmony_ci return fUsed; 338cb93a386Sopenharmony_ci} 339cb93a386Sopenharmony_ci 340