aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental
diff options
context:
space:
mode:
authorGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-05-18 20:50:33 +0000
committerGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-05-18 20:50:33 +0000
commitb45a1b46ee25e9b19800b028bb1ca925212ac7b4 (patch)
tree2a2bc0d004962519eaadd605c4b885386fc252cf /experimental
parenta611c3ea53c02ef80baa32fbfb9cca33f999378d (diff)
shape ops work in progress
git-svn-id: http://skia.googlecode.com/svn/trunk@4006 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'experimental')
-rwxr-xr-xexperimental/Intersection/ActiveEdge_Test.cpp12
-rw-r--r--experimental/Intersection/CubicIntersection_TestData.cpp1
-rw-r--r--experimental/Intersection/CubicIntersection_TestData.h4
-rw-r--r--experimental/Intersection/CubicReduceOrder.cpp4
-rw-r--r--experimental/Intersection/DataTypes.cpp4
-rw-r--r--experimental/Intersection/DataTypes.h52
-rw-r--r--experimental/Intersection/EdgeMain.cpp14
-rw-r--r--experimental/Intersection/EdgeMain.h6
-rw-r--r--experimental/Intersection/EdgeWalker.cpp8
-rw-r--r--experimental/Intersection/EdgeWalkerPolygons_Mismatches.h9
-rw-r--r--experimental/Intersection/EdgeWalkerPolygons_Test.cpp370
-rw-r--r--experimental/Intersection/Intersection_Tests.cpp9
-rw-r--r--experimental/Intersection/Intersection_Tests.h7
-rw-r--r--experimental/Intersection/LineParameters.h12
-rw-r--r--experimental/Intersection/QuadraticBezierClip_Test.cpp24
-rw-r--r--experimental/Intersection/QuadraticIntersection_Test.cpp49
-rw-r--r--experimental/Intersection/QuadraticIntersection_TestData.cpp8
-rw-r--r--experimental/Intersection/QuadraticIntersection_TestData.h4
-rw-r--r--experimental/Intersection/QuadraticReduceOrder.cpp9
-rw-r--r--experimental/Intersection/QuadraticReduceOrder_Test.cpp26
-rw-r--r--experimental/Intersection/Simplify.cpp820
-rw-r--r--experimental/Intersection/Simplify.h18
-rw-r--r--experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp17
-rw-r--r--experimental/Intersection/SimplifyAngle_Test.cpp183
-rw-r--r--experimental/Intersection/SimplifyFindNext_Test.cpp83
-rw-r--r--experimental/Intersection/SimplifyFindTop_Test.cpp208
-rw-r--r--experimental/Intersection/op.htm35
-rw-r--r--experimental/Intersection/thingsToDo.txt25
28 files changed, 1463 insertions, 558 deletions
diff --git a/experimental/Intersection/ActiveEdge_Test.cpp b/experimental/Intersection/ActiveEdge_Test.cpp
index 03cb595743..3e1b958ab6 100755
--- a/experimental/Intersection/ActiveEdge_Test.cpp
+++ b/experimental/Intersection/ActiveEdge_Test.cpp
@@ -25,24 +25,24 @@ SkPoint leftRight[][4] = {
{{10, 0}, {10, 50}, {20, 10}, {20, 50}},
{{10, 0}, {10, 50}, {10, 10}, {20, 50}},
{{10, 0}, {10, 50}, {20, 10}, {10, 50}},
- {{10, 0}, {10, 50}, {20, 10}, {10 + 0.000001, 40}},
+ {{10, 0}, {10, 50}, {20, 10}, {10 + 0.000001f, 40}},
// left top lower
{{10, 20}, {10, 50}, {20, 10}, {20, 50}},
{{10, 20}, {10, 50}, {10, 10}, {20, 50}},
{{10, 20}, {10, 50}, {20, 10}, {10, 50}},
- {{10, 20}, {10, 50}, {20, 10}, {10 + 0.000001, 40}},
+ {{10, 20}, {10, 50}, {20, 10}, {10 + 0.000001f, 40}},
{{10, 20}, {10, 50}, { 0, 0}, {50, 50}},
// left bottom higher
{{10, 10}, {10, 40}, {20, 10}, {20, 50}},
{{10, 10}, {10, 40}, {10, 10}, {20, 50}},
{{10, 10}, {10, 40}, {20, 10}, {10, 50}},
- {{10, 10}, {10, 40}, {20, 10}, { 0 + 0.000001, 70}},
+ {{10, 10}, {10, 40}, {20, 10}, { 0 + 0.000001f, 70}},
// left bottom lower
{{10, 10}, {10, 60}, {20, 10}, {20, 50}},
{{10, 10}, {10, 60}, {10, 10}, {20, 50}},
- {{10, 10}, {10, 60}, {20, 10}, {10 + 0.000001, 50}},
- {{10, 10}, {10, 60}, {20, 10}, {10 + 0.000001, 40}},
- {{10, 10}, {10, 60}, { 0, 0}, {20 + 0.000001, 20}},
+ {{10, 10}, {10, 60}, {20, 10}, {10 + 0.000001f, 50}},
+ {{10, 10}, {10, 60}, {20, 10}, {10 + 0.000001f, 40}},
+ {{10, 10}, {10, 60}, { 0, 0}, {20 + 0.000001f, 20}},
};
size_t leftRightCount = sizeof(leftRight) / sizeof(leftRight[0]);
diff --git a/experimental/Intersection/CubicIntersection_TestData.cpp b/experimental/Intersection/CubicIntersection_TestData.cpp
index 05d08afb9b..066d9b5b97 100644
--- a/experimental/Intersection/CubicIntersection_TestData.cpp
+++ b/experimental/Intersection/CubicIntersection_TestData.cpp
@@ -1,3 +1,4 @@
+#define IN_TEST 1
#include "CubicIntersection_TestData.h"
#include <limits>
diff --git a/experimental/Intersection/CubicIntersection_TestData.h b/experimental/Intersection/CubicIntersection_TestData.h
index 6ed0f49a72..7dc63a031a 100644
--- a/experimental/Intersection/CubicIntersection_TestData.h
+++ b/experimental/Intersection/CubicIntersection_TestData.h
@@ -1,3 +1,7 @@
+#if !defined(IN_TEST)
+ #define IN_TEST 1
+#endif
+
#include "DataTypes.h"
extern const Cubic pointDegenerates[];
diff --git a/experimental/Intersection/CubicReduceOrder.cpp b/experimental/Intersection/CubicReduceOrder.cpp
index 9c3b843c9f..19c69db944 100644
--- a/experimental/Intersection/CubicReduceOrder.cpp
+++ b/experimental/Intersection/CubicReduceOrder.cpp
@@ -150,12 +150,12 @@ bool isLinear(const Cubic& cubic, int startIndex, int endIndex) {
int inner1 = startIndex ^ mask;
int inner2 = endIndex ^ mask;
lineParameters.controlPtDistance(cubic, inner1, inner2, distance);
- double limit = normalSquared * SquaredEpsilon;
+ double limit = normalSquared;
int index;
for (index = 0; index < 2; ++index) {
double distSq = distance[index];
distSq *= distSq;
- if (distSq > limit) {
+ if (approximately_greater(distSq, limit)) {
return false;
}
}
diff --git a/experimental/Intersection/DataTypes.cpp b/experimental/Intersection/DataTypes.cpp
index a68cb9a96a..776d0cf991 100644
--- a/experimental/Intersection/DataTypes.cpp
+++ b/experimental/Intersection/DataTypes.cpp
@@ -14,8 +14,12 @@ void *memcpy(void *, const void *, size_t);
#endif
+#if USE_EPSILON
const double PointEpsilon = 0.000001;
const double SquaredEpsilon = PointEpsilon * PointEpsilon;
+#endif
+
+const int UlpsEpsilon = 16;
bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath) {
double dy = cubic[index].y - cubic[zero].y;
diff --git a/experimental/Intersection/DataTypes.h b/experimental/Intersection/DataTypes.h
index 290a336e35..8b7ff7fb30 100644
--- a/experimental/Intersection/DataTypes.h
+++ b/experimental/Intersection/DataTypes.h
@@ -12,6 +12,13 @@
#include <strings.h>
#include <sys/types.h>
+bool AlmostEqualUlps(float A, float B, int maxUlpsDiff);
+int UlpsDiff(float A, float B);
+int FloatAsInt(float A);
+
+#define USE_EPSILON 0
+
+#if USE_EPSILON
extern const double PointEpsilon;
extern const double SquaredEpsilon;
@@ -42,6 +49,48 @@ inline bool approximately_zero_squared(double x) {
inline bool approximately_negative(double x) {
return x < PointEpsilon;
}
+#else
+extern const int UlpsEpsilon;
+
+#if defined(IN_TEST)
+// FIXME: move to test-only header
+const double PointEpsilon = 0.000001;
+const double SquaredEpsilon = PointEpsilon * PointEpsilon;
+#endif
+
+inline bool approximately_zero(double x) {
+
+ return fabs(x) < FLT_EPSILON;
+}
+
+inline bool approximately_equal(double x, double y) {
+ if (approximately_zero(x - y)) {
+ return true;
+ }
+ return AlmostEqualUlps((float) x, (float) y, UlpsEpsilon);
+}
+
+inline bool approximately_equal_squared(double x, double y) {
+ return approximately_equal(x, y);
+}
+
+inline bool approximately_greater(double x, double y) {
+ return approximately_equal(x, y) ? false : x > y;
+}
+
+inline bool approximately_lesser(double x, double y) {
+ return approximately_equal(x, y) ? false : x < y;
+}
+
+inline bool approximately_zero_squared(double x) {
+ return approximately_zero(x);
+}
+
+inline bool approximately_negative(double x) {
+ return x < FLT_EPSILON;
+}
+
+#endif
struct _Point {
double x;
@@ -134,8 +183,5 @@ 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);
-bool AlmostEqualUlps(float A, float B, int maxUlpsDiff);
-int UlpsDiff(float A, float B);
-int FloatAsInt(float A);
#endif // __DataTypes_h__
diff --git a/experimental/Intersection/EdgeMain.cpp b/experimental/Intersection/EdgeMain.cpp
index a87a5cdd51..0042c9e611 100644
--- a/experimental/Intersection/EdgeMain.cpp
+++ b/experimental/Intersection/EdgeMain.cpp
@@ -1,11 +1,13 @@
/*
- * EdgeMain.cpp
- * shapeops_edge
- *
- * Created by Cary Clark on 3/26/12.
- * Copyright 2012 __MyCompanyName__. All rights reserved.
+ * 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 "EdgeMain.h"
+#include "Intersection_Tests.h"
+int main(int argc, char* argv) {
+ Intersection_Tests();
+ return 0;
+} \ No newline at end of file
diff --git a/experimental/Intersection/EdgeMain.h b/experimental/Intersection/EdgeMain.h
deleted file mode 100644
index 0ab897c07a..0000000000
--- a/experimental/Intersection/EdgeMain.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "Intersection_Tests.h"
-
-int main(int argc, char* argv) {
- Intersection_Tests();
- return 0;
-} \ No newline at end of file
diff --git a/experimental/Intersection/EdgeWalker.cpp b/experimental/Intersection/EdgeWalker.cpp
index 002e84c599..4aff58f4a2 100644
--- a/experimental/Intersection/EdgeWalker.cpp
+++ b/experimental/Intersection/EdgeWalker.cpp
@@ -767,6 +767,7 @@ public:
fTs = src.fTs;
fTopIntercepts = src.fTopIntercepts;
fBottomIntercepts = src.fBottomIntercepts;
+ return *this;
}
// OPTIMIZATION: remove this function if it's never called
@@ -1409,7 +1410,8 @@ public:
curve[1] = &rh.fTangent;
curve[2] = &rh.fBelow;
}
-
+ // FIXME: code has been abandoned, incomplete....
+ return false;
}
bool abCompare(const SkPoint& a1, const SkPoint& a2, const SkPoint& b1,
@@ -1727,9 +1729,9 @@ public:
b2 = edge->fTangent.fX;
}
if (fabs(t1 - t2) > fabs(b1 - b2)) {
- ulps = UlpsDiff(t1, t2);
+ ulps = UlpsDiff((float) t1, (float) t2);
} else {
- ulps = UlpsDiff(b1, b2);
+ ulps = UlpsDiff((float) b1, (float) b2);
}
#if DEBUG_ADJUST_COINCIDENT
SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(),
diff --git a/experimental/Intersection/EdgeWalkerPolygons_Mismatches.h b/experimental/Intersection/EdgeWalkerPolygons_Mismatches.h
deleted file mode 100644
index d0381723aa..0000000000
--- a/experimental/Intersection/EdgeWalkerPolygons_Mismatches.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * EdgeWalkerPolygons_Mismatches.h
- * edge
- *
- * Created by Cary Clark on 3/6/12.
- * Copyright 2012 __MyCompanyName__. All rights reserved.
- *
- */
-
diff --git a/experimental/Intersection/EdgeWalkerPolygons_Test.cpp b/experimental/Intersection/EdgeWalkerPolygons_Test.cpp
index f3455eec33..80ef5270ce 100644
--- a/experimental/Intersection/EdgeWalkerPolygons_Test.cpp
+++ b/experimental/Intersection/EdgeWalkerPolygons_Test.cpp
@@ -367,7 +367,7 @@ static void testPathTriangleRendering() {
one.lineTo(0, 3);
one.lineTo(1, 2);
one.close();
- for (float x = .1f; x <= 2.9f; x += .1f) {
+ for (float x = .1f; x <= 2.9ff; x += .1f) {
SkDebugf("%s x=%g\n", __FUNCTION__, x);
two.moveTo(0, 0);
two.lineTo(x, x);
@@ -412,25 +412,25 @@ static void testSimplifySkinnyTriangle1() {
static void testSimplifySkinnyTriangle2() {
SkPath path, out;
#if 01
-path.moveTo(591.091064, 627.534851);
-path.lineTo(541.088135, 560.707642);
-path.lineTo(491.085175, 493.880310);
-path.lineTo(441.082214, 427.053101);
-//path.lineTo(591.091064, 627.534851);
+path.moveTo(591.091064f, 627.534851f);
+path.lineTo(541.088135f, 560.707642f);
+path.lineTo(491.085175f, 493.880310f);
+path.lineTo(441.082214f, 427.053101f);
+//path.lineTo(591.091064f, 627.534851f);
path.close();
#endif
-path.moveTo(317.093445, 592.013306);
-path.lineTo(366.316162, 542.986572);
-path.lineTo(416.051514, 486.978577);
-path.lineTo(465.786865, 430.970581);
-//path.lineTo(317.093445, 592.013306);
+path.moveTo(317.093445f, 592.013306f);
+path.lineTo(366.316162f, 542.986572f);
+path.lineTo(416.051514f, 486.978577f);
+path.lineTo(465.786865f, 430.970581f);
+//path.lineTo(317.093445f, 592.013306f);
path.close();
#if 0
-path.moveTo(289.392517, 517.138489);
-path.lineTo(249.886078, 508.598022);
-path.lineTo(217.110916, 450.916443);
-path.lineTo(196.621033, 394.917633);
-//path.lineTo(289.392517, 517.138489);
+path.moveTo(289.392517f, 517.138489f);
+path.lineTo(249.886078f, 508.598022f);
+path.lineTo(217.110916f, 450.916443f);
+path.lineTo(196.621033f, 394.917633f);
+//path.lineTo(289.392517f, 517.138489f);
path.close();
#endif
simplify(__FUNCTION__, path, true, out);
@@ -438,84 +438,84 @@ path.close();
static void testSimplifySkinnyTriangle3() {
SkPath path, out;
- path.moveTo(591, 627.534851);
- path.lineTo(541, 560.707642);
- path.lineTo(491, 493.880310);
- path.lineTo(441, 427.053101);
+ path.moveTo(591, 627.534851f);
+ path.lineTo(541, 560.707642f);
+ path.lineTo(491, 493.880310f);
+ path.lineTo(441, 427.053101f);
path.close();
- path.moveTo(317, 592.013306);
- path.lineTo(366, 542.986572);
- path.lineTo(416, 486.978577);
- path.lineTo(465, 430.970581);
+ path.moveTo(317, 592.013306f);
+ path.lineTo(366, 542.986572f);
+ path.lineTo(416, 486.978577f);
+ path.lineTo(465, 430.970581f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle4() {
SkPath path, out;
-path.moveTo(572.655212, 614.959961);
-path.lineTo(524.618896, 549.339600);
-path.lineTo(476.582581, 483.719269);
-path.lineTo(428.546265, 418.098938);
-path.lineTo(572.655212, 614.959961);
-path.close();
-path.moveTo(312.166382, 583.723083);
-path.lineTo(361.047791, 529.824219);
-path.lineTo(409.929230, 475.925354);
-path.lineTo(458.810669, 422.026520);
-path.lineTo(312.166382, 583.723083);
-path.close();
-path.moveTo(278.742737, 508.065643);
-path.lineTo(241.475800, 493.465118);
-path.lineTo(210.344177, 437.315125);
-path.lineTo(197.019455, 383.794556);
-path.lineTo(278.742737, 508.065643);
+path.moveTo(572.655212f, 614.959961f);
+path.lineTo(524.618896f, 549.339600f);
+path.lineTo(476.582581f, 483.719269f);
+path.lineTo(428.546265f, 418.098938f);
+path.lineTo(572.655212f, 614.959961f);
+path.close();
+path.moveTo(312.166382f, 583.723083f);
+path.lineTo(361.047791f, 529.824219f);
+path.lineTo(409.929230f, 475.925354f);
+path.lineTo(458.810669f, 422.026520f);
+path.lineTo(312.166382f, 583.723083f);
+path.close();
+path.moveTo(278.742737f, 508.065643f);
+path.lineTo(241.475800f, 493.465118f);
+path.lineTo(210.344177f, 437.315125f);
+path.lineTo(197.019455f, 383.794556f);
+path.lineTo(278.742737f, 508.065643f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle5() {
SkPath path, out;
-path.moveTo(554.690613, 602.286072);
-path.lineTo(508.590057, 537.906250);
-path.lineTo(462.489441, 473.526520);
-path.lineTo(416.388855, 409.146729);
-path.lineTo(554.690613, 602.286072);
-path.close();
-path.moveTo(307.216949, 575.189270);
-path.lineTo(355.826965, 516.804688);
-path.lineTo(403.815918, 464.990753);
-path.lineTo(451.804871, 413.176819);
-path.lineTo(307.216949, 575.189270);
-path.close();
-path.moveTo(271.998901, 521.301025);
-path.lineTo(234.619705, 499.687683);
-path.lineTo(203.059692, 441.332336);
-path.lineTo(195.994370, 386.856506);
-path.lineTo(271.998901, 521.301025);
+path.moveTo(554.690613f, 602.286072f);
+path.lineTo(508.590057f, 537.906250f);
+path.lineTo(462.489441f, 473.526520f);
+path.lineTo(416.388855f, 409.146729f);
+path.lineTo(554.690613f, 602.286072f);
+path.close();
+path.moveTo(307.216949f, 575.189270f);
+path.lineTo(355.826965f, 516.804688f);
+path.lineTo(403.815918f, 464.990753f);
+path.lineTo(451.804871f, 413.176819f);
+path.lineTo(307.216949f, 575.189270f);
+path.close();
+path.moveTo(271.998901f, 521.301025f);
+path.lineTo(234.619705f, 499.687683f);
+path.lineTo(203.059692f, 441.332336f);
+path.lineTo(195.994370f, 386.856506f);
+path.lineTo(271.998901f, 521.301025f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle6() {
SkPath path, out;
-path.moveTo(591.091064, 627.534851);
-path.lineTo(541.088135, 560.707642);
-path.lineTo(491.085175, 493.880310);
-path.lineTo(441.082214, 427.053101);
-path.lineTo(591.091064, 627.534851);
-path.close();
-path.moveTo(317.093445, 592.013306);
-path.lineTo(366.316162, 542.986572);
-path.lineTo(416.051514, 486.978577);
-path.lineTo(465.786865, 430.970581);
-path.lineTo(317.093445, 592.013306);
-path.close();
-path.moveTo(289.392517, 517.138489);
-path.lineTo(249.886078, 508.598022);
-path.lineTo(217.110916, 450.916443);
-path.lineTo(196.621033, 394.917633);
-path.lineTo(289.392517, 517.138489);
+path.moveTo(591.091064f, 627.534851f);
+path.lineTo(541.088135f, 560.707642f);
+path.lineTo(491.085175f, 493.880310f);
+path.lineTo(441.082214f, 427.053101f);
+path.lineTo(591.091064f, 627.534851f);
+path.close();
+path.moveTo(317.093445f, 592.013306f);
+path.lineTo(366.316162f, 542.986572f);
+path.lineTo(416.051514f, 486.978577f);
+path.lineTo(465.786865f, 430.970581f);
+path.lineTo(317.093445f, 592.013306f);
+path.close();
+path.moveTo(289.392517f, 517.138489f);
+path.lineTo(249.886078f, 508.598022f);
+path.lineTo(217.110916f, 450.916443f);
+path.lineTo(196.621033f, 394.917633f);
+path.lineTo(289.392517f, 517.138489f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
@@ -561,69 +561,69 @@ static void testSimplifyTriangle24() {
static void testSimplifySkinnyTriangle7() {
SkPath path, out;
-path.moveTo(487.502319, 550.811279);
-path.lineTo(448.826050, 491.720123);
-path.lineTo(410.149780, 432.628967);
-path.lineTo(371.473572, 373.537781);
-path.lineTo(487.502319, 550.811279);
-path.close();
-path.moveTo(295.817108, 532.655579);
-path.lineTo(342.896271, 485.912292);
-path.lineTo(389.975433, 439.169006);
-path.lineTo(437.054596, 392.425781);
-path.lineTo(295.817108, 532.655579);
-path.close();
-path.moveTo(239.726822, 575.025269);
-path.lineTo(204.117569, 521.429688);
-path.lineTo(171.275452, 454.110382);
-path.lineTo(193.328583, 397.859497);
-path.lineTo(239.726822, 575.025269);
+path.moveTo(487.502319f, 550.811279f);
+path.lineTo(448.826050f, 491.720123f);
+path.lineTo(410.149780f, 432.628967f);
+path.lineTo(371.473572f, 373.537781f);
+path.lineTo(487.502319f, 550.811279f);
+path.close();
+path.moveTo(295.817108f, 532.655579f);
+path.lineTo(342.896271f, 485.912292f);
+path.lineTo(389.975433f, 439.169006f);
+path.lineTo(437.054596f, 392.425781f);
+path.lineTo(295.817108f, 532.655579f);
+path.close();
+path.moveTo(239.726822f, 575.025269f);
+path.lineTo(204.117569f, 521.429688f);
+path.lineTo(171.275452f, 454.110382f);
+path.lineTo(193.328583f, 397.859497f);
+path.lineTo(239.726822f, 575.025269f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle8() {
SkPath path, out;
-path.moveTo(441.943115, 511.678040);
-path.lineTo(408.487549, 456.880920);
-path.lineTo(375.031952, 402.083801);
-path.lineTo(341.576385, 347.286682);
-path.lineTo(441.943115, 511.678040);
-path.close();
-path.moveTo(297.548492, 557.246704);
-path.lineTo(350.768494, 507.627014);
-path.lineTo(403.988525, 458.007385);
-path.lineTo(457.208527, 408.387695);
-path.lineTo(297.548492, 557.246704);
-path.close();
-path.moveTo(209.857895, 615.802979);
-path.lineTo(178.249481, 534.230347);
-path.lineTo(144.905640, 460.056824);
-path.lineTo(192.953125, 404.972900);
-path.lineTo(209.857895, 615.802979);
+path.moveTo(441.943115f, 511.678040f);
+path.lineTo(408.487549f, 456.880920f);
+path.lineTo(375.031952f, 402.083801f);
+path.lineTo(341.576385f, 347.286682f);
+path.lineTo(441.943115f, 511.678040f);
+path.close();
+path.moveTo(297.548492f, 557.246704f);
+path.lineTo(350.768494f, 507.627014f);
+path.lineTo(403.988525f, 458.007385f);
+path.lineTo(457.208527f, 408.387695f);
+path.lineTo(297.548492f, 557.246704f);
+path.close();
+path.moveTo(209.857895f, 615.802979f);
+path.lineTo(178.249481f, 534.230347f);
+path.lineTo(144.905640f, 460.056824f);
+path.lineTo(192.953125f, 404.972900f);
+path.lineTo(209.857895f, 615.802979f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle9() {
SkPath path, out;
-path.moveTo(439.867065, 528.291931);
-path.lineTo(405.413025, 469.107178);
-path.lineTo(370.958954, 409.922363);
-path.lineTo(336.504883, 350.737610);
-path.lineTo(439.867065, 528.291931);
+path.moveTo(439.867065f, 528.291931f);
+path.lineTo(405.413025f, 469.107178f);
+path.lineTo(370.958954f, 409.922363f);
+path.lineTo(336.504883f, 350.737610f);
+path.lineTo(439.867065f, 528.291931f);
path.close();
-path.moveTo(298.922455, 573.251953);
-path.lineTo(356.360962, 521.905090);
-path.lineTo(413.799438, 470.558228);
-path.lineTo(471.237915, 419.211365);
-path.lineTo(298.922455, 573.251953);
+path.moveTo(298.922455f, 573.251953f);
+path.lineTo(356.360962f, 521.905090f);
+path.lineTo(413.799438f, 470.558228f);
+path.lineTo(471.237915f, 419.211365f);
+path.lineTo(298.922455f, 573.251953f);
path.close();
-path.moveTo(187.200775, 643.035156);
-path.lineTo(159.713165, 540.993774);
-path.lineTo(126.257164, 462.198517);
-path.lineTo(193.534012, 409.266235);
-path.lineTo(187.200775, 643.035156);
+path.moveTo(187.200775f, 643.035156f);
+path.lineTo(159.713165f, 540.993774f);
+path.lineTo(126.257164f, 462.198517f);
+path.lineTo(193.534012f, 409.266235f);
+path.lineTo(187.200775f, 643.035156f);
path.close();
path.close();
simplify(__FUNCTION__, path, true, out);
@@ -632,87 +632,87 @@ path.close();
static void testSimplifySkinnyTriangle10() {
SkPath path, out;
#if 0
-path.moveTo(99.270325, 239.365234);
-path.lineTo(105.967056, 173.361206);
-path.lineTo(148.821381, 141.309891);
-path.lineTo(159.101013, 189.235138);
-path.lineTo(99.270325, 239.365234);
+path.moveTo(99.270325f, 239.365234f);
+path.lineTo(105.967056f, 173.361206f);
+path.lineTo(148.821381f, 141.309891f);
+path.lineTo(159.101013f, 189.235138f);
+path.lineTo(99.270325f, 239.365234f);
path.close();
#endif
-path.moveTo(213.673737, 413.292938);
-path.lineTo(225.200134, 343.616821);
-path.lineTo(236.726532, 273.940704);
-path.lineTo(219.386414, 231.373322);
-path.lineTo(213.673737, 413.292938);
-path.close();
-path.moveTo(43.485352, 308.984497);
-path.lineTo(122.610657, 305.950134);
-path.lineTo(201.735962, 302.915802);
-path.lineTo(280.861267, 299.881470);
-path.lineTo(43.485352, 308.984497);
+path.moveTo(213.673737f, 413.292938f);
+path.lineTo(225.200134f, 343.616821f);
+path.lineTo(236.726532f, 273.940704f);
+path.lineTo(219.386414f, 231.373322f);
+path.lineTo(213.673737f, 413.292938f);
+path.close();
+path.moveTo(43.485352f, 308.984497f);
+path.lineTo(122.610657f, 305.950134f);
+path.lineTo(201.735962f, 302.915802f);
+path.lineTo(280.861267f, 299.881470f);
+path.lineTo(43.485352f, 308.984497f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle11() {
SkPath path, out;
-path.moveTo(-177.878387, 265.368988);
-path.lineTo(-254.415771, 303.709961);
-path.lineTo(-317.465363, 271.325562);
-path.lineTo(-374.520386, 207.507660);
-path.lineTo(-177.878387, 265.368988);
-path.close();
-path.moveTo(-63.582489, -3.679123);
-path.lineTo(-134.496841, 26.434566);
-path.lineTo(-205.411209, 56.548256);
-path.lineTo(-276.325562, 86.661942);
-path.lineTo(-63.582489, -3.679123);
-path.close();
-path.moveTo(-57.078423, 162.633453);
-path.lineTo(-95.963928, 106.261139);
-path.lineTo(-134.849457, 49.888824);
-path.lineTo(-173.734955, -6.483480);
-path.lineTo(-57.078423, 162.633453);
+path.moveTo(-177.878387f, 265.368988f);
+path.lineTo(-254.415771f, 303.709961f);
+path.lineTo(-317.465363f, 271.325562f);
+path.lineTo(-374.520386f, 207.507660f);
+path.lineTo(-177.878387f, 265.368988f);
+path.close();
+path.moveTo(-63.582489f, -3.679123f);
+path.lineTo(-134.496841f, 26.434566f);
+path.lineTo(-205.411209f, 56.548256f);
+path.lineTo(-276.325562f, 86.661942f);
+path.lineTo(-63.582489f, -3.679123f);
+path.close();
+path.moveTo(-57.078423f, 162.633453f);
+path.lineTo(-95.963928f, 106.261139f);
+path.lineTo(-134.849457f, 49.888824f);
+path.lineTo(-173.734955f, -6.483480f);
+path.lineTo(-57.078423f, 162.633453f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle12() {
SkPath path, out;
-path.moveTo(98.666489, -94.295059);
-path.lineTo(156.584320, -61.939133);
-path.lineTo(174.672974, -12.343765);
-path.lineTo(158.622345, 52.028267);
-path.lineTo(98.666489, -94.295059);
-path.close();
-path.moveTo(-133.225616, -48.622055);
-path.lineTo(-73.855499, -10.375397);
-path.lineTo(-14.485367, 27.871277);
-path.lineTo(44.884750, 66.117935);
-path.lineTo(-133.225616, -48.622055);
-path.close();
-path.moveTo( 9.030045, -163.413132);
-path.lineTo(-19.605331, -89.588760);
-path.lineTo(-48.240707, -15.764404);
-path.lineTo(-76.876053, 58.059944);
-path.lineTo( 9.030045, -163.413132);
+path.moveTo(98.666489f, -94.295059f);
+path.lineTo(156.584320f, -61.939133f);
+path.lineTo(174.672974f, -12.343765f);
+path.lineTo(158.622345f, 52.028267f);
+path.lineTo(98.666489f, -94.295059f);
+path.close();
+path.moveTo(-133.225616f, -48.622055f);
+path.lineTo(-73.855499f, -10.375397f);
+path.lineTo(-14.485367f, 27.871277f);
+path.lineTo(44.884750f, 66.117935f);
+path.lineTo(-133.225616f, -48.622055f);
+path.close();
+path.moveTo( 9.030045f, -163.413132f);
+path.lineTo(-19.605331f, -89.588760f);
+path.lineTo(-48.240707f, -15.764404f);
+path.lineTo(-76.876053f, 58.059944f);
+path.lineTo( 9.030045f, -163.413132f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
static void testSimplifySkinnyTriangle13() {
SkPath path, out;
-path.moveTo(340.41568, -170.97171);
-path.lineTo(418.846893, -142.428329);
-path.lineTo(497.278107, -113.884933);
-path.lineTo(449.18222, -45.6723022);
-path.lineTo(340.41568, -170.97171);
-path.close();
-path.moveTo(326.610535, 34.0393639);
-path.lineTo(371.334595, -14.9620667);
-path.lineTo(416.058624, -63.9634857);
-path.lineTo(460.782654, -112.96492);
-path.lineTo(326.610535, 34.0393639);
+path.moveTo(340.41568f, -170.97171f);
+path.lineTo(418.846893f, -142.428329f);
+path.lineTo(497.278107f, -113.884933f);
+path.lineTo(449.18222f, -45.6723022f);
+path.lineTo(340.41568f, -170.97171f);
+path.close();
+path.moveTo(326.610535f, 34.0393639f);
+path.lineTo(371.334595f, -14.9620667f);
+path.lineTo(416.058624f, -63.9634857f);
+path.lineTo(460.782654f, -112.96492f);
+path.lineTo(326.610535f, 34.0393639f);
path.close();
simplify(__FUNCTION__, path, true, out);
}
diff --git a/experimental/Intersection/Intersection_Tests.cpp b/experimental/Intersection/Intersection_Tests.cpp
index e3c042cf27..4e4c8de90b 100644
--- a/experimental/Intersection/Intersection_Tests.cpp
+++ b/experimental/Intersection/Intersection_Tests.cpp
@@ -7,8 +7,13 @@ void testSimplify();
#define TEST_QUADS_FIRST 0
void Intersection_Tests() {
+ SimplifyFindTop_Test();
+ SimplifyAngle_Test();
+ QuadraticReduceOrder_Test();
+ QuadraticBezierClip_Test();
+ QuadraticIntersection_Test();
SimplifyAddIntersectingTs_Test();
-
+
cubecode_test(1);
convert_testx();
// tests are in dependency / complexity order
@@ -39,8 +44,6 @@ void Intersection_Tests() {
#endif
QuadraticCoincidence_Test();
- QuadraticReduceOrder_Test();
- QuadraticBezierClip_Test();
QuadraticIntersection_Test();
CubicParameterization_Test();
diff --git a/experimental/Intersection/Intersection_Tests.h b/experimental/Intersection/Intersection_Tests.h
index a2d9df6807..50e5600412 100644
--- a/experimental/Intersection/Intersection_Tests.h
+++ b/experimental/Intersection/Intersection_Tests.h
@@ -1,3 +1,7 @@
+#if !defined(IN_TEST)
+ #define IN_TEST 1
+#endif
+
void ActiveEdge_Test();
void ConvexHull_Test();
void ConvexHull_X_Test();
@@ -13,6 +17,9 @@ void LineIntersection_Test();
void LineParameter_Test();
void LineQuadraticIntersection_Test();
void SimplifyAddIntersectingTs_Test();
+void SimplifyAngle_Test();
+void SimplifyFindNext_Test();
+void SimplifyFindTop_Test();
void SimplifyDegenerate4x4TrianglesThreaded_Test();
void SimplifyNondegenerate4x4TrianglesThreaded_Test();
void SimplifyPolygonPaths_Test();
diff --git a/experimental/Intersection/LineParameters.h b/experimental/Intersection/LineParameters.h
index 188962263e..202ba46c4c 100644
--- a/experimental/Intersection/LineParameters.h
+++ b/experimental/Intersection/LineParameters.h
@@ -4,6 +4,15 @@
// computer-aided design - volume 22 number 9 november 1990 pp 538 - 549
// online at http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
+// This turns a line segment into a parameterized line, of the form
+// ax + by + c = 0
+// When a^2 + b^2 == 1, the line is normalized.
+// The distance to the line for (x, y) is d(x,y) = ax + by + c
+//
+// Note that the distances below are not necessarily normalized. To get the true
+// distance, it's necessary to either call normalize() after xxxEndPoints(), or
+// divide the result of xxxDistance() by sqrt(normalSquared())
+
class LineParameters {
public:
void cubicEndPoints(const Cubic& pts) {
@@ -42,7 +51,7 @@ public:
bool normalize() {
double normal = sqrt(normalSquared());
- if (normal < SquaredEpsilon) {
+ if (approximately_zero_squared(normal)) {
a = b = c = 0;
return false;
}
@@ -87,6 +96,7 @@ public:
double pointDistance(const _Point& pt) {
return a * pt.x + b * pt.y + c;
}
+
private:
double a;
double b;
diff --git a/experimental/Intersection/QuadraticBezierClip_Test.cpp b/experimental/Intersection/QuadraticBezierClip_Test.cpp
index a9561fd259..289ebdb587 100644
--- a/experimental/Intersection/QuadraticBezierClip_Test.cpp
+++ b/experimental/Intersection/QuadraticBezierClip_Test.cpp
@@ -2,7 +2,24 @@
#include "Intersection_Tests.h"
#include "QuadraticIntersection_TestData.h"
-void QuadraticBezierClip_Test() {
+static const Quadratic testSet[] = {
+ {{8.0000000000000071, 8.0000000000000071},
+ {8.7289570079366854, 8.7289570079366889},
+ {9.3914917259458743, 9.0593802763083691}},
+ {{8.0000000000000142, 8.0000000000000142},
+ {8.1250000000000107, 8.1250000000000071},
+ {8.2500000000000071, 8.2187500000000053}}
+};
+
+static void oneOffTest() {
+ const Quadratic& quad1 = testSet[0];
+ const Quadratic& quad2 = testSet[1];
+ double minT = 0;
+ double maxT = 1;
+ bezier_clip(quad1, quad2, minT, maxT);
+}
+
+void standardTestCases() {
for (size_t index = 0; index < quadraticTests_count; ++index) {
const Quadratic& quad1 = quadraticTests[index][0];
const Quadratic& quad2 = quadraticTests[index][1];
@@ -22,3 +39,8 @@ void QuadraticBezierClip_Test() {
}
}
}
+
+void QuadraticBezierClip_Test() {
+ oneOffTest();
+ standardTestCases();
+}
diff --git a/experimental/Intersection/QuadraticIntersection_Test.cpp b/experimental/Intersection/QuadraticIntersection_Test.cpp
index 63b7338300..582b195633 100644
--- a/experimental/Intersection/QuadraticIntersection_Test.cpp
+++ b/experimental/Intersection/QuadraticIntersection_Test.cpp
@@ -3,10 +3,11 @@
#include "Intersections.h"
#include "QuadraticIntersection_TestData.h"
#include "TestUtilities.h"
+#include "SkTypes.h"
const int firstQuadIntersectionTest = 9;
-void QuadraticIntersection_Test() {
+static void standardTestCases() {
for (size_t index = firstQuadIntersectionTest; index < quadraticTests_count; ++index) {
const Quadratic& quad1 = quadraticTests[index][0];
const Quadratic& quad2 = quadraticTests[index][1];
@@ -44,3 +45,49 @@ void QuadraticIntersection_Test() {
}
}
+static const Quadratic testSet[] = {
+ {{8, 8}, {10, 10}, {8, -10}},
+ {{8, 8}, {12, 12}, {14, 4}},
+ {{8, 8}, {9, 9}, {10, 8}}
+};
+
+const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
+
+static void oneOffTest() {
+ for (int outer = 0; outer < testSetCount - 1; ++outer) {
+ for (int inner = outer + 1; inner < testSetCount; ++inner) {
+ const Quadratic& quad1 = testSet[outer];
+ const Quadratic& quad2 = testSet[inner];
+ Intersections intersections;
+ intersect(quad1, quad2, intersections);
+ if (!intersections.intersected()) {
+ SkDebugf("%s no intersection!\n", __FUNCTION__);
+ }
+ for (int pt = 0; pt < intersections.used(); ++pt) {
+ double tt1 = intersections.fT[0][pt];
+ double tx1, ty1;
+ xy_at_t(quad1, tt1, tx1, ty1);
+ double tt2 = intersections.fT[1][pt];
+ double tx2, ty2;
+ xy_at_t(quad2, tt2, tx2, ty2);
+ if (!approximately_equal(tx1, tx2)) {
+ SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+ __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+ SkASSERT(0);
+ }
+ if (!approximately_equal(ty1, ty2)) {
+ SkDebugf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+ __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+ SkASSERT(0);
+ }
+ SkDebugf("%s [%d][%d] t1=%1.9g (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
+ outer, inner, tt1, tx1, tx2, tt2);
+ }
+ }
+ }
+}
+
+void QuadraticIntersection_Test() {
+ oneOffTest();
+ standardTestCases();
+} \ No newline at end of file
diff --git a/experimental/Intersection/QuadraticIntersection_TestData.cpp b/experimental/Intersection/QuadraticIntersection_TestData.cpp
index 2fa8a98a95..9ec585a730 100644
--- a/experimental/Intersection/QuadraticIntersection_TestData.cpp
+++ b/experimental/Intersection/QuadraticIntersection_TestData.cpp
@@ -1,10 +1,8 @@
/*
- * QuadraticIntersection_TestData.cpp
- * edge
- *
- * Created by Cary Clark on 1/10/12.
- * Copyright 2012 __MyCompanyName__. All rights reserved.
+ * 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 "QuadraticIntersection_TestData.h"
diff --git a/experimental/Intersection/QuadraticIntersection_TestData.h b/experimental/Intersection/QuadraticIntersection_TestData.h
index 54f084cff4..1852d1709c 100644
--- a/experimental/Intersection/QuadraticIntersection_TestData.h
+++ b/experimental/Intersection/QuadraticIntersection_TestData.h
@@ -1,3 +1,7 @@
+#if !defined(IN_TEST)
+ #define IN_TEST 1
+#endif
+
#include "DataTypes.h"
extern const Quadratic quadraticLines[];
diff --git a/experimental/Intersection/QuadraticReduceOrder.cpp b/experimental/Intersection/QuadraticReduceOrder.cpp
index f85cdc86eb..7429f6edaf 100644
--- a/experimental/Intersection/QuadraticReduceOrder.cpp
+++ b/experimental/Intersection/QuadraticReduceOrder.cpp
@@ -110,11 +110,10 @@ static int check_linear(const Quadratic& quad, Quadratic& reduction,
bool isLinear(const Quadratic& quad, int startIndex, int endIndex) {
LineParameters lineParameters;
lineParameters.quadEndPoints(quad, startIndex, endIndex);
- double normalSquared = lineParameters.normalSquared();
- double distance = lineParameters.controlPtDistance(quad); // not normalized
- double limit = normalSquared * SquaredEpsilon;
- double distSq = distance * distance;
- return distSq <= limit;
+ // FIXME: maybe it's possible to avoid this and compare non-normalized
+ lineParameters.normalize();
+ double distance = lineParameters.controlPtDistance(quad);
+ return approximately_zero(distance);
}
// reduce to a quadratic or smaller
diff --git a/experimental/Intersection/QuadraticReduceOrder_Test.cpp b/experimental/Intersection/QuadraticReduceOrder_Test.cpp
index 397342b9a8..b716cdb1a8 100644
--- a/experimental/Intersection/QuadraticReduceOrder_Test.cpp
+++ b/experimental/Intersection/QuadraticReduceOrder_Test.cpp
@@ -2,8 +2,27 @@
#include "Intersection_Tests.h"
#include "QuadraticIntersection_TestData.h"
#include "TestUtilities.h"
+#include "SkTypes.h"
-void QuadraticReduceOrder_Test() {
+static const Quadratic testSet[] = {
+ {{1, 1}, {2, 2}, {1, 1.000003}},
+ {{1, 0}, {2, 6}, {3, 0}}
+};
+
+static const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
+
+
+static void oneOffTest() {
+ SkDebugf("%s FLT_EPSILON=%1.9g\n", __FUNCTION__, FLT_EPSILON);
+ for (int index = 0; index < testSetCount; ++index) {
+ const Quadratic& quad = testSet[index];
+ Quadratic reduce;
+ int order = reduceOrder(quad, reduce);
+ SkASSERT(order == 3);
+ }
+}
+
+static void standardTestCases() {
size_t index;
Quadratic reduce;
int order;
@@ -36,3 +55,8 @@ void QuadraticReduceOrder_Test() {
}
}
}
+
+void QuadraticReduceOrder_Test() {
+ oneOffTest();
+ standardTestCases();
+}
diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp
index cb2e3bc427..444b32d8d6 100644
--- a/experimental/Intersection/Simplify.cpp
+++ b/experimental/Intersection/Simplify.cpp
@@ -4,16 +4,7 @@
* Use of this source code is governed by a BSD-style license that can be
* 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 <algorithm> // used for std::min
+#include "Simplify.h"
#undef SkASSERT
#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
@@ -21,8 +12,8 @@
// Terminology:
// A Path contains one of more Contours
// A Contour is made up of Segment array
-// A Segment is described by a Verb and a Point array
-// A Verb is one of Line, Quad(ratic), and Cubic
+// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
+// A Verb is one of Line, Quad(ratic), or Cubic
// A Segment contains a Span array
// A Span is describes a portion of a Segment using starting and ending T
// T values range from 0 to 1, where 0 is the first Point in the Segment
@@ -263,6 +254,14 @@ static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
sub[3].fY = SkDoubleToScalar(dst[3].y);
}
+static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
+ SkPoint []) = {
+ NULL,
+ LineSubDivide,
+ QuadSubDivide,
+ CubicSubDivide
+};
+
static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
SkRect& bounds) {
SkPoint dst[3];
@@ -291,6 +290,9 @@ static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
{a[2].fX, a[2].fY}};
Quadratic dst;
int order = reduceOrder(aQuad, dst);
+ if (order == 3) {
+ return SkPath::kQuad_Verb;
+ }
for (int index = 0; index < order; ++index) {
SkPoint* pt = reducePts.append();
pt->fX = SkDoubleToScalar(dst[index].x);
@@ -305,6 +307,9 @@ static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
{a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
Cubic dst;
int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
+ if (order == 4) {
+ return SkPath::kCubic_Verb;
+ }
for (int index = 0; index < order; ++index) {
SkPoint* pt = reducePts.append();
pt->fX = SkDoubleToScalar(dst[index].x);
@@ -330,19 +335,19 @@ static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
double x[2];
xy_at_t(aLine, startT, x[0], *(double*) 0);
xy_at_t(aLine, endT, x[0], *(double*) 0);
- return startT < endT ? startT : endT;
+ return startT < endT ? (float) startT : (float) endT;
}
static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
{a[2].fX, a[2].fY}};
- return leftMostT(aQuad, startT, endT);
+ return (float) leftMostT(aQuad, startT, endT);
}
static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
{a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
- return leftMostT(aCubic, startT, endT);
+ return (float) leftMostT(aCubic, startT, endT);
}
static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
@@ -359,57 +364,166 @@ static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
return implicit_matches_ulps(aLine, bLine, 32);
}
+class Segment;
+
// sorting angles
// given angles of {dx dy ddx ddy dddx dddy} sort them
class Angle {
public:
+ // FIXME: this is bogus for quads and cubics
+ // if the quads and cubics' line from end pt to ctrl pt are coincident,
+ // there's no obvious way to determine the curve ordering from the
+ // derivatives alone. In particular, if one quadratic's coincident tangent
+ // is longer than the other curve, the final control point can place the
+ // longer curve on either side of the shorter one.
+ // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
+ // may provide some help, but nothing has been figured out yet.
bool operator<(const Angle& rh) const {
- if ((dy < 0) ^ (rh.dy < 0)) {
- return dy < 0;
+ if ((fDy < 0) ^ (rh.fDy < 0)) {
+ return fDy < 0;
+ }
+ if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
+ return fDx < rh.fDx;
}
- SkScalar cmp = dx * rh.dy - rh.dx * dy;
+ SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
if (cmp) {
return cmp < 0;
}
- if ((ddy < 0) ^ (rh.ddy < 0)) {
- return ddy < 0;
+ if ((fDDy < 0) ^ (rh.fDDy < 0)) {
+ return fDDy < 0;
+ }
+ if (fDDy == 0 && rh.fDDy == 0 && fDDx != rh.fDDx) {
+ return fDDx < rh.fDDx;
}
- cmp = ddx * rh.ddy - rh.ddx * ddy;
+ cmp = fDDx * rh.fDDy - rh.fDDx * fDDy;
if (cmp) {
return cmp < 0;
}
- if ((dddy < 0) ^ (rh.dddy < 0)) {
- return ddy < 0;
+ if ((fDDDy < 0) ^ (rh.fDDDy < 0)) {
+ return fDDDy < 0;
}
- return dddx * rh.dddy < rh.dddx * dddy;
+ if (fDDDy == 0 && rh.fDDDy == 0) {
+ return fDDDx < rh.fDDDx;
+ }
+ return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
+ }
+
+ int end() const {
+ return fEnd;
}
- void set(SkPoint* pts, SkPath::Verb verb) {
- dx = pts[1].fX - pts[0].fX; // b - a
- dy = pts[1].fY - pts[0].fY;
+ // since all angles share a point, this needs to know which point
+ // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
+ // practically, this should only be called by addAngle
+ void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
+ int start, int end, bool coincident) {
+ SkASSERT(start != end);
+ fSegment = segment;
+ fStart = start;
+ fEnd = end;
+ fCoincident = coincident;
+ fDx = pts[1].fX - pts[0].fX; // b - a
+ fDy = pts[1].fY - pts[0].fY;
if (verb == SkPath::kLine_Verb) {
- ddx = ddy = dddx = dddy = 0;
+ fDDx = fDDy = fDDDx = fDDDy = 0;
return;
}
- ddx = pts[2].fX - pts[1].fX - dx; // a - 2b + c
- ddy = pts[2].fY - pts[2].fY - dy;
+ fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
+ fDDy = pts[2].fY - pts[1].fY - fDy;
if (verb == SkPath::kQuad_Verb) {
- dddx = dddy = 0;
+ fDDDx = fDDDy = 0;
return;
}
- dddx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
- dddy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
+ fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
+ fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
+ }
+
+ // noncoincident quads/cubics may have the same initial angle
+ // as lines, so must sort by derivatives as well
+ // if flatness turns out to be a reasonable way to sort, use the below:
+ void setFlat(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
+ int start, int end, bool coincident) {
+ fSegment = segment;
+ fStart = start;
+ fEnd = end;
+ fCoincident = coincident;
+ fDx = pts[1].fX - pts[0].fX; // b - a
+ fDy = pts[1].fY - pts[0].fY;
+ if (verb == SkPath::kLine_Verb) {
+ fDDx = fDDy = fDDDx = fDDDy = 0;
+ return;
+ }
+ if (verb == SkPath::kQuad_Verb) {
+ int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
+ int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
+ int larger = std::max(abs(uplsX), abs(uplsY));
+ int shift = 0;
+ double flatT;
+ SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
+ LineParameters implicitLine;
+ _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
+ implicitLine.lineEndPoints(tangent);
+ implicitLine.normalize();
+ while (larger > UlpsEpsilon * 1024) {
+ larger >>= 2;
+ ++shift;
+ flatT = 0.5 / (1 << shift);
+ QuadXYAtT(pts, flatT, &ddPt);
+ _Point _pt = {ddPt.fX, ddPt.fY};
+ double distance = implicitLine.pointDistance(_pt);
+ if (approximately_zero(distance)) {
+ SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
+ break;
+ }
+ }
+ flatT = 0.5 / (1 << shift);
+ QuadXYAtT(pts, flatT, &ddPt);
+ fDDx = ddPt.fX - pts[0].fX;
+ fDDy = ddPt.fY - pts[0].fY;
+ SkASSERT(fDDx != 0 || fDDy != 0);
+ fDDDx = fDDDy = 0;
+ return;
+ }
+ SkASSERT(0); // FIXME: add cubic case
+ }
+
+ const Segment* segment() const {
+ return fSegment;
+ }
+
+ int sign() const {
+ int result = fStart - fEnd >> 31 | 1;
+ SkASSERT(result == fStart < fEnd ? -1 : 1);
+ return result;
+ }
+
+ int start() const {
+ return fStart;
}
private:
- SkScalar dx;
- SkScalar dy;
- SkScalar ddx;
- SkScalar ddy;
- SkScalar dddx;
- SkScalar dddy;
+ SkScalar fDx;
+ SkScalar fDy;
+ SkScalar fDDx;
+ SkScalar fDDy;
+ SkScalar fDDDx;
+ SkScalar fDDDy;
+ const Segment* fSegment;
+ int fStart;
+ int fEnd;
+ bool fCoincident;
};
+static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
+ int angleCount = angles.count();
+ int angleIndex;
+ angleList.setReserve(angleCount);
+ for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
+ *angleList.append() = &angles[angleIndex];
+ }
+ QSort<Angle>(angleList.begin(), angleList.end() - 1);
+}
+
// Bounds, unlike Rect, does not consider a vertical line to be empty.
struct Bounds : public SkRect {
static bool Intersects(const Bounds& a, const Bounds& b) {
@@ -429,7 +543,8 @@ struct Bounds : public SkRect {
Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
{a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
dRect.setBounds(cubic);
- set(dRect.left, dRect.top, dRect.right, dRect.bottom);
+ set((float) dRect.left, (float) dRect.top, (float) dRect.right,
+ (float) dRect.bottom);
}
void setQuadBounds(const SkPoint a[3]) {
@@ -437,16 +552,16 @@ struct Bounds : public SkRect {
{a[2].fX, a[2].fY}};
_Rect dRect;
dRect.setBounds(quad);
- set(dRect.left, dRect.top, dRect.right, dRect.bottom);
+ set((float) dRect.left, (float) dRect.top, (float) dRect.right,
+ (float) dRect.bottom);
}
};
-class Segment;
-
struct Span {
double fT;
Segment* fOther;
- double fOtherT;
+ double fOtherT; // value at fOther[fOtherIndex].fT
+ int fOtherIndex; // can't be used during intersection
int fWinding; // accumulated from contours surrounding this one
// OPTIMIZATION: done needs only 2 bits (values are -1, 0, 1)
int fDone; // set when t to t+fDone is processed
@@ -462,31 +577,34 @@ public:
#endif
}
- void addAngle(SkTDArray<Angle>& angles, double start, double end) {
- // FIXME complete this
- // start here;
+ void addAngle(SkTDArray<Angle>& angles, int start, int end,
+ bool coincident) const {
+ SkASSERT(start != end);
+ SkPoint edge[4];
+ (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
+ Angle* angle = angles.append();
+ angle->set(edge, fVerb, this, start, end, coincident);
}
- bool addCubic(const SkPoint pts[4]) {
- fPts = pts;
- fVerb = SkPath::kCubic_Verb;
+ void addCubic(const SkPoint pts[4]) {
+ init(pts, SkPath::kCubic_Verb);
fBounds.setCubicBounds(pts);
}
- bool addLine(const SkPoint pts[2]) {
- fPts = pts;
- fVerb = SkPath::kLine_Verb;
+ void addLine(const SkPoint pts[2]) {
+ init(pts, SkPath::kLine_Verb);
fBounds.set(pts, 2);
}
// add 2 to edge or out of range values to get T extremes
- void addOtherT(int index, double other) {
- fTs[index].fOtherT = other;
+ void addOtherT(int index, double otherT, int otherIndex) {
+ Span& span = fTs[index];
+ span.fOtherT = otherT;
+ span.fOtherIndex = otherIndex;
}
- bool addQuad(const SkPoint pts[3]) {
- fPts = pts;
- fVerb = SkPath::kQuad_Verb;
+ void addQuad(const SkPoint pts[3]) {
+ init(pts, SkPath::kQuad_Verb);
fBounds.setQuadBounds(pts);
}
@@ -522,10 +640,70 @@ finish:
return insertedAt;
}
+ void addTwoAngles(int start, int end, const SkPoint& endLoc,
+ const Span* endSpan, bool startCo, SkTDArray<Angle>& angles) const {
+ // add edge leading into junction
+ addAngle(angles, end, start, startCo);
+ // add edge leading away from junction
+ bool coincident;
+ int step = start < end ? 1 : -1;
+ int tIndex = nextSpan(end, step, endLoc, endSpan, NULL, coincident);
+ if (tIndex >= 0) {
+ lastSpan(tIndex, step, endLoc, endSpan, coincident);
+ addAngle(angles, end, tIndex, coincident);
+ }
+ }
+
const Bounds& bounds() const {
return fBounds;
}
+ void buildAngles(int index, int last, int step, const SkPoint& loc,
+ SkTDArray<Angle>& angles) const {
+ SkASSERT(index - last != 0);
+ SkASSERT((index - last < 0) ^ (step < 0));
+ int end = last + step;
+ do {
+ Span* span = &fTs[index];
+ Segment* other = span->fOther;
+ if (other->fDone) {
+ continue;
+ }
+ // if there is only one live crossing, and no coincidence, continue
+ // in the same direction
+ // if there is coincidence, the only choice may be to reverse direction
+ // find edge on either side of intersection
+ int oIndex = span->fOtherIndex;
+ Span* otherSpan = &other->fTs[oIndex];
+ SkASSERT(otherSpan->fOther == this);
+ // if done == -1, prior span has already been processed
+ bool otherCo;
+ int localStep = step;
+ int next = other->nextSpan(oIndex, localStep, loc, otherSpan,
+ NULL, otherCo);
+ if (next < 0) {
+ localStep = -step;
+ next = other->nextSpan(oIndex, localStep, loc, otherSpan,
+ NULL, otherCo);
+ }
+ other->lastSpan(next, localStep, loc, otherSpan, otherCo);
+ // add candidate into and away from junction
+ other->addTwoAngles(next, oIndex, loc, span, otherCo, angles);
+
+ } while ((index += step) != end);
+ }
+
+ // figure out if the segment's ascending T goes clockwise or not
+ // not enough context to write this as shown
+ // instead, add all segments meeting at the top
+ // sort them using buildAngleList
+ // find the first in the sort
+ // see if ascendingT goes to top
+ bool clockwise(int tIndex) const {
+ SkASSERT(0); // incomplete
+ return false;
+ }
+
bool done() const {
return fDone;
}
@@ -545,13 +723,13 @@ finish:
SkASSERT(0); // should never get here
return -1;
}
-
+
// start is the index of the beginning T of this edge
// it is guaranteed to have an end which describes a non-zero length (?)
// winding -1 means ccw, 1 means cw
// step is in/out -1 or 1
// spanIndex is returned
- Segment* findNext(int start, int winding, int& step, int& spanIndex) {
+ Segment* findNext(int start, int winding, int& step, int& spanIndex) const {
SkASSERT(step == 1 || step == -1);
int count = fTs.count();
SkASSERT(step > 0 ? start < count - 1 : start > 0);
@@ -565,129 +743,83 @@ finish:
SkPoint startLoc; // OPTIMIZATION: store this in the t span?
xyAtT(startSpan->fT, &startLoc);
SkPoint endLoc;
- Span* endSpan;
- int end = nextSpan(start, step, startLoc, startSpan, &endLoc, &endSpan);
+ bool startCo;
+ int end = nextSpan(start, step, startLoc, startSpan, &endLoc, startCo);
// if we hit the end looking for span end, is that always an error?
SkASSERT(step > 0 ? end + 1 < count : end - 1 >= 0);
// preflight for coincidence -- if present, it may change winding
// considerations and whether reversed edges can be followed
- bool foundCoincident = false;
- int last = lastSpan(end, step, &startLoc, startSpan, foundCoincident);
+ int last = lastSpan(end, step, startLoc, startSpan, startCo);
// Discard opposing direction candidates if no coincidence was found.
+ Span* endSpan = &fTs[end];
int candidateCount = abs(last - end);
+ Segment* other;
if (candidateCount == 1) {
- SkASSERT(!foundCoincident);
+ SkASSERT(!startCo);
// move in winding direction until edge in correct direction
// balance wrong direction edges before finding correct one
// this requres that the intersection is angularly sorted
// for a single intersection, special case -- choose the opposite
// edge that steps the same
- Segment* other = endSpan->fOther;
+ other = endSpan->fOther;
SkASSERT(!other->fDone);
- spanIndex = other->matchSpan(this, endSpan->fT);
- SkASSERT(step < 0 ? spanIndex > 0 : spanIndex < other->fTs.count() - 1);
+ spanIndex = endSpan->fOtherIndex;
+ SkASSERT(step < 0 ? spanIndex > 0
+ : spanIndex < other->fTs.count() - 1);
return other;
}
- // find the next T that describes a length
+ // more than one viable candidate -- measure angles to find best
SkTDArray<Angle> angles;
- Segment* segmentCandidate = NULL;
- int spanCandidate = -1;
- int directionCandidate;
+ SkASSERT(end - start != 0);
+ SkASSERT((end - start < 0) ^ (step < 0));
+ addTwoAngles(start, end, endLoc, endSpan, startCo, angles);
+ buildAngles(end, last, step, endLoc, angles);
+ SkTDArray<Angle*> sorted;
+ sortAngles(angles, sorted);
+ // find the starting edge
+ int startIndex = -1;
+ int angleCount = angles.count();
+ int angleIndex;
+ const Angle* angle;
+ for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
+ angle = sorted[angleIndex];
+ if (angle->segment() == this && angle->start() == end &&
+ angle->end() == start) {
+ startIndex = angleIndex;
+ break;
+ }
+ }
+ SkASSERT(startIndex >= 0);
+ winding += angle->sign();
+ int nextIndex = startIndex;
+ const Angle* nextAngle;
do {
- endSpan = &fTs[end];
- Segment* other = endSpan->fOther;
- if (other->fDone) {
- continue;
+ if (++nextIndex == angleCount) {
+ nextIndex = 0;
}
- // if there is only one live crossing, and no coincidence, continue
- // in the same direction
- // if there is coincidence, the only choice may be to reverse direction
- // find edge on either side of intersection
- int oIndex = other->matchSpan(this, endSpan->fT);
- int oCount = other->fTs.count();
- do {
- Span& otherSpan = other->fTs[oIndex];
- // if done == -1, prior span has already been processed
- int next = other->nextSpan(oIndex, step, endLoc, &otherSpan,
- NULL, NULL);
- if (next < 0) {
- continue;
- }
- bool otherIsCoincident;
- last = other->lastSpan(next, step, &endLoc, &otherSpan,
- otherIsCoincident);
- if (last < 0) {
- continue;
- }
- #if 0
- Span& prior = other->fTs[oIndex - 1];
- if (otherSpan.fDone >= 0 && oIndex > 0) {
- // FIXME: this needs to loop on -- until t && pt are different
- if (prior.fDone > 0) {
- continue;
- }
-
- }
- } else { // step == 1
- if (otherSpan.fDone <= 0 && oIndex < oCount - 1) {
- // FIXME: this needs to loop on ++ until t && pt are different
- Span& next = other->fTs[oIndex + 1];
- if (next.fDone < 0) {
- continue;
- }
- }
- }
- #endif
- if (!segmentCandidate) {
- segmentCandidate = other;
- spanCandidate = oIndex;
- directionCandidate = step;
- continue;
- }
- // there's two or more matches
- if (spanCandidate >= 0) { // retrieve first stored candidate
- // add edge leading into junction
- addAngle(angles, endSpan->fT, startSpan->fT);
- // add edge leading away from junction
- double nextT = nextSpan(end, step, endLoc, endSpan, NULL,
- NULL);
- if (nextT >= 0) {
- addAngle(angles, endSpan->fT, nextT);
- }
- // add first stored candidate into junction
- segmentCandidate->addAngle(angles,
- segmentCandidate->fTs[spanCandidate - 1].fT,
- segmentCandidate->fTs[spanCandidate].fT);
- // add first stored candidate away from junction
- segmentCandidate->addAngle(angles,
- segmentCandidate->fTs[spanCandidate].fT,
- segmentCandidate->fTs[spanCandidate + 1].fT);
- }
- // add candidate into and away from junction
-
-
- // start here;
- // more than once viable candidate -- need to
- // measure angles to find best
- // noncoincident quads/cubics may have the same initial angle
- // as lines, so must sort by derivatives as well
- // while we're here, figure out all connections given the
- // initial winding info
- // so the span needs to contain the pairing info found here
- // this should include the winding computed for the edge, and
- // what edge it connects to, and whether it is discarded
- // (maybe discarded == abs(winding) > 1) ?
- // only need derivatives for duration of sorting, add a new struct
- // for pairings, remove extra spans that have zero length and
- // reference an unused other
- // for coincident, the last span on the other may be marked done
- // (always?)
- } while (++oIndex < oCount);
- } while ((end += step) != last);
+ SkASSERT(nextIndex != startIndex); // should never wrap around
+ nextAngle = sorted[nextIndex];
+ // OPTIMIZATION: Figure out all connections, given the initial
+ // winding info (e.g., accumulate winding in span for reuse)
+ winding -= nextAngle->sign();
+ } while (winding);
+ // FIXME: get rid of cast
+ return const_cast<Segment*>(nextAngle->segment());
+
+ // so the span needs to contain the pairing info found here
+ // this should include the winding computed for the edge, and
+ // what edge it connects to, and whether it is discarded
+ // (maybe discarded == abs(winding) > 1) ?
+ // only need derivatives for duration of sorting, add a new struct
+ // for pairings, remove extra spans that have zero length and
+ // reference an unused other
+ // for coincident, the last span on the other may be marked done
+ // (always?)
+
// if loop is exhausted, contour may be closed.
// FIXME: pass in close point so we can check for closure
@@ -760,8 +892,7 @@ finish:
}
continue;
}
- if (toSpan.fOther == mOther && toSpan.fOtherT
- == moSpan.fT) {
+ if (toSpan.fOther == mOther && toSpan.fOtherT == moSpan.fT) {
found = true;
break;
}
@@ -786,28 +917,15 @@ finish:
}
}
- int findByT(double t, const Segment* match) const {
- // OPTIMIZATION: bsearch if count is honkin huge
- int count = fTs.count();
- for (int index = 0; index < count; ++index) {
- const Span& span = fTs[index];
- if (t == span.fT && match == span.fOther) {
- return index;
- }
- }
- SkASSERT(0); // should never get here
- return -1;
- }
-
// find the adjacent T that is leftmost, with a point != base
int findLefty(int tIndex, const SkPoint& base) const {
- int bestTIndex;
+ int bestTIndex = -1;
SkPoint test;
- SkScalar bestX = DBL_MAX;
+ SkScalar bestX = FLT_MAX;
int testTIndex = tIndex;
while (--testTIndex >= 0) {
- xyAtT(testTIndex, &test);
- if (test != base) {
+ xyAtT(fTs[testTIndex].fT, &test);
+ if (test == base) {
continue;
}
bestX = test.fX;
@@ -817,20 +935,23 @@ finish:
int count = fTs.count();
testTIndex = tIndex;
while (++testTIndex < count) {
- xyAtT(testTIndex, &test);
+ xyAtT(fTs[testTIndex].fT, &test);
if (test == base) {
continue;
}
- return bestX > test.fX ? testTIndex : bestTIndex;
+ if (bestX > test.fX) {
+ bestTIndex = testTIndex;
+ }
+ break;
}
- SkASSERT(0); // can't get here (?)
- return -1;
+ SkASSERT(bestTIndex != -1);
+ return bestTIndex;
}
// OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
// and use more concise logic like the old edge walker code?
// FIXME: this needs to deal with coincident edges
- const Segment* findTop(int& tIndex) const {
+ const Segment* findTop(int& tIndex, int& direction) const {
// iterate through T intersections and return topmost
// topmost tangent from y-min to first pt is closer to horizontal
int firstT = 0;
@@ -841,7 +962,7 @@ finish:
for (index = 1; index < count; ++index) {
const Span& span = fTs[index];
double t = span.fT;
- SkScalar yIntercept = yAtT(t);
+ SkScalar yIntercept = t == 1 ? fPts[fVerb].fY : yAtT(t);
if (topY > yIntercept) {
topY = yIntercept;
firstT = lastT = index;
@@ -850,42 +971,63 @@ finish:
}
}
// if there's only a pair of segments, go with the endpoint chosen above
- if (firstT == lastT && (firstT == 0 || firstT == count - 1)) {
+ if (firstT == lastT) {
tIndex = firstT;
return this;
}
+ // sort the edges to find the leftmost
+ SkPoint startLoc; // OPTIMIZATION: store this in the t span?
+ const Span* startSpan = &fTs[firstT];
+ xyAtT(startSpan->fT, &startLoc);
+ SkPoint endLoc;
+ bool nextCo;
+ int end = nextSpan(firstT, 1, startLoc, startSpan, &endLoc, nextCo);
+ if (end == -1) {
+ end = nextSpan(firstT, -1, startLoc, startSpan, &endLoc, nextCo);
+ }
// if the topmost T is not on end, or is three-way or more, find left
- SkPoint leftBase;
- xyAtT(firstT, &leftBase);
- int tLeft = findLefty(firstT, leftBase);
- const Segment* leftSegment = this;
// look for left-ness from tLeft to firstT (matching y of other)
- for (index = firstT; index <= lastT; ++index) {
- const Segment* other = fTs[index].fOther;
- double otherT = fTs[index].fOtherT;
- int otherTIndex = other->findByT(otherT, this);
- // pick companionT closest (but not too close) on either side
- int otherTLeft = other->findLefty(otherTIndex, leftBase);
- // within this span, find highest y
- SkPoint testPt, otherPt;
- testPt.fY = yAtT(tLeft);
- otherPt.fY = other->yAtT(otherTLeft);
- // FIXME: incomplete
- // find the y intercept with the opposite segment
- if (testPt.fY < otherPt.fY) {
-
- } else if (testPt.fY > otherPt.fY) {
+ SkTDArray<Angle> angles;
+ SkASSERT(firstT - end != 0);
+ addTwoAngles(end, firstT, endLoc, &fTs[firstT], nextCo, angles);
+ buildAngles(firstT, lastT, 1, startLoc, angles);
+ SkTDArray<Angle*> sorted;
+ sortAngles(angles, sorted);
+ const Segment* leftSegment = sorted[0]->segment();
+ tIndex = sorted[0]->end();
+ direction = sorted[0]->start() - tIndex;
+ SkASSERT(direction);
+ direction = direction < 0 ? -1 : 1;
+ return leftSegment;
+ }
+ // FIXME: not crazy about this
+ // when the intersections are performed, the other index is into an
+ // incomplete array. as the array grows, the indices become incorrect
+ // while the following fixes the indices up again, it isn't smart about
+ // skipping segments whose indices are already correct
+ // assuming we leave the code that wrote the index in the first place
+ void fixOtherTIndex() {
+ int iCount = fTs.count();
+ for (int i = 0; i < iCount; ++i) {
+ Span& iSpan = fTs[i];
+ double oT = iSpan.fOtherT;
+ Segment* other = iSpan.fOther;
+ int oCount = other->fTs.count();
+ for (int o = 0; o < oCount; ++o) {
+ Span& oSpan = other->fTs[o];
+ if (oT == oSpan.fT && this == oSpan.fOther) {
+ iSpan.fOtherIndex = o;
+ }
}
- // FIXME: leftMost no good. Use y intercept instead
-#if 0
- SkScalar otherMost = other->leftMost(otherTIndex, otherTLeft);
- if (otherMost < left) {
- leftSegment = other;
- }
-#endif
}
- return leftSegment;
+ }
+
+ void init(const SkPoint pts[], SkPath::Verb verb) {
+ fPts = pts;
+ fVerb = verb;
+ fDone = false;
+ fCoincident = 0;
}
bool intersected() const {
@@ -916,59 +1058,49 @@ finish:
return fBounds.fLeft == fBounds.fRight;
}
- int lastSpan(int end, int step, const SkPoint* startLoc,
- const Span* startSpan, bool& coincident) {
+ int lastSpan(int end, int step, const SkPoint& startLoc,
+ const Span* startSpan, bool& coincident) const {
int last = end;
int count = fTs.count();
- coincident = false;
SkPoint lastLoc;
do {
- if (fTs[last].fCoincident == -step) {
+ end = last;
+ if (fTs[end].fCoincident == -step) {
coincident = true;
}
- if (step > 0 ? ++last < count : --last >= 0) {
- return -1;
+ if (step > 0 ? ++last >= count : --last < 0) {
+ return end;
}
const Span& lastSpan = fTs[last];
if (lastSpan.fDone == -step) {
- return -1;
+ return end;
}
if (lastSpan.fT == startSpan->fT) {
continue;
}
xyAtT(lastSpan.fT, &lastLoc);
- } while (*startLoc == lastLoc);
- return last;
+ } while (startLoc == lastLoc);
+ return end;
}
SkScalar leftMost(int start, int end) const {
return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
}
- int matchSpan(const Segment* match, double matchT)
- {
- int count = fTs.count();
- for (int index = 0; index < count; ++index) {
- Span& span = fTs[index];
- if (span.fOther != match) {
- continue;
- }
- if (span.fOtherT != matchT) {
- continue;
- }
- return index;
- }
- SkASSERT(0); // should never get here
- return -1;
- }
-
int nextSpan(int from, int step, const SkPoint& fromLoc,
- const Span* fromSpan, SkPoint* toLoc, Span** toSpan) {
+ const Span* fromSpan, SkPoint* toLoc, bool& coincident) const {
+ coincident = false;
+ if (fDone) {
+ return -1;
+ }
int count = fTs.count();
int to = from;
while (step > 0 ? ++to < count : --to >= 0) {
Span* span = &fTs[to];
- if (span->fT == fromSpan->fT) {
+ if (span->fCoincident == step) {
+ coincident = true;
+ }
+ if (fromSpan->fT == span->fT) {
continue;
}
SkPoint loc;
@@ -976,12 +1108,12 @@ finish:
if (fromLoc == loc) {
continue;
}
+ if (span->fDone == -step) {
+ return -1;
+ }
if (toLoc) {
*toLoc = loc;
}
- if (toSpan) {
- *toSpan = span;
- }
return to;
}
return -1;
@@ -992,32 +1124,36 @@ finish:
}
void reset() {
- fPts = NULL;
- fVerb = (SkPath::Verb) -1;
+ init(NULL, (SkPath::Verb) -1);
fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
fTs.reset();
- fDone = false;
- fCoincident = 0;
}
// OPTIMIZATION: remove this function if it's never called
double t(int tIndex) const {
return fTs[tIndex].fT;
}
+
+ void updatePts(const SkPoint pts[]) {
+ fPts = pts;
+ }
SkPath::Verb verb() const {
return fVerb;
}
SkScalar xAtT(double t) const {
+ SkASSERT(t >= 0 && t <= 1);
return (*SegmentXAtT[fVerb])(fPts, t);
}
void xyAtT(double t, SkPoint* pt) const {
+ SkASSERT(t >= 0 && t <= 1);
(*SegmentXYAtT[fVerb])(fPts, t, pt);
}
SkScalar yAtT(double t) const {
+ SkASSERT(t >= 0 && t <= 1);
return (*SegmentYAtT[fVerb])(fPts, t);
}
@@ -1073,13 +1209,15 @@ public:
fContainsCurves = true;
}
- void addLine(const SkPoint pts[2]) {
+ int addLine(const SkPoint pts[2]) {
fSegments.push_back().addLine(pts);
+ return fSegments.count();
}
- void addQuad(const SkPoint pts[3]) {
+ int addQuad(const SkPoint pts[3]) {
fSegments.push_back().addQuad(pts);
fContainsCurves = true;
+ return fSegments.count();
}
const Bounds& bounds() const {
@@ -1102,6 +1240,13 @@ public:
}
}
+ void fixOtherTIndex() {
+ int segmentCount = fSegments.count();
+ for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+ fSegments[sIndex].fixOtherTIndex();
+ }
+ }
+
void reset() {
fSegments.reset();
fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
@@ -1217,12 +1362,6 @@ void complete() {
}
}
-void startContour() {
- if (!fCurrentContour) {
- fCurrentContour = fContours.push_back_n(1);
- }
-}
-
void walk() {
// FIXME:remove once we can access path pts directly
SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
@@ -1242,12 +1381,17 @@ void walk() {
SkPath::Verb reducedVerb;
uint8_t* verbPtr = fPathVerbs.begin();
const SkPoint* pointsPtr = fPathPts.begin();
+ const SkPoint* finalCurveStart = NULL;
+ const SkPoint* finalCurveEnd = NULL;
while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
complete();
- startContour();
- pointsPtr += 1;
+ if (!fCurrentContour) {
+ fCurrentContour = fContours.push_back_n(1);
+ finalCurveEnd = pointsPtr++;
+ *fExtra.append() = -1; // start new contour
+ }
continue;
case SkPath::kLine_Verb:
// skip degenerate points
@@ -1257,12 +1401,14 @@ void walk() {
}
break;
case SkPath::kQuad_Verb:
+
reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
if (reducedVerb == 0) {
break; // skip degenerate points
}
if (reducedVerb == 1) {
- fCurrentContour->addLine(fReducePts.end() - 2);
+ *fExtra.append() =
+ fCurrentContour->addLine(fReducePts.end() - 2);
break;
}
fCurrentContour->addQuad(&pointsPtr[-1]);
@@ -1273,23 +1419,33 @@ void walk() {
break; // skip degenerate points
}
if (reducedVerb == 1) {
- fCurrentContour->addLine(fReducePts.end() - 2);
+ *fExtra.append() =
+ fCurrentContour->addLine(fReducePts.end() - 2);
break;
}
if (reducedVerb == 2) {
- fCurrentContour->addQuad(fReducePts.end() - 3);
+ *fExtra.append() =
+ fCurrentContour->addQuad(fReducePts.end() - 3);
break;
}
fCurrentContour->addCubic(&pointsPtr[-1]);
break;
case SkPath::kClose_Verb:
SkASSERT(fCurrentContour);
+ if (finalCurveStart && finalCurveEnd
+ && *finalCurveStart != *finalCurveEnd) {
+ *fReducePts.append() = *finalCurveStart;
+ *fReducePts.append() = *finalCurveEnd;
+ *fExtra.append() =
+ fCurrentContour->addLine(fReducePts.end() - 2);
+ }
complete();
continue;
default:
SkDEBUGFAIL("bad verb");
return;
}
+ finalCurveStart = &pointsPtr[verb - 1];
pointsPtr += verb;
SkASSERT(fCurrentContour);
}
@@ -1297,6 +1453,24 @@ void walk() {
if (fCurrentContour && !fCurrentContour->fSegments.count()) {
fContours.pop_back();
}
+ // correct pointers in contours since fReducePts may have moved as it grew
+ int cIndex = 0;
+ fCurrentContour = &fContours[0];
+ int extraCount = fExtra.count();
+ SkASSERT(fExtra[0] == -1);
+ int eIndex = 0;
+ int rIndex = 0;
+ while (++eIndex < extraCount) {
+ int offset = fExtra[eIndex];
+ if (offset < 0) {
+ fCurrentContour = &fContours[++cIndex];
+ continue;
+ }
+ Segment& segment = fCurrentContour->fSegments[offset - 1];
+ segment.updatePts(&fReducePts[rIndex]);
+ rIndex += segment.verb() + 1;
+ }
+ fExtra.reset(); // we're done with this
}
private:
@@ -1306,6 +1480,7 @@ private:
Contour* fCurrentContour;
SkTArray<Contour>& fContours;
SkTDArray<SkPoint> fReducePts; // segments created on the fly
+ SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
};
class Work {
@@ -1318,8 +1493,10 @@ public:
kCubic_Segment = SkPath::kCubic_Verb,
};
- void addOtherT(int index, double other) {
- fContour->fSegments[fIndex].addOtherT(index, other);
+ // FIXME: does it make sense to write otherIndex now if we're going to
+ // fix it up later?
+ void addOtherT(int index, double otherT, int otherIndex) {
+ fContour->fSegments[fIndex].addOtherT(index, otherT, otherIndex);
}
// Avoid collapsing t values that are close to the same since
@@ -1436,29 +1613,34 @@ static void debugShowLineIntersection(int pts, const Work& wt,
const Work& wn, const double wtTs[2], const double wnTs[2]) {
#if DEBUG_ADD_INTERSECTING_TS
if (!pts) {
+ SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
+ __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
+ wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
+ wn.pts()[1].fX, wn.pts()[1].fY);
return;
}
SkPoint wtOutPt, wnOutPt;
LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
- SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
+ SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
__FUNCTION__,
wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
if (pts == 2) {
- SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
+ SkDebugf(" wtTs[1]=%g", wtTs[1]);
}
- SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
- __FUNCTION__,
+ SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
if (pts == 2) {
- SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
+ SkDebugf(" wnTs[1]=%g", wnTs[1]);
+ SkDebugf("\n");
}
#endif
}
static bool addIntersectTs(Contour* test, Contour* next, int winding) {
+
if (test != next) {
if (test->bounds().fBottom < next->bounds().fTop) {
return false;
@@ -1467,10 +1649,11 @@ static bool addIntersectTs(Contour* test, Contour* next, int winding) {
return true;
}
}
- Work wt, wn;
+ Work wt;
wt.init(test);
- wn.init(next);
do {
+ Work wn;
+ wn.init(next);
if (test == next && !wn.startAfter(wt)) {
continue;
}
@@ -1490,6 +1673,8 @@ static bool addIntersectTs(Contour* test, Contour* next, int winding) {
case Work::kLine_Segment: {
pts = HLineIntersect(wn.pts(), wt.left(),
wt.right(), wt.y(), wt.xFlipped(), ts);
+ debugShowLineIntersection(pts, wt, wn,
+ ts.fT[1], ts.fT[0]);
break;
}
case Work::kQuad_Segment: {
@@ -1514,6 +1699,8 @@ static bool addIntersectTs(Contour* test, Contour* next, int winding) {
case Work::kLine_Segment: {
pts = VLineIntersect(wn.pts(), wt.top(),
wt.bottom(), wt.x(), wt.yFlipped(), ts);
+ debugShowLineIntersection(pts, wt, wn,
+ ts.fT[1], ts.fT[0]);
break;
}
case Work::kQuad_Segment: {
@@ -1535,10 +1722,14 @@ static bool addIntersectTs(Contour* test, Contour* next, int winding) {
case Work::kHorizontalLine_Segment:
pts = HLineIntersect(wt.pts(), wn.left(),
wn.right(), wn.y(), wn.xFlipped(), ts);
+ debugShowLineIntersection(pts, wt, wn,
+ ts.fT[1], ts.fT[0]);
break;
case Work::kVerticalLine_Segment:
pts = VLineIntersect(wt.pts(), wn.top(),
wn.bottom(), wn.x(), wn.yFlipped(), ts);
+ debugShowLineIntersection(pts, wt, wn,
+ ts.fT[1], ts.fT[0]);
break;
case Work::kLine_Segment: {
pts = LineIntersect(wt.pts(), wn.pts(), ts);
@@ -1625,8 +1816,8 @@ static bool addIntersectTs(Contour* test, Contour* next, int winding) {
SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
int testTAt = wt.addT(ts.fT[swap][pt], wn, coincident);
int nextTAt = wn.addT(ts.fT[!swap][pt], wt, coincident);
- wt.addOtherT(testTAt, ts.fT[!swap][pt]);
- wn.addOtherT(nextTAt, ts.fT[swap][pt]);
+ wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
+ wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
coincident = -coincident;
}
} while (wn.advance());
@@ -1643,6 +1834,39 @@ static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
}
}
+
+// OPTIMIZATION: not crazy about linear search here to find top active y.
+// seems like we should break down and do the sort, or maybe sort each
+// contours' segments?
+// Once the segment array is built, there's no reason I can think of not to
+// sort it in Y. hmmm
+static Segment* findTopContour(SkTDArray<Contour*>& contourList,
+ int contourCount) {
+ int cIndex = 0;
+ Segment* topStart;
+ do {
+ Contour* topContour = contourList[cIndex];
+ topStart = topContour->topSegment();
+ } while (!topStart && ++cIndex < contourCount);
+ if (!topStart) {
+ return NULL;
+ }
+ SkScalar top = topStart->bounds().fTop;
+ for (int cTest = cIndex + 1; cTest < contourCount; ++cTest) {
+ Contour* contour = contourList[cTest];
+ if (top < contour->bounds().fTop) {
+ continue;
+ }
+ Segment* test = contour->topSegment();
+ if (top > test->bounds().fTop) {
+ cIndex = cTest;
+ topStart = test;
+ top = test->bounds().fTop;
+ }
+ }
+ return topStart;
+}
+
// Each segment may have an inside or an outside. Segments contained within
// winding may have insides on either side, and form a contour that should be
// ignored. Segments that are coincident with opposing direction segments may
@@ -1650,41 +1874,26 @@ static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
// 'Normal' segments will have one inside and one outside. Subsequent connections
// when winding should follow the intersection direction. If more than one edge
// is an option, choose first edge that continues the inside.
-
+ // since we start with leftmost top edge, we'll traverse through a
+ // smaller angle counterclockwise to get to the next edge.
static void bridge(SkTDArray<Contour*>& contourList) {
int contourCount = contourList.count();
+ int winding = 0; // there are no contours outside this one
do {
- // OPTIMIZATION: not crazy about linear search here to find top active y.
- // seems like we should break down and do the sort, or maybe sort each
- // contours' segments?
- // Once the segment array is built, there's no reason I can think of not to
- // sort it in Y. hmmm
- int cIndex = 0;
- Segment* topStart;
- do {
- Contour* topContour = contourList[cIndex];
- topStart = topContour->topSegment();
- } while (!topStart && ++cIndex < contourCount);
+ Segment* topStart = findTopContour(contourList, contourCount);
if (!topStart) {
break;
}
- SkScalar top = topStart->bounds().fTop;
- for (int cTest = cIndex + 1; cTest < contourCount; ++cTest) {
- Contour* contour = contourList[cTest];
- if (top < contour->bounds().fTop) {
- continue;
- }
- Segment* test = contour->topSegment();
- if (top > test->bounds().fTop) {
- cIndex = cTest;
- topStart = test;
- top = test->bounds().fTop;
- }
- }
- int index;
- const Segment* topSegment = topStart->findTop(index);
// Start at the top. Above the top is outside, below is inside.
- // follow edges to intersection
+ // follow edges to intersection by changing the tIndex by direction.
+ int tIndex, step;
+ const Segment* topSegment = topStart->findTop(tIndex, step);
+ const Segment* next = topSegment;
+ do {
+ int spanIndex;
+ next = next->findNext(tIndex, winding, step, spanIndex);
+ } while (next != topSegment);
+
// at intersection, stay on outside, but mark remaining edges as inside
// or, only mark first pair as inside?
// how is this going to work for contained (but not intersecting)
@@ -1700,6 +1909,14 @@ static void bridge(SkTDArray<Contour*>& contourList) {
}
+static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
+ int contourCount = contourList.count();
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ Contour* contour = contourList[cTest];
+ contour->fixOtherTIndex();
+ }
+}
+
static void makeContourList(SkTArray<Contour>& contours, Contour& sentinel,
SkTDArray<Contour*>& list) {
int count = contours.count();
@@ -1740,6 +1957,7 @@ void simplifyx(const SkPath& path, bool asFill, SkPath& simple) {
next = *nextPtr++;
} while (next != &sentinel && addIntersectTs(current, next, winding));
} while (*currentPtr != &sentinel);
+ fixOtherTIndex(contourList);
// eat through coincident edges
coincidenceCheck(contourList, winding);
// construct closed contours
diff --git a/experimental/Intersection/Simplify.h b/experimental/Intersection/Simplify.h
new file mode 100644
index 0000000000..ae338eedeb
--- /dev/null
+++ b/experimental/Intersection/Simplify.h
@@ -0,0 +1,18 @@
+/*
+ * 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 "CurveIntersection.h"
+#include "Intersections.h"
+#include "LineIntersection.h"
+#include "LineParameters.h"
+#include "SkPath.h"
+#include "SkRect.h"
+#include "SkTArray.h"
+#include "SkTDArray.h"
+#include "ShapeOps.h"
+#include "TSearch.h"
+#include <algorithm> // used for std::min
+
diff --git a/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp b/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp
index f920ebac53..d3a9f2e0d3 100644
--- a/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp
+++ b/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp
@@ -1,13 +1,10 @@
-#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 <algorithm> // used for std::min
+/*
+ * 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 "Simplify.h"
namespace SimplifyAddIntersectingTsTest {
diff --git a/experimental/Intersection/SimplifyAngle_Test.cpp b/experimental/Intersection/SimplifyAngle_Test.cpp
new file mode 100644
index 0000000000..89123bd80f
--- /dev/null
+++ b/experimental/Intersection/SimplifyAngle_Test.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 "Simplify.h"
+
+namespace SimplifyAngleTest {
+
+#include "Simplify.cpp"
+
+} // end of SimplifyAngleTest namespace
+
+#include "Intersection_Tests.h"
+
+static const SkPoint lines[][2] = {
+ { { 10, 10}, { 10, 20} },
+ { { 10, 10}, { 20, 10} },
+ { { 10, 10}, {-20, 10} },
+ { { 10, 10}, { 10, -20} },
+ { { 10, 10}, { 20, 20} },
+ { { 10, 10}, {-20, -20} },
+ { { 10, 10}, {-20, 40} },
+ { { 10, 10}, { 40, -20} }
+};
+
+static const size_t lineCount = sizeof(lines) / sizeof(lines[0]);
+
+static const SkPoint quads[][3] = {
+ {{ 1, 1}, { 2, 2}, { 1, 3}}, // 0
+ {{ 1, 1}, { 3, 3}, { 1, 5}}, // 1
+ {{ 1, 1}, { 4, 4}, { 1, 7}}, // 2
+ {{ 1, 1}, { 5, 5}, { 9, 9}}, // 3
+ {{ 1, 1}, { 4, 4}, { 7, 1}}, // 4
+ {{ 1, 1}, { 3, 3}, { 5, 1}}, // 5
+ {{ 1, 1}, { 2, 2}, { 3, 1}}, // 6
+};
+
+static const size_t quadCount = sizeof(quads) / sizeof(quads[0]);
+
+static const SkPoint cubics[][4] = {
+ {{ 1, 1}, { 2, 2}, { 2, 3}, { 1, 4}},
+ {{ 1, 1}, { 3, 3}, { 3, 5}, { 1, 7}},
+ {{ 1, 1}, { 4, 4}, { 4, 7}, { 1, 10}},
+ {{ 1, 1}, { 5, 5}, { 8, 8}, { 9, 9}},
+ {{ 1, 1}, { 4, 4}, { 7, 4}, { 10, 1}},
+ {{ 1, 1}, { 3, 3}, { 5, 3}, { 7, 1}},
+ {{ 1, 1}, { 2, 2}, { 3, 2}, { 4, 1}},
+};
+
+static const size_t cubicCount = sizeof(cubics) / sizeof(cubics[0]);
+
+static void testLines(bool testFlat) {
+ // create angles in a circle
+ SkTDArray<SimplifyAngleTest::Angle> angles;
+ SkTDArray<SimplifyAngleTest::Angle* > angleList;
+ SkTDArray<double> arcTans;
+ size_t x;
+ for (x = 0; x < lineCount; ++x) {
+ SimplifyAngleTest::Angle* angle = angles.append();
+ if (testFlat) {
+ angle->setFlat(lines[x], SkPath::kLine_Verb, 0, x, x + 1, false);
+ } else {
+ angle->set(lines[x], SkPath::kLine_Verb, 0, x, x + 1, false);
+ }
+ double arcTan = atan2(lines[x][0].fX - lines[x][1].fX,
+ lines[x][0].fY - lines[x][1].fY);
+ arcTans.push(arcTan);
+ }
+ for (x = 0; x < lineCount; ++x) {
+ angleList.push(&angles[x]);
+ }
+ QSort<SimplifyAngleTest::Angle>(angleList.begin(), angleList.end() - 1);
+ bool first = true;
+ bool wrap = false;
+ double base, last;
+ for (size_t x = 0; x < lineCount; ++x) {
+ const SimplifyAngleTest::Angle* angle = angleList[x];
+ int span = angle->start();
+// SkDebugf("%s [%d] %1.9g (%1.9g,%1.9g %1.9g,%1.9g)\n", __FUNCTION__,
+// span, arcTans[span], lines[span][0].fX, lines[span][0].fY,
+// lines[span][1].fX, lines[span][1].fY);
+ if (first) {
+ base = last = arcTans[span];
+ first = false;
+ continue;
+ }
+ if (last < arcTans[span]) {
+ last = arcTans[span];
+ continue;
+ }
+ if (!wrap) {
+ if (base < arcTans[span]) {
+ SkDebugf("%s !wrap [%d] %g\n", __FUNCTION__, span, arcTans[span]);
+ SkASSERT(0);
+ }
+ last = arcTans[span];
+ wrap = true;
+ continue;
+ }
+ SkDebugf("%s wrap [%d] %g\n", __FUNCTION__, span, arcTans[span]);
+ SkASSERT(0);
+ }
+}
+
+static void testQuads(bool testFlat) {
+ SkTDArray<SimplifyAngleTest::Angle> angles;
+ SkTDArray<SimplifyAngleTest::Angle* > angleList;
+ size_t x;
+ for (x = 0; x < quadCount; ++x) {
+ SimplifyAngleTest::Angle* angle = angles.append();
+ if (testFlat) {
+ angle->setFlat(quads[x], SkPath::kQuad_Verb, 0, x, x + 1, false);
+ } else {
+ angle->set(quads[x], SkPath::kQuad_Verb, 0, x, x + 1, false);
+ }
+ }
+ for (x = 0; x < quadCount; ++x) {
+ angleList.push(&angles[x]);
+ }
+ QSort<SimplifyAngleTest::Angle>(angleList.begin(), angleList.end() - 1);
+ for (size_t x = 0; x < quadCount; ++x) {
+ *angleList[x] < *angleList[x + 1];
+ SkASSERT(x == quadCount - 1 || *angleList[x] < *angleList[x + 1]);
+ const SimplifyAngleTest::Angle* angle = angleList[x];
+ if (x != angle->start()) {
+ SkDebugf("%s [%d] [%d]\n", __FUNCTION__, x, angle->start());
+ SkASSERT(0);
+ }
+ }
+}
+
+static void testCubics(bool testFlat) {
+ SkTDArray<SimplifyAngleTest::Angle> angles;
+ SkTDArray<SimplifyAngleTest::Angle* > angleList;
+ for (size_t x = 0; x < cubicCount; ++x) {
+ SimplifyAngleTest::Angle* angle = angles.append();
+ if (testFlat) {
+ angle->setFlat(cubics[x], SkPath::kCubic_Verb, 0, x, x + 1, false);
+ } else {
+ angle->set(cubics[x], SkPath::kCubic_Verb, 0, x, x + 1, false);
+ }
+ angleList.push(angle);
+ }
+ QSort<SimplifyAngleTest::Angle>(angleList.begin(), angleList.end() - 1);
+ for (size_t x = 0; x < cubicCount; ++x) {
+ const SimplifyAngleTest::Angle* angle = angleList[x];
+ if (x != angle->start()) {
+ SkDebugf("%s [%d] [%d]\n", __FUNCTION__, x, angle->start());
+ SkASSERT(0);
+ }
+ }
+}
+
+static void (*tests[])(bool) = {
+ testLines,
+ testQuads,
+ testCubics
+};
+
+static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+
+static void (*firstTest)(bool) = 0;
+static bool skipAll = false;
+
+void SimplifyAngle_Test() {
+ if (skipAll) {
+ return;
+ }
+ size_t index = 0;
+ if (firstTest) {
+ while (index < testCount && tests[index] != firstTest) {
+ ++index;
+ }
+ }
+ bool firstTestComplete = false;
+ for ( ; index < testCount; ++index) {
+ (*tests[index])(true);
+ firstTestComplete = true;
+ }
+} \ No newline at end of file
diff --git a/experimental/Intersection/SimplifyFindNext_Test.cpp b/experimental/Intersection/SimplifyFindNext_Test.cpp
new file mode 100644
index 0000000000..6d47a93fb7
--- /dev/null
+++ b/experimental/Intersection/SimplifyFindNext_Test.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 "Simplify.h"
+
+namespace SimplifyFindNextTest {
+
+#include "Simplify.cpp"
+
+} // end of SimplifyFindNextTest namespace
+
+#include "Intersection_Tests.h"
+
+static const SimplifyFindNextTest::Segment* testCommon(
+ int start, int winding, int step,
+ SkTArray<SimplifyFindNextTest::Contour>& contours,
+ SimplifyFindNextTest::EdgeBuilder& builder, const SkPath& path) {
+ SkTDArray<SimplifyFindNextTest::Contour*> contourList;
+ SimplifyFindNextTest::Contour sentinel;
+ sentinel.reset();
+ makeContourList(contours, sentinel, contourList);
+ addIntersectTs(contourList[0], contourList[0], -1);
+ if (contours.count() > 1) {
+ SkASSERT(contours.count() == 2);
+ addIntersectTs(contourList[0], contourList[1], -1);
+ addIntersectTs(contourList[1], contourList[1], -1);
+ }
+ fixOtherTIndex(contourList);
+ SimplifyFindNextTest::Segment& segment = contours[0].fSegments[0];
+ int spanIndex;
+ SimplifyFindNextTest::Segment* next = segment.findNext(start, winding,
+ step, spanIndex);
+ SkASSERT(spanIndex == 1);
+ return next;
+}
+
+static void test(const SkPath& path) {
+ SkTArray<SimplifyFindNextTest::Contour> contours;
+ SimplifyFindNextTest::EdgeBuilder builder(path, contours);
+ int start = 0;
+ int winding = 0;
+ int step = 1;
+ testCommon(start, winding, step, contours, builder, path);
+}
+
+static void testLine1() {
+ SkPath path;
+ path.moveTo(2,0);
+ path.lineTo(1,1);
+ path.lineTo(0,0);
+ path.close();
+ test(path);
+}
+
+static void (*tests[])() = {
+ testLine1,
+};
+
+static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+
+static void (*firstTest)() = 0;
+static bool skipAll = false;
+
+void SimplifyFindNext_Test() {
+ if (skipAll) {
+ return;
+ }
+ size_t index = 0;
+ if (firstTest) {
+ while (index < testCount && tests[index] != firstTest) {
+ ++index;
+ }
+ }
+ bool firstTestComplete = false;
+ for ( ; index < testCount; ++index) {
+ (*tests[index])();
+ firstTestComplete = true;
+ }
+}
diff --git a/experimental/Intersection/SimplifyFindTop_Test.cpp b/experimental/Intersection/SimplifyFindTop_Test.cpp
new file mode 100644
index 0000000000..ade9299ab3
--- /dev/null
+++ b/experimental/Intersection/SimplifyFindTop_Test.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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 "Simplify.h"
+
+namespace SimplifyFindTopTest {
+
+#include "Simplify.cpp"
+
+} // end of SimplifyFindTopTest namespace
+
+#include "Intersection_Tests.h"
+
+static const SimplifyFindTopTest::Segment* testCommon(
+ SkTArray<SimplifyFindTopTest::Contour>& contours,
+ SimplifyFindTopTest::EdgeBuilder& builder, const SkPath& path) {
+ SkTDArray<SimplifyFindTopTest::Contour*> contourList;
+ SimplifyFindTopTest::Contour sentinel;
+ sentinel.reset();
+ makeContourList(contours, sentinel, contourList);
+ addIntersectTs(contourList[0], contourList[0], -1);
+ if (contours.count() > 1) {
+ SkASSERT(contours.count() == 2);
+ addIntersectTs(contourList[0], contourList[1], -1);
+ addIntersectTs(contourList[1], contourList[1], -1);
+ }
+ fixOtherTIndex(contourList);
+ SimplifyFindTopTest::Segment* topStart = findTopContour(contourList,
+ contourList.count());
+ int index, direction;
+ const SimplifyFindTopTest::Segment* topSegment = topStart->findTop(index,
+ direction);
+ SkASSERT(direction == 1);
+ return topSegment;
+}
+
+static void test(const SkPath& path) {
+ SkTArray<SimplifyFindTopTest::Contour> contours;
+ SimplifyFindTopTest::EdgeBuilder builder(path, contours);
+ testCommon(contours, builder, path);
+}
+
+static void test(const SkPath& path, SkScalar x1, SkScalar y1,
+ SkScalar x2, SkScalar y2) {
+ SkTArray<SimplifyFindTopTest::Contour> contours;
+ SimplifyFindTopTest::EdgeBuilder builder(path, contours);
+ const SimplifyFindTopTest::Segment* topSegment =
+ testCommon(contours, builder, path);
+ const SkPoint* pts = topSegment->pts();
+ SkPoint top = pts[0];
+ SkPoint bottom = pts[1];
+ if (top.fY > bottom.fY) {
+ SkTSwap<SkPoint>(top, bottom);
+ }
+ SkASSERT(top.fX == x1);
+ SkASSERT(top.fY == y1);
+ SkASSERT(bottom.fX == x2);
+ SkASSERT(bottom.fY == y2);
+}
+
+static void testLine1() {
+ SkPath path;
+ path.moveTo(2,0);
+ path.lineTo(1,1);
+ path.lineTo(0,0);
+ path.close();
+ test(path);
+}
+
+static void addInnerCWTriangle(SkPath& path) {
+ path.moveTo(3,0);
+ path.lineTo(4,1);
+ path.lineTo(2,1);
+ path.close();
+}
+
+static void addInnerCCWTriangle(SkPath& path) {
+ path.moveTo(3,0);
+ path.lineTo(2,1);
+ path.lineTo(4,1);
+ path.close();
+}
+
+static void addOuterCWTriangle(SkPath& path) {
+ path.moveTo(3,0);
+ path.lineTo(6,2);
+ path.lineTo(0,2);
+ path.close();
+}
+
+static void addOuterCCWTriangle(SkPath& path) {
+ path.moveTo(3,0);
+ path.lineTo(0,2);
+ path.lineTo(6,2);
+ path.close();
+}
+
+static void testLine2() {
+ SkPath path;
+ addInnerCWTriangle(path);
+ addOuterCWTriangle(path);
+ test(path, 3, 0, 0, 2);
+}
+
+static void testLine3() {
+ SkPath path;
+ addOuterCWTriangle(path);
+ addInnerCWTriangle(path);
+ test(path, 3, 0, 0, 2);
+}
+
+static void testLine4() {
+ SkPath path;
+ addInnerCCWTriangle(path);
+ addOuterCWTriangle(path);
+ test(path, 3, 0, 0, 2);
+}
+
+static void testLine5() {
+ SkPath path;
+ addOuterCWTriangle(path);
+ addInnerCCWTriangle(path);
+ test(path, 3, 0, 0, 2);
+}
+
+static void testLine6() {
+ SkPath path;
+ addInnerCWTriangle(path);
+ addOuterCCWTriangle(path);
+ test(path, 3, 0, 0, 2);
+}
+
+static void testLine7() {
+ SkPath path;
+ addOuterCCWTriangle(path);
+ addInnerCWTriangle(path);
+ test(path, 3, 0, 0, 2);
+}
+
+static void testLine8() {
+ SkPath path;
+ addInnerCCWTriangle(path);
+ addOuterCCWTriangle(path);
+ test(path, 3, 0, 0, 2);
+}
+
+static void testLine9() {
+ SkPath path;
+ addOuterCCWTriangle(path);
+ addInnerCCWTriangle(path);
+ test(path, 3, 0, 0, 2);
+}
+
+static void testQuads() {
+ SkPath path;
+ path.moveTo(2,0);
+ path.quadTo(1,1, 0,0);
+ path.close();
+ test(path);
+}
+
+static void testCubics() {
+ SkPath path;
+ path.moveTo(2,0);
+ path.cubicTo(2,3, 1,1, 0,0);
+ path.close();
+ test(path);
+}
+
+static void (*tests[])() = {
+ testLine1,
+ testLine2,
+ testLine3,
+ testLine4,
+ testLine5,
+ testLine6,
+ testLine7,
+ testLine8,
+ testLine9,
+ testQuads,
+ testCubics
+};
+
+static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+
+static void (*firstTest)() = 0;
+static bool skipAll = false;
+
+void SimplifyFindTop_Test() {
+ if (skipAll) {
+ return;
+ }
+ size_t index = 0;
+ if (firstTest) {
+ while (index < testCount && tests[index] != firstTest) {
+ ++index;
+ }
+ }
+ bool firstTestComplete = false;
+ for ( ; index < testCount; ++index) {
+ (*tests[index])();
+ firstTestComplete = true;
+ }
+}
diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm
index 4df4aeb1c1..5173eaba27 100644
--- a/experimental/Intersection/op.htm
+++ b/experimental/Intersection/op.htm
@@ -234,24 +234,37 @@ path.close();
<div id="testSimplifyQuadratic17">
SkPath path, out;
- path.moveTo(0, 0);
- path.quadTo(0, 0, 0, 0);
- path.lineTo(2, 2);
+ path.moveTo(8, 8);
+ path.quadTo(10, 10, 8, -10);
path.close();
- path.moveTo(0, 1);
- path.lineTo(0, 1);
- path.quadTo(2, 1, 3, 3);
+ path.moveTo(8, 8);
+ path.quadTo(12, 12, 14, 4);
+ path.close();
+ path.moveTo(8, 8);
+ path.quadTo(9, 9, 10, 8);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
</div>
+<div id="testSimplifyQuadratic18">
+ SkPath path, out;
+ path.moveTo(8.0000000000000071, 8.0000000000000071);
+ path.quadTo(8.7289570079366854, 8.7289570079366889, 9.3914917259458743, 9.0593802763083691);
+ path.close();
+ path.moveTo(8.0000000000000142, 8.0000000000000142);
+ path.quadTo(8.1250000000000107, 8.1250000000000071, 8.2500000000000071, 8.2187500000000053);
+ path.close();
+ testSimplify(path, true, out, bitmap);
+ drawAsciiPaths(path, out, true);
+</div>
</div>
<script type="text/javascript">
var testDivs = [
+ testSimplifyQuadratic18,
testSimplifyQuadratic17,
testSimplifyQuadratic16,
testSimplifyQuadratic15,
@@ -332,10 +345,12 @@ function init(test) {
for (var verbs in contour) {
var verb = contour[verbs];
var last = verb.length;
- xmin = Math.min(xmin, verb[0]);
- ymin = Math.min(ymin, verb[1]);
- xmax = Math.max(xmax, verb[last - 2]);
- ymax = Math.max(ymax, verb[last - 1]);
+ for (var idx = 0; idx < last; idx += 2) {
+ xmin = Math.min(xmin, verb[idx]);
+ xmax = Math.max(xmax, verb[idx]);
+ ymin = Math.min(ymin, verb[idx + 1]);
+ ymax = Math.max(ymax, verb[idx + 1]);
+ }
}
}
var subscale = 1;
diff --git a/experimental/Intersection/thingsToDo.txt b/experimental/Intersection/thingsToDo.txt
index c6e4563432..7dd60d9a40 100644
--- a/experimental/Intersection/thingsToDo.txt
+++ b/experimental/Intersection/thingsToDo.txt
@@ -139,3 +139,28 @@ continue tracing the touched segments with reversed outside/inside sense
once the edges are exhausted, remaining must be disjoint contours
send a ray from a disjoint point through all other contours
count the crossings, determine if disjoint is inside or outside, then continue
+
+===
+
+On Quadratic (and Cubic) Intersections
+
+Currently, if only the end points touch, QuadracticIntersections does a lot of
+work to figure that out. Can I test for that up front, then short circuit the
+recursive search for the end points?
+
+Or, is there something defective in the current approach that makes the end
+point recursion go so deep? I'm seeing 56 stack frames (about 28 divides, but
+thankfully, no splits) to find one matching endpoint.
+
+
+Bezier curve focus may allow more quickly determining that end points with
+identical tangents are practically coicident for some range of T, but I don't
+understand the math yet to know.
+
+Another approach is to determine how flat the curve is to make good guesses
+about how far to move away in T before doing the intersection for the remainder
+and/or to determine whether one curve is to the inside or outside of another.
+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. \ No newline at end of file