| /* | |
| * Copyright 2012 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef SkIntersections_DEFINE | |
| #define SkIntersections_DEFINE | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkTypes.h" | |
| #include "include/private/base/SkDebug.h" | |
| #include "include/private/base/SkMalloc.h" | |
| #include "src/pathops/SkPathOpsConic.h" | |
| #include "src/pathops/SkPathOpsCubic.h" | |
| #include "src/pathops/SkPathOpsDebug.h" | |
| #include "src/pathops/SkPathOpsLine.h" | |
| #include "src/pathops/SkPathOpsPoint.h" | |
| #include "src/pathops/SkPathOpsQuad.h" | |
| #include "src/pathops/SkPathOpsTCurve.h" | |
| #include "src/pathops/SkPathOpsTypes.h" | |
| #include <array> | |
| #include <cstdint> | |
| struct SkDRect; | |
| class SkIntersections { | |
| public: | |
| SkIntersections(SkDEBUGCODE(SkOpGlobalState* globalState = nullptr)) | |
| : fSwap(0) | |
| #ifdef SK_DEBUG | |
| SkDEBUGPARAMS(fDebugGlobalState(globalState)) | |
| , fDepth(0) | |
| #endif | |
| { | |
| sk_bzero(fPt, sizeof(fPt)); | |
| sk_bzero(fPt2, sizeof(fPt2)); | |
| sk_bzero(fT, sizeof(fT)); | |
| sk_bzero(fNearlySame, sizeof(fNearlySame)); | |
| #if DEBUG_T_SECT_LOOP_COUNT | |
| sk_bzero(fDebugLoopCount, sizeof(fDebugLoopCount)); | |
| #endif | |
| reset(); | |
| fMax = 0; // require that the caller set the max | |
| } | |
| class TArray { | |
| public: | |
| explicit TArray(const double ts[10]) : fTArray(ts) {} | |
| double operator[](int n) const { | |
| return fTArray[n]; | |
| } | |
| const double* fTArray; | |
| }; | |
| TArray operator[](int n) const { return TArray(fT[n]); } | |
| void allowNear(bool nearAllowed) { | |
| fAllowNear = nearAllowed; | |
| } | |
| void clearCoincidence(int index) { | |
| SkASSERT(index >= 0); | |
| int bit = 1 << index; | |
| fIsCoincident[0] &= ~bit; | |
| fIsCoincident[1] &= ~bit; | |
| } | |
| int conicHorizontal(const SkPoint a[3], SkScalar weight, SkScalar left, SkScalar right, | |
| SkScalar y, bool flipped) { | |
| SkDConic conic; | |
| conic.set(a, weight); | |
| fMax = 2; | |
| return horizontal(conic, left, right, y, flipped); | |
| } | |
| int conicVertical(const SkPoint a[3], SkScalar weight, SkScalar top, SkScalar bottom, | |
| SkScalar x, bool flipped) { | |
| SkDConic conic; | |
| conic.set(a, weight); | |
| fMax = 2; | |
| return vertical(conic, top, bottom, x, flipped); | |
| } | |
| int conicLine(const SkPoint a[3], SkScalar weight, const SkPoint b[2]) { | |
| SkDConic conic; | |
| conic.set(a, weight); | |
| SkDLine line; | |
| line.set(b); | |
| fMax = 3; // 2; permit small coincident segment + non-coincident intersection | |
| return intersect(conic, line); | |
| } | |
| int cubicHorizontal(const SkPoint a[4], SkScalar left, SkScalar right, SkScalar y, | |
| bool flipped) { | |
| SkDCubic cubic; | |
| cubic.set(a); | |
| fMax = 3; | |
| return horizontal(cubic, left, right, y, flipped); | |
| } | |
| int cubicVertical(const SkPoint a[4], SkScalar top, SkScalar bottom, SkScalar x, bool flipped) { | |
| SkDCubic cubic; | |
| cubic.set(a); | |
| fMax = 3; | |
| return vertical(cubic, top, bottom, x, flipped); | |
| } | |
| int cubicLine(const SkPoint a[4], const SkPoint b[2]) { | |
| SkDCubic cubic; | |
| cubic.set(a); | |
| SkDLine line; | |
| line.set(b); | |
| fMax = 3; | |
| return intersect(cubic, line); | |
| } | |
| #ifdef SK_DEBUG | |
| SkOpGlobalState* globalState() const { return fDebugGlobalState; } | |
| #endif | |
| bool hasT(double t) const { | |
| SkASSERT(t == 0 || t == 1); | |
| return fUsed > 0 && (t == 0 ? fT[0][0] == 0 : fT[0][fUsed - 1] == 1); | |
| } | |
| bool hasOppT(double t) const { | |
| SkASSERT(t == 0 || t == 1); | |
| return fUsed > 0 && (fT[1][0] == t || fT[1][fUsed - 1] == t); | |
| } | |
| int insertSwap(double one, double two, const SkDPoint& pt) { | |
| if (fSwap) { | |
| return insert(two, one, pt); | |
| } else { | |
| return insert(one, two, pt); | |
| } | |
| } | |
| bool isCoincident(int index) { | |
| return (fIsCoincident[0] & 1 << index) != 0; | |
| } | |
| int lineHorizontal(const SkPoint a[2], SkScalar left, SkScalar right, SkScalar y, | |
| bool flipped) { | |
| SkDLine line; | |
| line.set(a); | |
| fMax = 2; | |
| return horizontal(line, left, right, y, flipped); | |
| } | |
| int lineVertical(const SkPoint a[2], SkScalar top, SkScalar bottom, SkScalar x, bool flipped) { | |
| SkDLine line; | |
| line.set(a); | |
| fMax = 2; | |
| return vertical(line, top, bottom, x, flipped); | |
| } | |
| int lineLine(const SkPoint a[2], const SkPoint b[2]) { | |
| SkDLine aLine, bLine; | |
| aLine.set(a); | |
| bLine.set(b); | |
| fMax = 2; | |
| return intersect(aLine, bLine); | |
| } | |
| bool nearlySame(int index) const { | |
| SkASSERT(index == 0 || index == 1); | |
| return fNearlySame[index]; | |
| } | |
| const SkDPoint& pt(int index) const { | |
| return fPt[index]; | |
| } | |
| const SkDPoint& pt2(int index) const { | |
| return fPt2[index]; | |
| } | |
| int quadHorizontal(const SkPoint a[3], SkScalar left, SkScalar right, SkScalar y, | |
| bool flipped) { | |
| SkDQuad quad; | |
| quad.set(a); | |
| fMax = 2; | |
| return horizontal(quad, left, right, y, flipped); | |
| } | |
| int quadVertical(const SkPoint a[3], SkScalar top, SkScalar bottom, SkScalar x, bool flipped) { | |
| SkDQuad quad; | |
| quad.set(a); | |
| fMax = 2; | |
| return vertical(quad, top, bottom, x, flipped); | |
| } | |
| int quadLine(const SkPoint a[3], const SkPoint b[2]) { | |
| SkDQuad quad; | |
| quad.set(a); | |
| SkDLine line; | |
| line.set(b); | |
| return intersect(quad, line); | |
| } | |
| // leaves swap, max alone | |
| void reset() { | |
| fAllowNear = true; | |
| fUsed = 0; | |
| sk_bzero(fIsCoincident, sizeof(fIsCoincident)); | |
| } | |
| void set(bool swap, int tIndex, double t) { | |
| fT[(int) swap][tIndex] = t; | |
| } | |
| void setMax(int max) { | |
| SkASSERT(max <= (int) std::size(fPt)); | |
| fMax = max; | |
| } | |
| void swap() { | |
| fSwap ^= true; | |
| } | |
| bool swapped() const { | |
| return fSwap; | |
| } | |
| int used() const { | |
| return fUsed; | |
| } | |
| void downDepth() { | |
| SkASSERT(--fDepth >= 0); | |
| } | |
| bool unBumpT(int index) { | |
| SkASSERT(fUsed == 1); | |
| fT[0][index] = fT[0][index] * (1 + BUMP_EPSILON * 2) - BUMP_EPSILON; | |
| if (!between(0, fT[0][index], 1)) { | |
| fUsed = 0; | |
| return false; | |
| } | |
| return true; | |
| } | |
| void upDepth() { | |
| SkASSERT(++fDepth < 16); | |
| } | |
| void alignQuadPts(const SkPoint a[3], const SkPoint b[3]); | |
| int cleanUpCoincidence(); | |
| int closestTo(double rangeStart, double rangeEnd, const SkDPoint& testPt, double* dist) const; | |
| void cubicInsert(double one, double two, const SkDPoint& pt, const SkDCubic& c1, | |
| const SkDCubic& c2); | |
| void flip(); | |
| int horizontal(const SkDLine&, double left, double right, double y, bool flipped); | |
| int horizontal(const SkDQuad&, double left, double right, double y, bool flipped); | |
| int horizontal(const SkDQuad&, double left, double right, double y, double tRange[2]); | |
| int horizontal(const SkDCubic&, double y, double tRange[3]); | |
| int horizontal(const SkDConic&, double left, double right, double y, bool flipped); | |
| int horizontal(const SkDCubic&, double left, double right, double y, bool flipped); | |
| int horizontal(const SkDCubic&, double left, double right, double y, double tRange[3]); | |
| static double HorizontalIntercept(const SkDLine& line, double y); | |
| static int HorizontalIntercept(const SkDQuad& quad, SkScalar y, double* roots); | |
| static int HorizontalIntercept(const SkDConic& conic, SkScalar y, double* roots); | |
| // FIXME : does not respect swap | |
| int insert(double one, double two, const SkDPoint& pt); | |
| void insertNear(double one, double two, const SkDPoint& pt1, const SkDPoint& pt2); | |
| // start if index == 0 : end if index == 1 | |
| int insertCoincident(double one, double two, const SkDPoint& pt); | |
| int intersect(const SkDLine&, const SkDLine&); | |
| int intersect(const SkDQuad&, const SkDLine&); | |
| int intersect(const SkDQuad&, const SkDQuad&); | |
| int intersect(const SkDConic&, const SkDLine&); | |
| int intersect(const SkDConic&, const SkDQuad&); | |
| int intersect(const SkDConic&, const SkDConic&); | |
| int intersect(const SkDCubic&, const SkDLine&); | |
| int intersect(const SkDCubic&, const SkDQuad&); | |
| int intersect(const SkDCubic&, const SkDConic&); | |
| int intersect(const SkDCubic&, const SkDCubic&); | |
| int intersectRay(const SkDLine&, const SkDLine&); | |
| int intersectRay(const SkDQuad&, const SkDLine&); | |
| int intersectRay(const SkDConic&, const SkDLine&); | |
| int intersectRay(const SkDCubic&, const SkDLine&); | |
| int intersectRay(const SkTCurve& tCurve, const SkDLine& line) { | |
| return tCurve.intersectRay(this, line); | |
| } | |
| void merge(const SkIntersections& , int , const SkIntersections& , int ); | |
| int mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const; | |
| void removeOne(int index); | |
| void setCoincident(int index); | |
| int vertical(const SkDLine&, double top, double bottom, double x, bool flipped); | |
| int vertical(const SkDQuad&, double top, double bottom, double x, bool flipped); | |
| int vertical(const SkDConic&, double top, double bottom, double x, bool flipped); | |
| int vertical(const SkDCubic&, double top, double bottom, double x, bool flipped); | |
| static double VerticalIntercept(const SkDLine& line, double x); | |
| static int VerticalIntercept(const SkDQuad& quad, SkScalar x, double* roots); | |
| static int VerticalIntercept(const SkDConic& conic, SkScalar x, double* roots); | |
| int depth() const { | |
| #ifdef SK_DEBUG | |
| return fDepth; | |
| #else | |
| return 0; | |
| #endif | |
| } | |
| enum DebugLoop { | |
| kIterations_DebugLoop, | |
| kCoinCheck_DebugLoop, | |
| kComputePerp_DebugLoop, | |
| }; | |
| void debugBumpLoopCount(DebugLoop ); | |
| int debugCoincidentUsed() const; | |
| int debugLoopCount(DebugLoop ) const; | |
| void debugResetLoopCount(); | |
| void dump() const; // implemented for testing only | |
| private: | |
| bool cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2); | |
| bool cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2); | |
| void cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2, const SkDRect& ); | |
| void cleanUpParallelLines(bool parallel); | |
| void computePoints(const SkDLine& line, int used); | |
| SkDPoint fPt[13]; // FIXME: since scans store points as SkPoint, this should also | |
| SkDPoint fPt2[2]; // used by nearly same to store alternate intersection point | |
| double fT[2][13]; | |
| uint16_t fIsCoincident[2]; // bit set for each curve's coincident T | |
| bool fNearlySame[2]; // true if end points nearly match | |
| unsigned char fUsed; | |
| unsigned char fMax; | |
| bool fAllowNear; | |
| bool fSwap; | |
| #ifdef SK_DEBUG | |
| SkOpGlobalState* fDebugGlobalState; | |
| int fDepth; | |
| #endif | |
| #if DEBUG_T_SECT_LOOP_COUNT | |
| int fDebugLoopCount[3]; | |
| #endif | |
| }; | |
| #endif |