michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: #include "SkAddIntersections.h" michael@0: #include "SkPathOpsBounds.h" michael@0: michael@0: #if DEBUG_ADD_INTERSECTING_TS michael@0: michael@0: static void debugShowLineIntersection(int pts, const SkIntersectionHelper& wt, michael@0: const SkIntersectionHelper& wn, const SkIntersections& i) { michael@0: SkASSERT(i.used() == pts); michael@0: if (!pts) { michael@0: SkDebugf("%s no intersect " LINE_DEBUG_STR " " LINE_DEBUG_STR "\n", michael@0: __FUNCTION__, LINE_DEBUG_DATA(wt.pts()), LINE_DEBUG_DATA(wn.pts())); michael@0: return; michael@0: } michael@0: SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " LINE_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, michael@0: i[0][0], LINE_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); michael@0: if (pts == 2) { michael@0: SkDebugf(" " T_DEBUG_STR(wtTs, 1) " " PT_DEBUG_STR, i[0][1], PT_DEBUG_DATA(i, 1)); michael@0: } michael@0: SkDebugf(" wnTs[0]=%g " LINE_DEBUG_STR, i[1][0], LINE_DEBUG_DATA(wn.pts())); michael@0: if (pts == 2) { michael@0: SkDebugf(" " T_DEBUG_STR(wnTs, 1), i[1][1]); michael@0: } michael@0: SkDebugf("\n"); michael@0: } michael@0: michael@0: static void debugShowQuadLineIntersection(int pts, const SkIntersectionHelper& wt, michael@0: const SkIntersectionHelper& wn, michael@0: const SkIntersections& i) { michael@0: SkASSERT(i.used() == pts); michael@0: if (!pts) { michael@0: SkDebugf("%s no intersect " QUAD_DEBUG_STR " " LINE_DEBUG_STR "\n", michael@0: __FUNCTION__, QUAD_DEBUG_DATA(wt.pts()), LINE_DEBUG_DATA(wn.pts())); michael@0: return; michael@0: } michael@0: SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " QUAD_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, michael@0: i[0][0], QUAD_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n)); michael@0: } michael@0: SkDebugf(" wnTs[0]=%g " LINE_DEBUG_STR, i[1][0], LINE_DEBUG_DATA(wn.pts())); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]); michael@0: } michael@0: SkDebugf("\n"); michael@0: } michael@0: michael@0: static void debugShowQuadIntersection(int pts, const SkIntersectionHelper& wt, michael@0: const SkIntersectionHelper& wn, const SkIntersections& i) { michael@0: SkASSERT(i.used() == pts); michael@0: if (!pts) { michael@0: SkDebugf("%s no intersect " QUAD_DEBUG_STR " " QUAD_DEBUG_STR "\n", michael@0: __FUNCTION__, QUAD_DEBUG_DATA(wt.pts()), QUAD_DEBUG_DATA(wn.pts())); michael@0: return; michael@0: } michael@0: SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " QUAD_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, michael@0: i[0][0], QUAD_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n)); michael@0: } michael@0: SkDebugf(" wnTs[0]=%g " QUAD_DEBUG_STR, i[1][0], QUAD_DEBUG_DATA(wn.pts())); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]); michael@0: } michael@0: SkDebugf("\n"); michael@0: } michael@0: michael@0: static void debugShowCubicLineIntersection(int pts, const SkIntersectionHelper& wt, michael@0: const SkIntersectionHelper& wn, const SkIntersections& i) { michael@0: SkASSERT(i.used() == pts); michael@0: if (!pts) { michael@0: SkDebugf("%s no intersect " CUBIC_DEBUG_STR " " LINE_DEBUG_STR "\n", michael@0: __FUNCTION__, CUBIC_DEBUG_DATA(wt.pts()), LINE_DEBUG_DATA(wn.pts())); michael@0: return; michael@0: } michael@0: SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, michael@0: i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n)); michael@0: } michael@0: SkDebugf(" wnTs[0]=%g " LINE_DEBUG_STR, i[1][0], LINE_DEBUG_DATA(wn.pts())); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]); michael@0: } michael@0: SkDebugf("\n"); michael@0: } michael@0: michael@0: static void debugShowCubicQuadIntersection(int pts, const SkIntersectionHelper& wt, michael@0: const SkIntersectionHelper& wn, const SkIntersections& i) { michael@0: SkASSERT(i.used() == pts); michael@0: if (!pts) { michael@0: SkDebugf("%s no intersect " CUBIC_DEBUG_STR " " QUAD_DEBUG_STR "\n", michael@0: __FUNCTION__, CUBIC_DEBUG_DATA(wt.pts()), QUAD_DEBUG_DATA(wn.pts())); michael@0: return; michael@0: } michael@0: SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, michael@0: i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n)); michael@0: } michael@0: SkDebugf(" wnTs[0]=%g " QUAD_DEBUG_STR, i[1][0], QUAD_DEBUG_DATA(wn.pts())); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]); michael@0: } michael@0: SkDebugf("\n"); michael@0: } michael@0: michael@0: static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt, michael@0: const SkIntersectionHelper& wn, const SkIntersections& i) { michael@0: SkASSERT(i.used() == pts); michael@0: if (!pts) { michael@0: SkDebugf("%s no intersect " CUBIC_DEBUG_STR " " CUBIC_DEBUG_STR "\n", michael@0: __FUNCTION__, CUBIC_DEBUG_DATA(wt.pts()), CUBIC_DEBUG_DATA(wn.pts())); michael@0: return; michael@0: } michael@0: SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, michael@0: i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n)); michael@0: } michael@0: SkDebugf(" wnTs[0]=%g " CUBIC_DEBUG_STR, i[1][0], CUBIC_DEBUG_DATA(wn.pts())); michael@0: for (int n = 1; n < pts; ++n) { michael@0: SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]); michael@0: } michael@0: SkDebugf("\n"); michael@0: } michael@0: michael@0: static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt, michael@0: const SkIntersections& i) { michael@0: SkASSERT(i.used() == pts); michael@0: if (!pts) { michael@0: SkDebugf("%s no self intersect " CUBIC_DEBUG_STR "\n", __FUNCTION__, michael@0: CUBIC_DEBUG_DATA(wt.pts())); michael@0: return; michael@0: } michael@0: SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, michael@0: i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); michael@0: SkDebugf(" " T_DEBUG_STR(wtTs, 1), i[1][0]); michael@0: SkDebugf("\n"); michael@0: } michael@0: michael@0: #else michael@0: static void debugShowLineIntersection(int , const SkIntersectionHelper& , michael@0: const SkIntersectionHelper& , const SkIntersections& ) { michael@0: } michael@0: michael@0: static void debugShowQuadLineIntersection(int , const SkIntersectionHelper& , michael@0: const SkIntersectionHelper& , const SkIntersections& ) { michael@0: } michael@0: michael@0: static void debugShowQuadIntersection(int , const SkIntersectionHelper& , michael@0: const SkIntersectionHelper& , const SkIntersections& ) { michael@0: } michael@0: michael@0: static void debugShowCubicLineIntersection(int , const SkIntersectionHelper& , michael@0: const SkIntersectionHelper& , const SkIntersections& ) { michael@0: } michael@0: michael@0: static void debugShowCubicQuadIntersection(int , const SkIntersectionHelper& , michael@0: const SkIntersectionHelper& , const SkIntersections& ) { michael@0: } michael@0: michael@0: static void debugShowCubicIntersection(int , const SkIntersectionHelper& , michael@0: const SkIntersectionHelper& , const SkIntersections& ) { michael@0: } michael@0: michael@0: static void debugShowCubicIntersection(int , const SkIntersectionHelper& , michael@0: const SkIntersections& ) { michael@0: } michael@0: #endif michael@0: michael@0: bool AddIntersectTs(SkOpContour* test, SkOpContour* next) { michael@0: if (test != next) { michael@0: if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) { michael@0: return false; michael@0: } michael@0: // OPTIMIZATION: outset contour bounds a smidgen instead? michael@0: if (!SkPathOpsBounds::Intersects(test->bounds(), next->bounds())) { michael@0: return true; michael@0: } michael@0: } michael@0: SkIntersectionHelper wt; michael@0: wt.init(test); michael@0: bool foundCommonContour = test == next; michael@0: do { michael@0: SkIntersectionHelper wn; michael@0: wn.init(next); michael@0: if (test == next && !wn.startAfter(wt)) { michael@0: continue; michael@0: } michael@0: do { michael@0: if (!SkPathOpsBounds::Intersects(wt.bounds(), wn.bounds())) { michael@0: continue; michael@0: } michael@0: int pts = 0; michael@0: SkIntersections ts; michael@0: bool swap = false; michael@0: switch (wt.segmentType()) { michael@0: case SkIntersectionHelper::kHorizontalLine_Segment: michael@0: swap = true; michael@0: switch (wn.segmentType()) { michael@0: case SkIntersectionHelper::kHorizontalLine_Segment: michael@0: case SkIntersectionHelper::kVerticalLine_Segment: michael@0: case SkIntersectionHelper::kLine_Segment: { michael@0: pts = ts.lineHorizontal(wn.pts(), wt.left(), michael@0: wt.right(), wt.y(), wt.xFlipped()); michael@0: debugShowLineIntersection(pts, wn, wt, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kQuad_Segment: { michael@0: pts = ts.quadHorizontal(wn.pts(), wt.left(), michael@0: wt.right(), wt.y(), wt.xFlipped()); michael@0: debugShowQuadLineIntersection(pts, wn, wt, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kCubic_Segment: { michael@0: pts = ts.cubicHorizontal(wn.pts(), wt.left(), michael@0: wt.right(), wt.y(), wt.xFlipped()); michael@0: debugShowCubicLineIntersection(pts, wn, wt, ts); michael@0: break; michael@0: } michael@0: default: michael@0: SkASSERT(0); michael@0: } michael@0: break; michael@0: case SkIntersectionHelper::kVerticalLine_Segment: michael@0: swap = true; michael@0: switch (wn.segmentType()) { michael@0: case SkIntersectionHelper::kHorizontalLine_Segment: michael@0: case SkIntersectionHelper::kVerticalLine_Segment: michael@0: case SkIntersectionHelper::kLine_Segment: { michael@0: pts = ts.lineVertical(wn.pts(), wt.top(), michael@0: wt.bottom(), wt.x(), wt.yFlipped()); michael@0: debugShowLineIntersection(pts, wn, wt, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kQuad_Segment: { michael@0: pts = ts.quadVertical(wn.pts(), wt.top(), michael@0: wt.bottom(), wt.x(), wt.yFlipped()); michael@0: debugShowQuadLineIntersection(pts, wn, wt, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kCubic_Segment: { michael@0: pts = ts.cubicVertical(wn.pts(), wt.top(), michael@0: wt.bottom(), wt.x(), wt.yFlipped()); michael@0: debugShowCubicLineIntersection(pts, wn, wt, ts); michael@0: break; michael@0: } michael@0: default: michael@0: SkASSERT(0); michael@0: } michael@0: break; michael@0: case SkIntersectionHelper::kLine_Segment: michael@0: switch (wn.segmentType()) { michael@0: case SkIntersectionHelper::kHorizontalLine_Segment: michael@0: pts = ts.lineHorizontal(wt.pts(), wn.left(), michael@0: wn.right(), wn.y(), wn.xFlipped()); michael@0: debugShowLineIntersection(pts, wt, wn, ts); michael@0: break; michael@0: case SkIntersectionHelper::kVerticalLine_Segment: michael@0: pts = ts.lineVertical(wt.pts(), wn.top(), michael@0: wn.bottom(), wn.x(), wn.yFlipped()); michael@0: debugShowLineIntersection(pts, wt, wn, ts); michael@0: break; michael@0: case SkIntersectionHelper::kLine_Segment: { michael@0: pts = ts.lineLine(wt.pts(), wn.pts()); michael@0: debugShowLineIntersection(pts, wt, wn, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kQuad_Segment: { michael@0: swap = true; michael@0: pts = ts.quadLine(wn.pts(), wt.pts()); michael@0: debugShowQuadLineIntersection(pts, wn, wt, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kCubic_Segment: { michael@0: swap = true; michael@0: pts = ts.cubicLine(wn.pts(), wt.pts()); michael@0: debugShowCubicLineIntersection(pts, wn, wt, ts); michael@0: break; michael@0: } michael@0: default: michael@0: SkASSERT(0); michael@0: } michael@0: break; michael@0: case SkIntersectionHelper::kQuad_Segment: michael@0: switch (wn.segmentType()) { michael@0: case SkIntersectionHelper::kHorizontalLine_Segment: michael@0: pts = ts.quadHorizontal(wt.pts(), wn.left(), michael@0: wn.right(), wn.y(), wn.xFlipped()); michael@0: debugShowQuadLineIntersection(pts, wt, wn, ts); michael@0: break; michael@0: case SkIntersectionHelper::kVerticalLine_Segment: michael@0: pts = ts.quadVertical(wt.pts(), wn.top(), michael@0: wn.bottom(), wn.x(), wn.yFlipped()); michael@0: debugShowQuadLineIntersection(pts, wt, wn, ts); michael@0: break; michael@0: case SkIntersectionHelper::kLine_Segment: { michael@0: pts = ts.quadLine(wt.pts(), wn.pts()); michael@0: debugShowQuadLineIntersection(pts, wt, wn, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kQuad_Segment: { michael@0: pts = ts.quadQuad(wt.pts(), wn.pts()); michael@0: debugShowQuadIntersection(pts, wt, wn, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kCubic_Segment: { michael@0: swap = true; michael@0: pts = ts.cubicQuad(wn.pts(), wt.pts()); michael@0: debugShowCubicQuadIntersection(pts, wn, wt, ts); michael@0: break; michael@0: } michael@0: default: michael@0: SkASSERT(0); michael@0: } michael@0: break; michael@0: case SkIntersectionHelper::kCubic_Segment: michael@0: switch (wn.segmentType()) { michael@0: case SkIntersectionHelper::kHorizontalLine_Segment: michael@0: pts = ts.cubicHorizontal(wt.pts(), wn.left(), michael@0: wn.right(), wn.y(), wn.xFlipped()); michael@0: debugShowCubicLineIntersection(pts, wt, wn, ts); michael@0: break; michael@0: case SkIntersectionHelper::kVerticalLine_Segment: michael@0: pts = ts.cubicVertical(wt.pts(), wn.top(), michael@0: wn.bottom(), wn.x(), wn.yFlipped()); michael@0: debugShowCubicLineIntersection(pts, wt, wn, ts); michael@0: break; michael@0: case SkIntersectionHelper::kLine_Segment: { michael@0: pts = ts.cubicLine(wt.pts(), wn.pts()); michael@0: debugShowCubicLineIntersection(pts, wt, wn, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kQuad_Segment: { michael@0: pts = ts.cubicQuad(wt.pts(), wn.pts()); michael@0: debugShowCubicQuadIntersection(pts, wt, wn, ts); michael@0: break; michael@0: } michael@0: case SkIntersectionHelper::kCubic_Segment: { michael@0: pts = ts.cubicCubic(wt.pts(), wn.pts()); michael@0: debugShowCubicIntersection(pts, wt, wn, ts); michael@0: break; michael@0: } michael@0: default: michael@0: SkASSERT(0); michael@0: } michael@0: break; michael@0: default: michael@0: SkASSERT(0); michael@0: } michael@0: if (!foundCommonContour && pts > 0) { michael@0: test->addCross(next); michael@0: next->addCross(test); michael@0: foundCommonContour = true; michael@0: } michael@0: // in addition to recording T values, record matching segment michael@0: if (pts == 2) { michael@0: if (wn.segmentType() <= SkIntersectionHelper::kLine_Segment michael@0: && wt.segmentType() <= SkIntersectionHelper::kLine_Segment) { michael@0: if (wt.addCoincident(wn, ts, swap)) { michael@0: continue; michael@0: } michael@0: ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1) michael@0: pts = 1; michael@0: } else if (wn.segmentType() >= SkIntersectionHelper::kQuad_Segment michael@0: && wt.segmentType() >= SkIntersectionHelper::kQuad_Segment michael@0: && ts.isCoincident(0)) { michael@0: SkASSERT(ts.coincidentUsed() == 2); michael@0: if (wt.addCoincident(wn, ts, swap)) { michael@0: continue; michael@0: } michael@0: ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1) michael@0: pts = 1; michael@0: } michael@0: } michael@0: if (pts >= 2) { michael@0: for (int pt = 0; pt < pts - 1; ++pt) { michael@0: const SkDPoint& point = ts.pt(pt); michael@0: const SkDPoint& next = ts.pt(pt + 1); michael@0: if (wt.isPartial(ts[swap][pt], ts[swap][pt + 1], point, next) michael@0: && wn.isPartial(ts[!swap][pt], ts[!swap][pt + 1], point, next)) { michael@0: if (!wt.addPartialCoincident(wn, ts, pt, swap)) { michael@0: // remove extra point if two map to same float values michael@0: ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1) michael@0: pts = 1; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: for (int pt = 0; pt < pts; ++pt) { michael@0: SkASSERT(ts[0][pt] >= 0 && ts[0][pt] <= 1); michael@0: SkASSERT(ts[1][pt] >= 0 && ts[1][pt] <= 1); michael@0: SkPoint point = ts.pt(pt).asSkPoint(); michael@0: int testTAt = wt.addT(wn, point, ts[swap][pt]); michael@0: int nextTAt = wn.addT(wt, point, ts[!swap][pt]); michael@0: wt.addOtherT(testTAt, ts[!swap][pt], nextTAt); michael@0: wn.addOtherT(nextTAt, ts[swap][pt], testTAt); michael@0: } michael@0: } while (wn.advance()); michael@0: } while (wt.advance()); michael@0: return true; michael@0: } michael@0: michael@0: void AddSelfIntersectTs(SkOpContour* test) { michael@0: SkIntersectionHelper wt; michael@0: wt.init(test); michael@0: do { michael@0: if (wt.segmentType() != SkIntersectionHelper::kCubic_Segment) { michael@0: continue; michael@0: } michael@0: SkIntersections ts; michael@0: int pts = ts.cubic(wt.pts()); michael@0: debugShowCubicIntersection(pts, wt, ts); michael@0: if (!pts) { michael@0: continue; michael@0: } michael@0: SkASSERT(pts == 1); michael@0: SkASSERT(ts[0][0] >= 0 && ts[0][0] <= 1); michael@0: SkASSERT(ts[1][0] >= 0 && ts[1][0] <= 1); michael@0: SkPoint point = ts.pt(0).asSkPoint(); michael@0: int testTAt = wt.addSelfT(wt, point, ts[0][0]); michael@0: int nextTAt = wt.addT(wt, point, ts[1][0]); michael@0: wt.addOtherT(testTAt, ts[1][0], nextTAt); michael@0: wt.addOtherT(nextTAt, ts[0][0], testTAt); michael@0: } while (wt.advance()); michael@0: } michael@0: michael@0: // resolve any coincident pairs found while intersecting, and michael@0: // see if coincidence is formed by clipping non-concident segments michael@0: void CoincidenceCheck(SkTArray* contourList, int total) { michael@0: int contourCount = (*contourList).count(); michael@0: for (int cIndex = 0; cIndex < contourCount; ++cIndex) { michael@0: SkOpContour* contour = (*contourList)[cIndex]; michael@0: contour->addCoincidentPoints(); michael@0: } michael@0: for (int cIndex = 0; cIndex < contourCount; ++cIndex) { michael@0: SkOpContour* contour = (*contourList)[cIndex]; michael@0: contour->calcCoincidentWinding(); michael@0: } michael@0: for (int cIndex = 0; cIndex < contourCount; ++cIndex) { michael@0: SkOpContour* contour = (*contourList)[cIndex]; michael@0: contour->calcPartialCoincidentWinding(); michael@0: } michael@0: }