shape ops work in progress

M    Intersection/DataTypes.cpp
M    Intersection/QuadraticIntersection_Test.cpp
M    Intersection/EdgeWalker.cpp
M    Intersection/LineQuadraticIntersection_Test.cpp
M    Intersection/LineIntersection_Test.cpp
M    Intersection/LineIntersection.cpp
D    Intersection/edge.xcodeproj
M    Intersection/SimplifyFindTop_Test.cpp
M    Intersection/DataTypes.h
A    Intersection/SimplifyRect4x4_Test.cpp
M    Intersection/CubicIntersection_Test.cpp
M    Intersection/QuadraticUtilities.h
M    Intersection/LineCubicIntersection_Test.cpp
A    Intersection/CurveUtilities.h
M    Intersection/QuadraticBezierClip.cpp
M    Intersection/QuadraticBounds.cpp
M    Intersection/LineUtilities.h
M    Intersection/Intersection_Tests.cpp
M    Intersection/Simplify.cpp
M    Intersection/EdgeWalker_TestUtility.cpp
M    Intersection/QuadraticUtilities.cpp
M    Intersection/thingsToDo.txt
M    Intersection/LineUtilities.cpp
M    Intersection/CubicUtilities.h
M    Intersection/SimplifyFindNext_Test.cpp
M    Intersection/Intersection_Tests.h
M    Intersection/CubicBezierClip.cpp
M    Intersection/ActiveEdge_Test.cpp
M    Intersection/CubicBounds.cpp
M    Intersection/Simplify.h
M    Intersection/SimplifyNew_Test.cpp
M    Intersection/EdgeWalker_Test.h
M    Intersection/CubicUtilities.cpp
M    Intersection/op.htm
M    Intersection/ConvexHull.cpp
D    Intersection/RectUtilities.cpp
M    Intersection/SimplifyAddIntersectingTs_Test.cpp



git-svn-id: http://skia.googlecode.com/svn/trunk@4429 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
caryclark@google.com 2012-07-02 20:27:02 +00:00
parent 0985c558fc
commit 8dcf114db9
37 changed files with 2144 additions and 1767 deletions

View File

