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