@ -1,12 +1,4 @@
#include "CurveIntersection.h"
#include "Intersections.h"
#include "LineIntersection.h"
#include "SkPath.h"
#include "SkRect.h"
#include "SkTArray.h"
#include "SkTDArray.h"
#include "ShapeOps.h"
#include "TSearch.h"
#include "Simplify.h"
namespace UnitTest {

View File

@ -1,4 +1,5 @@
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "IntersectionUtilities.h"
/* Given a cubic, find the convex hull described by the end and control points.

View File

@ -1,4 +1,5 @@
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "LineParameters.h"
#include <algorithm> // used for std::swap

View File

@ -1,4 +1,5 @@
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Extrema.h"
static int isBoundedByEndPoints(double a, double b, double c, double d)

View File

@ -1,4 +1,5 @@
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "CubicIntersection_TestData.h"
#include "Intersection_Tests.h"
#include "Intersections.h"

View File

@ -74,3 +74,75 @@ int cubicRoots(double A, double B, double C, double D, double t[3]) {
}
return (int)(roots - t);
}
// from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf
// c(t) = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3
// c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2
// = 3(b-a)(1-t)^2 + 6(c-b)t(1-t) + 3(d-c)t^2
double derivativeAtT(const double* cubic, double t) {
double one_t = 1 - t;
double a = cubic[0];
double b = cubic[2];
double c = cubic[4];
double d = cubic[6];
return (b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t;
}
// same as derivativeAtT
// which is more accurate? which is faster?
double derivativeAtT_2(const double* cubic, double t) {
double a = cubic[2] - cubic[0];
double b = cubic[4] - 2 * cubic[2] + cubic[0];
double c = cubic[6] + 3 * (cubic[2] - cubic[4]) - cubic[0];
return c * c * t * t + 2 * b * t + a;
}
void dxdy_at_t(const Cubic& cubic, double t, double& dx, double& dy) {
if (&dx) {
dx = derivativeAtT(&cubic[0].x, t);
}
if (&dy) {
dy = derivativeAtT(&cubic[0].y, t);
}
}
bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath) {
double dy = cubic[index].y - cubic[zero].y;
double dx = cubic[index].x - cubic[zero].x;
if (approximately_equal(dy, 0)) {
if (approximately_equal(dx, 0)) {
return false;
}
memcpy(rotPath, cubic, sizeof(Cubic));
return true;
}
for (int index = 0; index < 4; ++index) {
rotPath[index].x = cubic[index].x * dx + cubic[index].y * dy;
rotPath[index].y = cubic[index].y * dx - cubic[index].x * dy;
}
return true;
}
double secondDerivativeAtT(const double* cubic, double t) {
double a = cubic[0];
double b = cubic[2];
double c = cubic[4];
double d = cubic[6];
return (c - 2 * b + a) * (1 - t) + (d - 2 * c + b) * t;
}
void xy_at_t(const Cubic& cubic, double t, double& x, double& y) {
double one_t = 1 - t;
double one_t2 = one_t * one_t;
double a = one_t2 * one_t;
double b = 3 * one_t2 * t;
double t2 = t * t;
double c = 3 * one_t * t2;
double d = t2 * t;
if (&x) {
x = a * cubic[0].x + b * cubic[1].x + c * cubic[2].x + d * cubic[3].x;
}
if (&y) {
y = a * cubic[0].y + b * cubic[1].y + c * cubic[2].y + d * cubic[3].y;
}
}

View File

@ -1,3 +1,12 @@
#include "DataTypes.h"
double cube_root(double x);
void coefficients(const double* cubic, double& A, double& B, double& C, double& D);
int cubicRoots(double A, double B, double C, double D, double t[3]);
double derivativeAtT(const double* cubic, double t);
// competing version that should produce same results
double derivativeAtT_2(const double* cubic, double t);
void dxdy_at_t(const Cubic& , double t, double& x, double& y);
bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath);
double secondDerivativeAtT(const double* cubic, double t);
void xy_at_t(const Cubic& , double t, double& x, double& y);

View File

@ -0,0 +1,15 @@
/*
* 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 CurveUtilities_DEFINE
#define CurveUtilities_DEFINE
#include "CubicUtilities.h"
#include "LineUtilities.h"
#include "QuadraticUtilities.h"
#endif

View File

@ -21,115 +21,6 @@ const double SquaredEpsilon = PointEpsilon * PointEpsilon;
const int UlpsEpsilon = 16;
bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath) {
double dy = cubic[index].y - cubic[zero].y;
double dx = cubic[index].x - cubic[zero].x;
if (approximately_equal(dy, 0)) {
if (approximately_equal(dx, 0)) {
return false;
}
memcpy(rotPath, cubic, sizeof(Cubic));
return true;
}
for (int index = 0; index < 4; ++index) {
rotPath[index].x = cubic[index].x * dx + cubic[index].y * dy;
rotPath[index].y = cubic[index].y * dx - cubic[index].x * dy;
}
return true;
}
double t_at(const _Line& line, const _Point& pt) {
double dx = line[1].x - line[0].x;
double dy = line[1].y - line[0].y;
if (fabs(dx) > fabs(dy)) {
if (approximately_zero(dx)) {
return 0;
}
return (pt.x - line[0].x) / dx;
}
if (approximately_zero(dy)) {
return 0;
}
return (pt.y - line[0].y) / dy;
}
static void setMinMax(double x, int flags, double& minX, double& maxX) {
if (minX > x && (flags & (kFindTopMin | kFindBottomMin))) {
minX = x;
}
if (maxX < x && (flags & (kFindTopMax | kFindBottomMax))) {
maxX = x;
}
}
void x_at(const _Point& p1, const _Point& p2, double top, double bottom,
int flags, double& minX, double& maxX) {
if (approximately_equal(p1.y, p2.y)) {
// It should be OK to bail early in this case. There's another edge
// which shares this end point which can intersect without failing to
// have a slope ... maybe
return;
}
// p2.x is always greater than p1.x -- the part of points (p1, p2) are
// moving from the start of the cubic towards its end.
// if p1.y < p2.y, minX can be affected
// if p1.y > p2.y, maxX can be affected
double slope = (p2.x - p1.x) / (p2.y - p1.y);
int topFlags = flags & (kFindTopMin | kFindTopMax);
if (topFlags && (top <= p1.y && top >= p2.y
|| top >= p1.y && top <= p2.y)) {
double x = p1.x + (top - p1.y) * slope;
setMinMax(x, topFlags, minX, maxX);
}
int bottomFlags = flags & (kFindBottomMin | kFindBottomMax);
if (bottomFlags && (bottom <= p1.y && bottom >= p2.y
|| bottom >= p1.y && bottom <= p2.y)) {
double x = p1.x + (bottom - p1.y) * slope;
setMinMax(x, bottomFlags, minX, maxX);
}
}
void xy_at_t(const Cubic& cubic, double t, double& x, double& y) {
double one_t = 1 - t;
double one_t2 = one_t * one_t;
double a = one_t2 * one_t;
double b = 3 * one_t2 * t;
double t2 = t * t;
double c = 3 * one_t * t2;
double d = t2 * t;
if (&x) {
x = a * cubic[0].x + b * cubic[1].x + c * cubic[2].x + d * cubic[3].x;
}
if (&y) {
y = a * cubic[0].y + b * cubic[1].y + c * cubic[2].y + d * cubic[3].y;
}
}
void xy_at_t(const _Line& line, double t, double& x, double& y) {
double one_t = 1 - t;
if (&x) {
x = one_t * line[0].x + t * line[1].x;
}
if (&y) {
y = one_t * line[0].y + t * line[1].y;
}
}
void xy_at_t(const Quadratic& quad, double t, double& x, double& y) {
double one_t = 1 - t;
double a = one_t * one_t;
double b = 2 * one_t * t;
double c = t * t;
if (&x) {
x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
}
if (&y) {
y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
}
}
// from http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
union Float_t
{

View File

@ -168,20 +168,4 @@ struct QuadraticPair {
_Point pts[5];
};
enum x_at_flags {
kFindTopMin = 1,
kFindTopMax = 2,
kFindBottomMin = 4,
kFindBottomMax = 8
};
bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath);
double t_at(const _Line&, const _Point& );
void x_at(const _Point& p1, const _Point& p2, double minY, double maxY,
int flags, double& tMin, double& tMax);
void xy_at_t(const Cubic& , double t, double& x, double& y);
void xy_at_t(const _Line& , double t, double& x, double& y);
void xy_at_t(const Quadratic& , double t, double& x, double& y);
#endif // __DataTypes_h__

View File

@ -5,15 +5,7 @@
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "Intersections.h"
#include "LineIntersection.h"
#include "SkPath.h"
#include "SkRect.h"
#include "SkTArray.h"
#include "SkTDArray.h"
#include "ShapeOps.h"
#include "TSearch.h"
#include "Simplify.h"
#undef SkASSERT
#define SkASSERT(cond) while (!(cond)) { sk_throw(); }

View File

@ -15,6 +15,9 @@ extern bool drawAsciiPaths(const SkPath& one, const SkPath& two,
extern void showPath(const SkPath& path, const char* str = NULL);
extern bool testSimplify(const SkPath& path, bool fill, SkPath& out,
SkBitmap& bitmap, SkCanvas* canvas = 0);
extern bool testSimplifyx(const SkPath& path, SkPath& out,
SkBitmap& bitmap, SkCanvas* canvas = 0);
extern bool testSimplifyx(const SkPath& path);
struct State4 {
State4();

View File

@ -60,7 +60,7 @@ static int pathsDrawTheSame(const SkPath& one, const SkPath& two,
int bitWidth = SkScalarCeil(larger.width()) + 2;
int bitHeight = SkScalarCeil(larger.height()) + 2;
if (bits.width() < bitWidth * 2 || bits.height() < bitHeight) {
if (bits.width() >= 200) {
if (bits.width() >= 200 && false) {
SkDebugf("%s bitWidth=%d bitHeight=%d\n", __FUNCTION__, bitWidth, bitHeight);
}
bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
@ -192,10 +192,10 @@ int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
const SkRect& bounds2 = two.getBounds();
SkRect larger = bounds1;
larger.join(bounds2);
SkScalar xScale = std::max(80.0f / larger.width(), 1.0f);
SkScalar yScale = std::max(60.0f / larger.height(), 1.0f);
SkScalar xScale = std::max(32.0f / larger.width(), 1.0f);
SkScalar yScale = std::max(24.0f / larger.height(), 1.0f);
errors = scaledDrawTheSame(one, two, xScale, yScale, false, bitmap, canvas);
if (errors > 50) {
if (errors > 5) {
scaledDrawTheSame(one, two, xScale, yScale, true, bitmap, canvas);
}
}
@ -203,7 +203,7 @@ int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors);
max = errors;
}
const int MAX_ERRORS = 100;
const int MAX_ERRORS = 20;
if (errors > MAX_ERRORS) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors);
if (errors > MAX_ERRORS && gComparePathsAssert) {
showPath(one);
@ -256,6 +256,31 @@ bool testSimplify(const SkPath& path, bool fill, SkPath& out, SkBitmap& bitmap,
return comparePaths(path, out, bitmap, canvas) == 0;
}
bool testSimplifyx(const SkPath& path, SkPath& out, SkBitmap& bitmap,
SkCanvas* canvas) {
if (gShowPath) {
showPath(path);
}
simplifyx(path, out);
if (!gComparePaths) {
return true;
}
return comparePaths(path, out, bitmap, canvas) == 0;
}
bool testSimplifyx(const SkPath& path) {
if (false) {
showPath(path);
}
SkPath out;
simplifyx(path, out);
if (false) {
return true;
}
SkBitmap bitmap;
return comparePaths(path, out, bitmap, 0) == 0;
}
State4::State4() {
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100);
bitmap.allocPixels();

View File

@ -2,12 +2,12 @@
#include "Intersection_Tests.h"
void cubecode_test(int test);
void testSimplify();
#define TEST_QUADS_FIRST 0
void Intersection_Tests() {
SimplifyNew_Test();
Simplify4x4RectsThreaded_Test();
SimplifyFindNext_Test();
SimplifyFindTop_Test();
SimplifyAngle_Test();

View File

@ -28,6 +28,7 @@ void SimplifyQuadralateralPaths_Test();
void SimplifyQuadraticPaths_Test();
void Simplify4x4QuadralateralsThreaded_Test();
void Simplify4x4QuadraticsThreaded_Test();
void Simplify4x4RectsThreaded_Test();
void SimplifyRectangularPaths_Test();
void QuadraticBezierClip_Test();
void QuadraticCoincidence_Test();

View File

@ -1,7 +1,7 @@
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Intersection_Tests.h"
#include "Intersections.h"
#include "LineUtilities.h"
#include "TestUtilities.h"
struct lineCubic {

View File

@ -39,6 +39,7 @@ int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]
aPtr = &a[0].y;
bPtr = &b[0].y;
}
#if 0 // sorting edges fails to preserve original direction
double aMin = aPtr[0];
double aMax = aPtr[2];
double bMin = bPtr[0];
@ -62,6 +63,27 @@ int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]
bRange[!bIn] = aMax >= bMax ? 1 : (aMax - bMin) / (bMax - bMin);
}
return 1 + ((aRange[0] != aRange[1]) || (bRange[0] != bRange[1]));
#else
assert(aRange);
assert(bRange);
double a0 = aPtr[0];
double a1 = aPtr[2];
double b0 = bPtr[0];
double b1 = bPtr[2];
double at0 = (a0 - b0) / (a0 - a1);
double at1 = (a0 - b1) / (a0 - a1);
if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
return 0;
}
aRange[0] = std::max(std::min(at0, 1.0), 0.0);
aRange[1] = std::max(std::min(at1, 1.0), 0.0);
int bIn = (a0 - a1) * (b0 - b1) < 0;
bRange[bIn] = std::max(std::min((b0 - a0) / (b0 - b1), 1.0), 0.0);
bRange[!bIn] = std::max(std::min((b0 - a1) / (b0 - b1), 1.0), 0.0);
bool second = fabs(aRange[0] - aRange[1]) > FLT_EPSILON;
assert((fabs(bRange[0] - bRange[1]) <= FLT_EPSILON) ^ second);
return 1 + second;
#endif
}
}
double ab0y = a[0].y - b[0].y;
@ -140,6 +162,7 @@ int horizontalIntersect(const _Line& line, double left, double right,
break;
}
case 2:
#if 0 // sorting edges fails to preserve original direction
double lineL = line[0].x;
double lineR = line[1].x;
if (lineL > lineR) {
@ -159,6 +182,30 @@ int horizontalIntersect(const _Line& line, double left, double right,
intersections.fT[0][1] = (overlapR - line[0].x) / (line[1].x - line[0].x);
intersections.fT[1][1] = (overlapR - left) / (right - left);
}
#else
double a0 = line[0].x;
double a1 = line[1].x;
double b0 = flipped ? right : left;
double b1 = flipped ? left : right;
// FIXME: share common code below
double at0 = (a0 - b0) / (a0 - a1);
double at1 = (a0 - b1) / (a0 - a1);
if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
return 0;
}
intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
int bIn = (a0 - a1) * (b0 - b1) < 0;
intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
1.0), 0.0);
intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
1.0), 0.0);
bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
> FLT_EPSILON;
assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
<= FLT_EPSILON) ^ second);
return 1 + second;
#endif
break;
}
if (flipped) {
@ -204,6 +251,7 @@ int verticalIntersect(const _Line& line, double top, double bottom,
break;
}
case 2:
#if 0 // sorting edges fails to preserve original direction
double lineT = line[0].y;
double lineB = line[1].y;
if (lineT > lineB) {
@ -223,6 +271,30 @@ int verticalIntersect(const _Line& line, double top, double bottom,
intersections.fT[0][1] = (overlapB - line[0].y) / (line[1].y - line[0].y);
intersections.fT[1][1] = (overlapB - top) / (bottom - top);
}
#else
double a0 = line[0].y;
double a1 = line[1].y;
double b0 = flipped ? bottom : top;
double b1 = flipped ? top : bottom;
// FIXME: share common code above
double at0 = (a0 - b0) / (a0 - a1);
double at1 = (a0 - b1) / (a0 - a1);
if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
return 0;
}
intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
int bIn = (a0 - a1) * (b0 - b1) < 0;
intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
1.0), 0.0);
intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
1.0), 0.0);
bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
> FLT_EPSILON;
assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
<= FLT_EPSILON) ^ second);
return 1 + second;
#endif
break;
}
if (flipped) {

View File

@ -1,3 +1,4 @@
#include "CurveUtilities.h"
#include "Intersection_Tests.h"
#include "LineIntersection.h"

View File

@ -1,7 +1,7 @@
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Intersection_Tests.h"
#include "Intersections.h"
#include "LineUtilities.h"
#include "TestUtilities.h"
struct lineQuad {

View File

@ -58,3 +58,64 @@ float isLeft( _Point P0, _Point P1, _Point P2 )
}
#endif
double t_at(const _Line& line, const _Point& pt) {
double dx = line[1].x - line[0].x;
double dy = line[1].y - line[0].y;
if (fabs(dx) > fabs(dy)) {
if (approximately_zero(dx)) {
return 0;
}
return (pt.x - line[0].x) / dx;
}
if (approximately_zero(dy)) {
return 0;
}
return (pt.y - line[0].y) / dy;
}
static void setMinMax(double x, int flags, double& minX, double& maxX) {
if (minX > x && (flags & (kFindTopMin | kFindBottomMin))) {
minX = x;
}
if (maxX < x && (flags & (kFindTopMax | kFindBottomMax))) {
maxX = x;
}
}
void x_at(const _Point& p1, const _Point& p2, double top, double bottom,
int flags, double& minX, double& maxX) {
if (approximately_equal(p1.y, p2.y)) {
// It should be OK to bail early in this case. There's another edge
// which shares this end point which can intersect without failing to
// have a slope ... maybe
return;
}
// p2.x is always greater than p1.x -- the part of points (p1, p2) are
// moving from the start of the cubic towards its end.
// if p1.y < p2.y, minX can be affected
// if p1.y > p2.y, maxX can be affected
double slope = (p2.x - p1.x) / (p2.y - p1.y);
int topFlags = flags & (kFindTopMin | kFindTopMax);
if (topFlags && (top <= p1.y && top >= p2.y
|| top >= p1.y && top <= p2.y)) {
double x = p1.x + (top - p1.y) * slope;
setMinMax(x, topFlags, minX, maxX);
}
int bottomFlags = flags & (kFindBottomMin | kFindBottomMax);
if (bottomFlags && (bottom <= p1.y && bottom >= p2.y
|| bottom >= p1.y && bottom <= p2.y)) {
double x = p1.x + (bottom - p1.y) * slope;
setMinMax(x, bottomFlags, minX, maxX);
}
}
void xy_at_t(const _Line& line, double t, double& x, double& y) {
double one_t = 1 - t;
if (&x) {
x = one_t * line[0].x + t * line[1].x;
}
if (&y) {
y = one_t * line[0].y + t * line[1].y;
}
}

View File

@ -2,3 +2,17 @@
bool implicitLine(const _Line& line, double& slope, double& axisIntercept);
int reduceOrder(const _Line& line, _Line& reduced);
double t_at(const _Line&, const _Point& );
void xy_at_t(const _Line& , double t, double& x, double& y);
enum x_at_flags {
kFindTopMin = 1,
kFindTopMax = 2,
kFindBottomMin = 4,
kFindBottomMax = 8
};
void x_at(const _Point& p1, const _Point& p2, double minY, double maxY,
int flags, double& tMin, double& tMax);

View File

@ -1,4 +1,5 @@
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "LineParameters.h"
#include <algorithm> // used for std::swap

View File

@ -1,4 +1,5 @@
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Extrema.h"
static int isBoundedByEndPoints(double a, double b, double c)

View File

@ -1,4 +1,5 @@
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Intersection_Tests.h"
#include "Intersections.h"
#include "QuadraticIntersection_TestData.h"

View File

@ -25,3 +25,28 @@ int quadraticRoots(double A, double B, double C, double t[2]) {
}
return foundRoots;
}
void dxdy_at_t(const Quadratic& quad, double t, double& x, double& y) {
double a = t - 1;
double b = 1 - 2 * t;
double c = t;
if (&x) {
x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
}
if (&y) {
y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
}
}
void xy_at_t(const Quadratic& quad, double t, double& x, double& y) {
double one_t = 1 - t;
double a = one_t * one_t;
double b = 2 * one_t * t;
double c = t * t;
if (&x) {
x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
}
if (&y) {
y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
}
}

View File

@ -1,3 +1,7 @@
#include "DataTypes.h"
void dxdy_at_t(const Quadratic& , double t, double& x, double& y);
/* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
*
* a = A - 2*B + C
@ -14,3 +18,5 @@ inline void set_abc(const double* quad, double& a, double& b, double& c) {
}
int quadraticRoots(double A, double B, double C, double t[2]);
void xy_at_t(const Quadratic& , double t, double& x, double& y);

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
#include "CurveIntersection.h"
#include "CurveUtilities.h"
#include "Intersections.h"
#include "LineIntersection.h"
#include "LineParameters.h"

View File

@ -87,7 +87,7 @@ static void testPath(const SkPath& path, const SkPoint* pts1, SkPath::Verb c1Typ
SimplifyAddIntersectingTsTest::Contour& c1 = contour[0];
SimplifyAddIntersectingTsTest::Contour& c2 = contour[1];
addIntersectTs(&c1, &c2);
bool c1Intersected = c1.fSegments[0].intersected();
bool c1Intersected = c1.segments()[0].intersected();
// bool c2Intersected = c2.fSegments[0].intersected();
#if DEBUG_DUMP
SkDebugf("%s %s (%1.9g,%1.9g %1.9g,%1.9g) %s %s (%1.9g,%1.9g %1.9g,%1.9g)\n",

View File

@ -5,6 +5,8 @@
* found in the LICENSE file.
*/
#define DEBUG_TEST 1
#include "Simplify.h"
namespace SimplifyFindNextTest {
@ -27,12 +29,12 @@ static const SimplifyFindNextTest::Segment* testCommon(
addIntersectTs(contourList[1], contourList[1]);
}
fixOtherTIndex(contourList);
SimplifyFindNextTest::Segment& segment = contours[0].fSegments[0];
SimplifyFindNextTest::Segment& segment = contours[0].debugSegments()[0];
SkPoint pts[2];
pts[0] = segment.xyAtT(&segment.span(endIndex));
int nextStart, nextEnd;
SimplifyFindNextTest::Segment* next = segment.findNext(winding,
startIndex, endIndex, nextStart, nextEnd);
startIndex, endIndex, nextStart, nextEnd, true);
pts[1] = next->xyAtT(&next->span(nextStart));
SkASSERT(pts[0] == pts[1]);
return next;

View File

@ -27,8 +27,8 @@ static const SimplifyFindTopTest::Segment* testCommon(
addIntersectTs(contourList[1], contourList[1]);
}
fixOtherTIndex(contourList);
SimplifyFindTopTest::Segment* topStart = findTopContour(contourList,
contourList.count());
SimplifyFindTopTest::Contour* top;
SimplifyFindTopTest::Segment* topStart = findTopContour(contourList, top);
const SimplifyFindTopTest::Segment* topSegment = topStart->findTop(index,
end);
return topSegment;

View File

@ -5,29 +5,9 @@
* found in the LICENSE file.
*/
#include "Simplify.h"
namespace SimplifyNewTest {
#include "Simplify.cpp"
} // end of SimplifyNewTest namespace
#include "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
static bool testSimplifyx(const SkPath& path) {
if (false) {
showPath(path);
}
SkPath out;
simplifyx(path, out);
if (false) {
return true;
}
SkBitmap bitmap;
return comparePaths(path, out, bitmap, 0) == 0;
}
#include "ShapeOps.h"
static void testLine1() {
SkPath path, simple;
@ -146,17 +126,228 @@ static void testLine9() {
testSimplifyx(path);
}
static void testLine10() {
SkPath path, simple;
path.moveTo(0,4);
path.lineTo(4,4);
path.lineTo(2,2);
path.close();
path.moveTo(2,1);
path.lineTo(3,4);
path.lineTo(6,1);
path.close();
testSimplifyx(path);
}
static void (*tests[])() = {
testLine1,
testLine2,
testLine3,
testLine4,
testLine5,
testLine6,
testLine7,
testLine8,
testLine9
static void testLine10a() {
SkPath path, simple;
path.moveTo(0,4);
path.lineTo(8,4);
path.lineTo(4,0);
path.close();
path.moveTo(2,2);
path.lineTo(3,3);
path.lineTo(4,2);
path.close();
testSimplifyx(path);
}
static void addCWContainer(SkPath& path) {
path.moveTo(6,4);
path.lineTo(0,4);
path.lineTo(3,1);
path.close();
}
static void addCCWContainer(SkPath& path) {
path.moveTo(0,4);
path.lineTo(6,4);
path.lineTo(3,1);
path.close();
}
static void addCWContents(SkPath& path) {
path.moveTo(2,3);
path.lineTo(3,2);
path.lineTo(4,3);
path.close();
}
static void addCCWContents(SkPath& path) {
path.moveTo(3,2);
path.lineTo(2,3);
path.lineTo(4,3);
path.close();
}
static void testLine11() {
SkPath path, simple;
addCWContainer(path);
addCWContents(path);
testSimplifyx(path);
}
static void testLine12() {
SkPath path, simple;
addCCWContainer(path);
addCWContents(path);
testSimplifyx(path);
}
static void testLine13() {
SkPath path, simple;
addCWContainer(path);
addCCWContents(path);
testSimplifyx(path);
}
static void testLine14() {
SkPath path, simple;
addCCWContainer(path);
addCCWContents(path);
testSimplifyx(path);
}
static void testLine15() {
SkPath path, simple;
path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine16() {
SkPath path, simple;
path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
path.addRect(0, 4, 9, 9, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine17() {
SkPath path, simple;
path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine18() {
SkPath path, simple;
path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
path.addRect(12, 4, 21, 21, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine19() {
SkPath path, simple;
path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
path.addRect(12, 16, 21, 21, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine20() {
SkPath path, simple;
path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
path.addRect(0, 12, 9, 9, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine21() {
SkPath path, simple;
path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
path.addRect(0, 16, 9, 9, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine22() {
SkPath path, simple;
path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine23() {
SkPath path, simple;
path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
path.addRect(12, 0, 21, 21, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine24a() {
SkPath path, simple;
path.moveTo(2,0);
path.lineTo(4,4);
path.lineTo(0,4);
path.close();
path.moveTo(2,0);
path.lineTo(1,2);
path.lineTo(2,2);
path.close();
testSimplifyx(path);
}
static void testLine24() {
SkPath path, simple;
path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine25() {
SkPath path, simple;
path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
path.addRect(12, 0, 21, 21, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine26() {
SkPath path, simple;
path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
path.addRect(0, 12, 9, 9, (SkPath::Direction) 0);
testSimplifyx(path);
}
static void testLine27() {
SkPath path, simple;
path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
path.addRect(12, 8, 21, 21, (SkPath::Direction) 0);
testSimplifyx(path);
}
#define TEST(name) { name, #name }
static struct {
void (*fun)();
const char* str;
} tests[] = {
TEST(testLine1),
TEST(testLine2),
TEST(testLine3),
TEST(testLine4),
TEST(testLine5),
TEST(testLine6),
TEST(testLine7),
TEST(testLine8),
TEST(testLine9),
TEST(testLine10),
TEST(testLine10a),
TEST(testLine11),
TEST(testLine12),
TEST(testLine13),
TEST(testLine14),
TEST(testLine15),
TEST(testLine16),
TEST(testLine17),
TEST(testLine18),
TEST(testLine19),
TEST(testLine20),
TEST(testLine21),
TEST(testLine22),
TEST(testLine23),
TEST(testLine24a),
TEST(testLine24),
TEST(testLine25),
TEST(testLine26),
TEST(testLine27),
};
static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
@ -170,14 +361,14 @@ void SimplifyNew_Test() {
}
size_t index = 0;
if (firstTest) {
while (index < testCount && tests[index] != firstTest) {
while (index < testCount && tests[index].fun != firstTest) {
++index;
}
}
bool firstTestComplete = false;
for ( ; index < testCount; ++index) {
SkDebugf("%s [%d]\n", __FUNCTION__, index + 1);
(*tests[index])();
SkDebugf("%s [%s]\n", __FUNCTION__, tests[index].str);
(*tests[index].fun)();
firstTestComplete = true;
}
}

View File

@ -0,0 +1,209 @@
/*
* 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 "EdgeWalker_Test.h"
#include "Intersection_Tests.h"
#include "ShapeOps.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include <assert.h>
#include <pthread.h>
// four rects, of four sizes
// for 3 smaller sizes, tall, wide
// top upper mid lower bottom aligned (3 bits, 5 values)
// same with x (3 bits, 5 values)
// not included, square, tall, wide (2 bits)
// cw or ccw (1 bit)
static void* testSimplify4x4RectsMain(void* data)
{
char pathStr[1024]; // gdb: set print elements 400
bzero(pathStr, sizeof(pathStr));
SkASSERT(data);
State4& state = *(State4*) data;
int aShape = state.a & 0x03;
int aCW = state.a >> 1;
int bShape = state.b & 0x03;
int bCW = state.b >> 1;
int cShape = state.c & 0x03;
int cCW = state.c >> 1;
int dShape = state.d & 0x03;
int dCW = state.d >> 1;
for (int aXAlign = 0 ; aXAlign < 5; ++aXAlign) {
for (int aYAlign = 0 ; aYAlign < 5; ++aYAlign) {
for (int bXAlign = 0 ; bXAlign < 5; ++bXAlign) {
for (int bYAlign = 0 ; bYAlign < 5; ++bYAlign) {
for (int cXAlign = 0 ; cXAlign < 5; ++cXAlign) {
for (int cYAlign = 0 ; cYAlign < 5; ++cYAlign) {
for (int dXAlign = 0 ; dXAlign < 5; ++dXAlign) {
for (int dYAlign = 0 ; dYAlign < 5; ++dYAlign) {
SkPath path, out;
char* str = pathStr;
path.setFillType(SkPath::kWinding_FillType);
int l, t, r, b;
if (aShape) {
switch (aShape) {
case 1: // square
l = 0; r = 60;
t = 0; b = 60;
aXAlign = 5;
aYAlign = 5;
break;
case 2:
l = aXAlign * 12;
r = l + 30;
t = 0; b = 60;
aYAlign = 5;
break;
case 3:
l = 0; r = 60;
t = aYAlign * 12;
b = l + 30;
aXAlign = 5;
break;
}
path.addRect(l, t, r, b, (SkPath::Direction) aCW);
str += sprintf(str, " path.addRect(%d, %d, %d, %d,"
" (SkPath::Direction) %d);\n", l, t, r, b, aCW);
} else {
aXAlign = 5;
aYAlign = 5;
}
if (bShape) {
switch (bShape) {
case 1: // square
l = bXAlign * 10;
r = l + 20;
t = bYAlign * 10;
b = l + 20;
break;
case 2:
l = bXAlign * 10;
r = l + 20;
t = 10; b = 40;
bYAlign = 5;
break;
case 3:
l = 10; r = 40;
t = bYAlign * 10;
b = l + 20;
bXAlign = 5;
break;
}
path.addRect(l, t, r, b, (SkPath::Direction) bCW);
str += sprintf(str, " path.addRect(%d, %d, %d, %d,"
" (SkPath::Direction) %d);\n", l, t, r, b, bCW);
} else {
bXAlign = 5;
bYAlign = 5;
}
if (cShape) {
switch (cShape) {
case 1: // square
l = cXAlign * 6;
r = l + 12;
t = cYAlign * 6;
b = l + 12;
break;
case 2:
l = cXAlign * 6;
r = l + 12;
t = 20; b = 30;
cYAlign = 5;
break;
case 3:
l = 20; r = 30;
t = cYAlign * 6;
b = l + 20;
cXAlign = 5;
break;
}
path.addRect(l, t, r, b, (SkPath::Direction) cCW);
str += sprintf(str, " path.addRect(%d, %d, %d, %d,"
" (SkPath::Direction) %d);\n", l, t, r, b, cCW);
} else {
cXAlign = 5;
cYAlign = 5;
}
if (dShape) {
switch (dShape) {
case 1: // square
l = dXAlign * 4;
r = l + 9;
t = dYAlign * 4;
b = l + 9;
break;
case 2:
l = dXAlign * 6;
r = l + 9;
t = 32; b = 36;
dYAlign = 5;
break;
case 3:
l = 32; r = 36;
t = dYAlign * 6;
b = l + 9;
dXAlign = 5;
break;
}
path.addRect(l, t, r, b, (SkPath::Direction) dCW);
str += sprintf(str, " path.addRect(%d, %d, %d, %d,"
" (SkPath::Direction) %d);\n", l, t, r, b, dCW);
} else {
dXAlign = 5;
dYAlign = 5;
}
path.close();
SkDebugf("%s", pathStr);
if (!testSimplifyx(path, out, state.bitmap, state.canvas)) {
SkDebugf("*/\n{ %s %d, %d, %d, %d, %d, %d, %d, %d,"
" %d, %d, %d, %d },\n/*\n",
__FUNCTION__, state.a, state.b, state.c, state.d,
aXAlign, aYAlign, bXAlign, bYAlign,
cXAlign, cYAlign, dXAlign, dYAlign);
}
}
}
}
}
}
}
}
}
return NULL;
}
const int maxThreads = 1; // gRunTestsInOneThread ? 1 : 24;
void Simplify4x4RectsThreaded_Test()
{
State4 threadState[maxThreads];
int threadIndex = 0;
for (int a = 0; a < 8; ++a) { // outermost
for (int b = a ; b < 8; ++b) {
for (int c = b ; c < 8; ++c) {
for (int d = c; d < 8; ++d) {
State4* statePtr = &threadState[threadIndex];
statePtr->a = a;
statePtr->b = b;
statePtr->c = c;
statePtr->d = d;
if (maxThreads > 1) {
createThread(statePtr, testSimplify4x4RectsMain);
if (++threadIndex >= maxThreads) {
waitForCompletion(threadState, threadIndex);
}
} else {
testSimplify4x4RectsMain(statePtr);
}
}
}
}
}
waitForCompletion(threadState, threadIndex);
}

File diff suppressed because it is too large Load Diff

View File

@ -259,11 +259,76 @@ path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
</div>
<div id="testSimplifyQuadratic19">
SkPath path, simple;
path.moveTo(0,4);
path.lineTo(6,4);
path.lineTo(3,1);
path.close();
path.moveTo(2,3);
path.lineTo(3,2);
path.lineTo(4,3);
path.close();
testSimplifyx(path);
</div>
<div id="testSimplifyQuadratic20">
SkPath path, simple;
path.moveTo(0,4);
path.lineTo(6,4);
path.lineTo(3,1);
path.close();
path.moveTo(2,3);
path.lineTo(4,3);
path.lineTo(3,2);
path.close();
testSimplifyx(path);
</div>
<div id="testSimplifyQuadratic21">
SkPath path, simple;
path.moveTo(0,4);
path.lineTo(8,4);
path.lineTo(4,0);
path.close();
path.moveTo(2,2);
path.lineTo(3,3);
path.lineTo(4,2);
path.close();
testSimplifyx(path);
</div>
<div id="testLine6">
SkPath path, simple;
path.moveTo(0,0);
path.lineTo(4,0);
path.lineTo(2,2);
path.close();
path.moveTo(2,0);
path.lineTo(6,0);
path.lineTo(4,2);
path.close();
testSimplifyx(path);
</div>
<!-- don't support addRect yet -->
<div id="testLine17">
SkPath path, simple;
path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
testSimplifyx(path);
</div>
</div>
<script type="text/javascript">
var testDivs = [
testLine6,
testSimplifyQuadratic21,
testSimplifyQuadratic20,
testSimplifyQuadratic19,
testSimplifyQuadratic18,
testSimplifyQuadratic17,
testSimplifyQuadratic16,
@ -412,10 +477,10 @@ function draw(test, _at_x, _at_y, scale) {
}
ctx.strokeStyle = "red";
var contours, verbs, pts;
ctx.beginPath();
for (contours in test) {
var contour = test[contours];
if (contours == 2) ctx.strokeStyle = "blue";
ctx.beginPath();
var first = true;
for (verbs in contour) {
var verb = contour[verbs];
@ -438,8 +503,11 @@ function draw(test, _at_x, _at_y, scale) {
break;
}
}
ctx.stroke();
ctx.closePath();
}
ctx.stroke();
ctx.fillStyle="rgba(192,192,255, 0.3)";
ctx.fill();
ctx.fillStyle="blue";
for (contours in test) {

View File

@ -164,3 +164,356 @@ According to Mike/Rob, the flatness for quadratics increases by 4 for each
subdivision, and a crude guess of the curvature can be had by comparing P1 to
(P0+P2)/2. By looking at the ULPS of the numbers, I can guess what value of
T may be far enough that the curves diverge but don't cross.
====
Code I May Not Need Any More
static bool CoincidentCandidate(const Angle* current) {
const Segment* segment = current->segment();
int min = SkMin32(current->start(), current->end());
do {
const Span& span = segment->fTs[min];
if (span.fCoincident == Span::kStart_Coincidence) {
return true;
}
} while (--min >= 0);
return false;
}
static bool CoincidentHalf(const Angle* current, const Angle* next) {
const Segment* other = next->segment();
const Segment* segment = current->segment();
int min = SkMin32(current->start(), current->end());
const Span& minSpan = segment->fTs[min];
if (minSpan.fOther == other) {
return minSpan.fCoincident == Span::kStart_Coincidence;
}
int index = min;
int spanCount = segment->fTs.count();
while (++index < spanCount) {
const Span& span = segment->fTs[index];
if (minSpan.fT != span.fT) {
break;
}
if (span.fOther != other) {
continue;
}
return span.fCoincident == Span::kStart_Coincidence;
}
index = min;
while (--index >= 0) {
const Span& span = segment->fTs[index];
if (span.fOther != other) {
continue;
}
return span.fCoincident == Span::kStart_Coincidence;
}
return false;
}
static bool Coincident(const Angle* current, const Angle* next) {
return CoincidentHalf(current, next) &&
CoincidentHalf(next, current);
}
// If three lines cancel in a - b - c order, a - b may or may not
// eliminate the edge that describes the b - c cancellation. Check done to
// determine this.
static bool CoincidentCancels(const Angle* current, const Angle* next) {
int curMin = SkMin32(current->start(), current->end());
if (current->segment()->fTs[curMin].fDone) {
return false;
}
int nextMin = SkMin32(next->start(), next->end());
if (next->segment()->fTs[nextMin].fDone) {
return false;
}
return SkSign32(current->start() - current->end())
!= SkSign32(next->start() - next->end());
}
// FIXME: at this point, just have two functions for the different steps
int coincidentEnd(int from, int step) const {
double fromT = fTs[from].fT;
int count = fTs.count();
int to = from;
while (step > 0 ? ++to < count : --to >= 0) {
const Span& span = fTs[to];
if ((step > 0 ? span.fT - fromT : fromT - span.fT) >= FLT_EPSILON ) {
// FIXME: we assume that if the T changes, we don't care about
// coincident -- but in nextSpan, we require that both the T
// and actual loc change to represent a span. This asymettry may
// be OK or may be trouble -- if trouble, probably will need to
// detect coincidence earlier or sort differently
break;
}
#if 01
if (span.fCoincident == (step < 0 ? Span::kStart_Coincidence :
Span::kEnd_Coincidence)) {
from = to;
}
#else
from = to;
#endif
}
return from;
}
// once past current span, if step>0, look for coicident==1
// if step<0, look for coincident==-1
int nextSpanEnd(int from, int step) const {
int result = nextSpan(from, step);
if (result < 0) {
return result;
}
return coincidentEnd(result, step);
}
void adjustFirst(const SkTDArray<Angle*>& sorted, int& first, int& winding,
bool outside) {
int firstIndex = first;
int angleCount = sorted.count();
if (true || outside) {
const Angle* angle = sorted[firstIndex];
int prior = firstIndex;
do {
if (--prior < 0) {
prior = angleCount - 1;
}
if (prior == firstIndex) { // all are coincident with each other
return;
}
if (!Coincident(sorted[prior], sorted[first])) {
return;
}
winding += angle->sign();
first = prior;
angle = sorted[prior];
winding -= angle->sign();
} while (true);
}
do {
int next = first + 1;
if (next == angleCount) {
next = 0;
}
if (next == firstIndex) { // all are coincident with each other
return;
}
if (!Coincident(sorted[first], sorted[next])) {
return;
}
first = next;
} while (true);
}
bool nextIsCoincident = CoincidentCandidate(nextAngle);
bool finalOrNoCoincident = true;
bool pairCoincides = false;
bool pairCancels = false;
if (nextIsCoincident) {
int followIndex = nextIndex + 1;
if (followIndex == angleCount) {
followIndex = 0;
}
const Angle* followAngle = sorted[followIndex];
finalOrNoCoincident = !Coincident(nextAngle, followAngle);
if ((pairCoincides = Coincident(angle, nextAngle))) {
pairCancels = CoincidentCancels(angle, nextAngle);
}
}
if (pairCancels && !foundAngle && !nextSegment->done()) {
Segment* aSeg = angle->segment();
// alreadyMarked |= aSeg == sorted[firstIndex]->segment();
aSeg->markAndChaseCoincident(angle->start(), angle->end(),
nextSegment);
if (firstEdge) {
return NULL;
}
}
if (pairCoincides) {
if (pairCancels) {
goto doNext;
}
int minT = SkMin32(nextAngle->start(), nextAngle->end());
bool markNext = abs(maxWinding) < abs(winding);
if (markNext) {
nextSegment->markDone(minT, winding);
}
int oldMinT = SkMin32(angle->start(), angle->end());
if (true || !foundAngle) {
// SkASSERT(0); // do we ever get here?
Segment* aSeg = angle->segment();
// alreadyMarked |= aSeg == sorted[firstIndex]->segment();
aSeg->markDone(oldMinT, maxWinding);
}
}
// OPTIMIZATION: uses tail recursion. Unwise?
void innerCoincidentChase(int step, Segment* other) {
// find other at index
// SkASSERT(!done());
const Span* start = NULL;
const Span* end = NULL;
int index, startIndex, endIndex;
int count = fTs.count();
for (index = 0; index < count; ++index) {
const Span& span = fTs[index];
if (!span.fCoincident || span.fOther != other) {
continue;
}
if (!start) {
startIndex = index;
start = &span;
} else {
SkASSERT(!end);
endIndex = index;
end = &span;
}
}
if (!end) {
return;
}
bool thisDone = fTs[SkMin32(startIndex, endIndex)].fDone;
bool otherDone = other->fTs[SkMin32(start->fOtherIndex,
end->fOtherIndex)].fDone;
if (thisDone && otherDone) {
return;
}
Segment* next;
Segment* nextOther;
if (step < 0) {
next = start->fT == 0 ? NULL : this;
nextOther = other->fTs[start->fOtherIndex].fT > 1 - FLT_EPSILON ? NULL : other;
} else {
next = end->fT == 1 ? NULL : this;
nextOther = other->fTs[end->fOtherIndex].fT < FLT_EPSILON ? NULL : other;
}
SkASSERT(!next || !nextOther);
for (index = 0; index < count; ++index) {
const Span& span = fTs[index];
if (span.fCoincident || span.fOther == other) {
continue;
}
bool checkNext = !next && (step < 0 ? span.fT < FLT_EPSILON
&& span.fOtherT > 1 - FLT_EPSILON : span.fT > 1 - FLT_EPSILON
&& span.fOtherT < FLT_EPSILON);
bool checkOther = !nextOther && (step < 0 ? fabs(span.fT - start->fT) < FLT_EPSILON
&& span.fOtherT < FLT_EPSILON : fabs(span.fT - end->fT) < FLT_EPSILON
&& span.fOtherT > 1 - FLT_EPSILON);
if (!checkNext && !checkOther) {
continue;
}
Segment* oSegment = span.fOther;
if (oSegment->done()) {
continue;
}
int oCount = oSegment->fTs.count();
for (int oIndex = 0; oIndex < oCount; ++oIndex) {
const Span& oSpan = oSegment->fTs[oIndex];
if (oSpan.fT >= FLT_EPSILON && oSpan.fT <= 1 - FLT_EPSILON) {
continue;
}
if (!oSpan.fCoincident) {
continue;
}
if (checkNext && (oSpan.fT < FLT_EPSILON ^ step < 0)) {
next = oSegment;
checkNext = false;
}
if (checkOther && (oSpan.fT > 1 - FLT_EPSILON ^ step < 0)) {
nextOther = oSegment;
checkOther = false;
}
}
}
// this needs to walk both spans in lock step, skipping edges that
// are already marked done on one or the other
markCanceled(startIndex, endIndex);
if (next && nextOther) {
next->innerCoincidentChase(step, nextOther);
}
}
// cancel coincident edges in lock step
void markCanceled(int start, int end) {
if (done()) {
return;
}
Segment* other = fTs[start].fOther;
if (other->done()) {
return;
}
if (start > end) {
SkTSwap<int>(start, end);
}
double maxT = fTs[end].fT - FLT_EPSILON;
int spanCount = fTs.count();
// since these cancel, this walks up and other walks down
int oStart = fTs[start].fOtherIndex;
double oStartT = other->fTs[oStart].fT;
while (oStartT - other->fTs[--oStart].fT < FLT_EPSILON)
;
double startT = fTs[start].fT;
while (start > 0 && startT - fTs[start - 1].fT < FLT_EPSILON) {
--start;
}
do {
Span* span = &fTs[start];
Span* oSpan = &other->fTs[oStart];
// find start of each, and see if both are not done
bool markDone = !span->fDone && !oSpan->fDone;
double spanT = span->fT;
do {
if (markDone) {
span->fCanceled = true;
#if DEBUG_MARK_DONE
const SkPoint& pt = xyAtT(span);
SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g)\n",
__FUNCTION__, fID, start, span->fT, pt.fX, pt.fY);
#endif
SkASSERT(!span->fDone);
span->fDone = true;
span->fWinding = 0;
fDoneSpans++;
}
if (++start == spanCount) {
break;
}
span = &fTs[start];
} while (span->fT - spanT < FLT_EPSILON);
double oSpanT = oSpan->fT;
do {
if (markDone) {
oSpan->fCanceled = true;
#if DEBUG_MARK_DONE
const SkPoint& oPt = xyAtT(oSpan);
SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g)\n",
__FUNCTION__, other->fID, oStart, oSpan->fT,
oPt.fX, oPt.fY);
#endif
SkASSERT(!oSpan->fDone);
oSpan->fDone = true;
oSpan->fWinding = 0;
other->fDoneSpans++;
}
if (--oStart < 0) {
break;
}
oSpan = &other->fTs[oStart];
} while (oSpanT - oSpan->fT < FLT_EPSILON);
} while (fTs[start].fT <= maxT);
}
bool canceled(int start, int end) const {
int min = SkMin32(start, end);
return fTs[min].fCanceled;
}
void markAndChaseCoincident(int index, int endIndex, Segment* other) {
int step = SkSign32(endIndex - index);
innerCoincidentChase(step, other);
}