• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "include/core/SkPoint.h"
8 #include "include/core/SkScalar.h"
9 #include "include/core/SkTypes.h"
10 #include "src/core/SkGeometry.h"
11 #include "src/pathops/SkIntersections.h"
12 #include "src/pathops/SkPathOpsConic.h"
13 #include "src/pathops/SkPathOpsPoint.h"
14 #include "src/pathops/SkPathOpsQuad.h"
15 #include "src/pathops/SkPathOpsTypes.h"
16 #include "tests/PathOpsTestCommon.h"
17 #include "tests/Test.h"
18 
19 #include <array>
20 
21 /*
22 manually compute the intersection of a pair of circles and see if the conic intersection matches
23   given two circles
24     construct a line connecting their centers
25 
26  */
27 
28 static const ConicPts testSet[] = {
29     {{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
30     {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
31 
32     {{{{5.1114602088928223, 628.77813720703125},
33         {10.834027290344238, 988.964111328125},
34         {163.40835571289062, 988.964111328125}}}, 0.72944212f},
35     {{{{163.40835571289062, 988.964111328125},
36         {5, 988.964111328125},
37         {5, 614.7423095703125}}}, 0.707106769f},
38 
39     {{{{11.17222976684570312, -8.103978157043457031},
40         {22.91432571411132812, -10.37866020202636719},
41         {23.7764129638671875, -7.725424289703369141}}}, 1.00862849f},
42     {{{{-1.545085430145263672, -4.755282402038574219},
43         {22.23132705688476562, -12.48070907592773438},
44         {23.7764129638671875, -7.725427150726318359}}}, 0.707106769f},
45 
46     {{{{-4,1}, {-4,5}, {0,5}}}, 0.707106769f},
47     {{{{-3,4}, {-3,1}, {0,1}}}, 0.707106769f},
48 
49     {{{{0, 0}, {0, 1}, {1, 1}}}, 0.5f},
50     {{{{1, 0}, {0, 0}, {0, 1}}}, 0.5f},
51 
52 };
53 
54 const int testSetCount = (int) std::size(testSet);
55 
chopCompare(const SkConic chopped[2],const SkDConic dChopped[2])56 static void chopCompare(const SkConic chopped[2], const SkDConic dChopped[2]) {
57     SkASSERT(roughly_equal(chopped[0].fW, dChopped[0].fWeight));
58     SkASSERT(roughly_equal(chopped[1].fW, dChopped[1].fWeight));
59     for (int cIndex = 0; cIndex < 2; ++cIndex) {
60         for (int pIndex = 0; pIndex < 3; ++pIndex) {
61             SkDPoint up;
62             up.set(chopped[cIndex].fPts[pIndex]);
63             SkASSERT(dChopped[cIndex].fPts[pIndex].approximatelyEqual(up));
64         }
65     }
66 #if DEBUG_VISUALIZE_CONICS
67     dChopped[0].dump();
68     dChopped[1].dump();
69 #endif
70 }
71 
72 #define DEBUG_VISUALIZE_CONICS 0
73 
74 #if DEBUG_VISUALIZE_CONICS
75 #include "include/core/SkBitmap.h"
76 #include "include/core/SkCanvas.h"
77 #include "include/core/SkImageEncoder.h"
78 #include "include/core/SkPaint.h"
79 #include "include/core/SkString.h"
80 #include "src/pathops/SkPathOpsRect.h"
81 
writePng(const SkConic & c,const SkConic ch[2],const char * name)82 static void writePng(const SkConic& c, const SkConic ch[2], const char* name) {
83     const int scale = 10;
84     SkConic conic, chopped[2];
85     for (int index = 0; index < 3; ++index) {
86         conic.fPts[index].fX = c.fPts[index].fX * scale;
87         conic.fPts[index].fY = c.fPts[index].fY * scale;
88         for (int chIndex = 0; chIndex < 2; ++chIndex) {
89             chopped[chIndex].fPts[index].fX = ch[chIndex].fPts[index].fX * scale;
90             chopped[chIndex].fPts[index].fY = ch[chIndex].fPts[index].fY * scale;
91         }
92     }
93     conic.fW = c.fW;
94     chopped[0].fW = ch[0].fW;
95     chopped[1].fW = ch[1].fW;
96     SkBitmap bitmap;
97     SkRect bounds;
98     conic.computeTightBounds(&bounds);
99     bounds.outset(10, 10);
100     bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul(
101           SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height())));
102     SkCanvas canvas(bitmap);
103     SkPaint paint;
104     paint.setAntiAlias(true);
105     paint.setStyle(SkPaint::kStroke_Style);
106     canvas.translate(-bounds.fLeft, -bounds.fTop);
107     canvas.drawColor(SK_ColorWHITE);
108     SkPath path;
109     path.moveTo(conic.fPts[0]);
110     path.conicTo(conic.fPts[1], conic.fPts[2], conic.fW);
111     paint.setARGB(0x80, 0xFF, 0, 0);
112     canvas.drawPath(path, paint);
113     path.reset();
114     path.moveTo(chopped[0].fPts[0]);
115     path.conicTo(chopped[0].fPts[1], chopped[0].fPts[2], chopped[0].fW);
116     path.moveTo(chopped[1].fPts[0]);
117     path.conicTo(chopped[1].fPts[1], chopped[1].fPts[2], chopped[1].fW);
118     paint.setARGB(0x80, 0, 0, 0xFF);
119     canvas.drawPath(path, paint);
120     SkString filename("c:\\Users\\caryclark\\Documents\\");
121     filename.appendf("%s.png", name);
122     ToolUtils::EncodeImageToFile(filename.c_str(), bitmap, SkEncodedImageFormat::kPNG, 100);
123 }
124 
writeDPng(const SkDConic & dC,const char * name)125 static void writeDPng(const SkDConic& dC, const char* name) {
126     const int scale = 5;
127     SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale },
128         {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale },
129         {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight };
130     SkBitmap bitmap;
131     SkDRect bounds;
132     bounds.setBounds(dConic);
133     bounds.fLeft -= 10;
134     bounds.fTop -= 10;
135     bounds.fRight += 10;
136     bounds.fBottom += 10;
137     bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul(
138           SkScalarRoundToInt(SkDoubleToScalar(bounds.width())),
139           SkScalarRoundToInt(SkDoubleToScalar(bounds.height()))));
140     SkCanvas canvas(bitmap);
141     SkPaint paint;
142     paint.setAntiAlias(true);
143     paint.setStyle(SkPaint::kStroke_Style);
144     canvas.translate(SkDoubleToScalar(-bounds.fLeft), SkDoubleToScalar(-bounds.fTop));
145     canvas.drawColor(SK_ColorWHITE);
146     SkPath path;
147     path.moveTo(dConic.fPts[0].asSkPoint());
148     path.conicTo(dConic.fPts[1].asSkPoint(), dConic.fPts[2].asSkPoint(), dConic.fWeight);
149     paint.setARGB(0x80, 0xFF, 0, 0);
150     canvas.drawPath(path, paint);
151     path.reset();
152     const int chops = 2;
153     for (int tIndex = 0; tIndex < chops; ++tIndex) {
154         SkDConic chopped = dConic.subDivide(tIndex / (double) chops,
155                 (tIndex + 1) / (double) chops);
156         path.moveTo(chopped.fPts[0].asSkPoint());
157         path.conicTo(chopped.fPts[1].asSkPoint(), chopped.fPts[2].asSkPoint(), chopped.fWeight);
158     }
159     paint.setARGB(0x80, 0, 0, 0xFF);
160     canvas.drawPath(path, paint);
161     SkString filename("c:\\Users\\caryclark\\Documents\\");
162     filename.appendf("%s.png", name);
163     ToolUtils::EncodeImageToFile(filename.c_str(), bitmap, SkEncodedImageFormat::kPNG, 100);
164 }
165 #endif
166 
chopBothWays(const SkDConic & dConic,double t,const char * name)167 static void chopBothWays(const SkDConic& dConic, double t, const char* name) {
168     SkConic conic;
169     for (int index = 0; index < 3; ++index) {
170         conic.fPts[index] = dConic.fPts[index].asSkPoint();
171     }
172     conic.fW = dConic.fWeight;
173     SkConic chopped[2];
174     SkDConic dChopped[2];
175     if (!conic.chopAt(SkDoubleToScalar(t), chopped)) {
176         return;
177     }
178     dChopped[0] = dConic.subDivide(0, t);
179     dChopped[1] = dConic.subDivide(t, 1);
180 #if DEBUG_VISUALIZE_CONICS
181     dConic.dump();
182 #endif
183     chopCompare(chopped, dChopped);
184 #if DEBUG_VISUALIZE_CONICS
185     writePng(conic, chopped, name);
186 #endif
187 }
188 
189 #if DEBUG_VISUALIZE_CONICS
190 const SkDConic frame0[] = {
191 {{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
192 {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
193 };
194 
195 const SkDConic frame1[] = {
196 {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
197 {{{{306.58801299999999, -227.983994}, {212.46499600000001, -262.24200400000001}, {95.551200899999998, 58.976398500000002}}}, 0.707107008f},
198 {{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f},
199 {{{{134.08399674208422, -155.06258330544892}, {30.390000629402859, -143.55685905168704}, {23.185499199999999, -102.697998}}}, 0.923879623f},
200 };
201 
202 const SkDConic frame2[] = {
203 {{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
204 {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
205 {{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f},
206 {{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f},
207 };
208 
209 const SkDConic frame3[] = {
210 {{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
211 {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
212 {{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f},
213 {{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f},
214 };
215 
216 const SkDConic frame4[] = {
217 {{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
218 {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
219 {{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f},
220 {{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f},
221 };
222 
223 const SkDConic frame5[] = {
224 {{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
225 {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
226 {{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f},
227 {{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f},
228 };
229 
230 const SkDConic frame6[] = {
231 {{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
232 {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
233 {{{{205.78973252799028, -158.12538713371103}, {190.33692178059735, -137.11320166154385}, {174.87004877564593, -111.2132534799228}}}, 0.858117759f},
234 {{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f},
235 };
236 
237 const SkDConic* frames[] = {
238     frame0, frame1, frame2, frame3, frame4, frame5, frame6
239 };
240 
241 const int frameSizes[] = { (int) std::size(frame0), (int) std::size(frame1),
242         (int) std::size(frame2), (int) std::size(frame3),
243         (int) std::size(frame4), (int) std::size(frame5),
244         (int) std::size(frame6),
245 };
246 
writeFrames()247 static void writeFrames() {
248     const int scale = 5;
249 
250     for (int index = 0; index < (int) std::size(frameSizes); ++index) {
251         SkDRect bounds;
252         bool boundsSet = false;
253         int frameSize = frameSizes[index];
254         for (int fIndex = 0; fIndex < frameSize; ++fIndex) {
255             const SkDConic& dC = frames[index][fIndex];
256             SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale },
257                 {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale },
258                 {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight };
259             SkDRect dBounds;
260             dBounds.setBounds(dConic);
261             if (!boundsSet) {
262                 bounds = dBounds;
263                 boundsSet = true;
264             } else {
265                 bounds.add((SkDPoint&) dBounds.fLeft);
266                 bounds.add((SkDPoint&) dBounds.fRight);
267             }
268         }
269         bounds.fLeft -= 10;
270         bounds.fTop -= 10;
271         bounds.fRight += 10;
272         bounds.fBottom += 10;
273         SkBitmap bitmap;
274         bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul(
275               SkScalarRoundToInt(SkDoubleToScalar(bounds.width())),
276               SkScalarRoundToInt(SkDoubleToScalar(bounds.height()))));
277         SkCanvas canvas(bitmap);
278         SkPaint paint;
279         paint.setAntiAlias(true);
280         paint.setStyle(SkPaint::kStroke_Style);
281         canvas.translate(SkDoubleToScalar(-bounds.fLeft), SkDoubleToScalar(-bounds.fTop));
282         canvas.drawColor(SK_ColorWHITE);
283         for (int fIndex = 0; fIndex < frameSize; ++fIndex) {
284             const SkDConic& dC = frames[index][fIndex];
285             SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale },
286                 {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale },
287                 {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight };
288             SkPath path;
289             path.moveTo(dConic.fPts[0].asSkPoint());
290             path.conicTo(dConic.fPts[1].asSkPoint(), dConic.fPts[2].asSkPoint(), dConic.fWeight);
291             if (fIndex < 2) {
292                 paint.setARGB(0x80, 0xFF, 0, 0);
293             } else {
294                 paint.setARGB(0x80, 0, 0, 0xFF);
295             }
296             canvas.drawPath(path, paint);
297         }
298         SkString filename("c:\\Users\\caryclark\\Documents\\");
299         filename.appendf("f%d.png", index);
300         ToolUtils::EncodeImageToFile(filename.c_str(), bitmap, SkEncodedImageFormat::kPNG, 100);
301     }
302 }
303 #endif
304 
oneOff(skiatest::Reporter * reporter,const ConicPts & conic1,const ConicPts & conic2,bool coin)305 static void oneOff(skiatest::Reporter* reporter, const ConicPts& conic1, const ConicPts& conic2,
306         bool coin) {
307 #if DEBUG_VISUALIZE_CONICS
308     writeFrames();
309 #endif
310     SkDConic c1, c2;
311     c1.debugSet(conic1.fPts.fPts, conic1.fWeight);
312     c2.debugSet(conic2.fPts.fPts, conic2.fWeight);
313     chopBothWays(c1, 0.5, "c1");
314     chopBothWays(c2, 0.5, "c2");
315 #if DEBUG_VISUALIZE_CONICS
316     writeDPng(c1, "d1");
317     writeDPng(c2, "d2");
318 #endif
319     SkASSERT(ValidConic(c1));
320     SkASSERT(ValidConic(c2));
321     SkIntersections intersections;
322     intersections.intersect(c1, c2);
323     REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
324     double tt1, tt2;
325     SkDPoint xy1, xy2;
326     for (int pt3 = 0; pt3 < intersections.used(); ++pt3) {
327         tt1 = intersections[0][pt3];
328         xy1 = c1.ptAtT(tt1);
329         tt2 = intersections[1][pt3];
330         xy2 = c2.ptAtT(tt2);
331         const SkDPoint& iPt = intersections.pt(pt3);
332         REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt));
333         REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt));
334         REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
335     }
336     reporter->bumpTestCount();
337 }
338 
oneOff(skiatest::Reporter * reporter,int outer,int inner)339 static void oneOff(skiatest::Reporter* reporter, int outer, int inner) {
340     const ConicPts& c1 = testSet[outer];
341     const ConicPts& c2 = testSet[inner];
342     oneOff(reporter, c1, c2, false);
343 }
344 
oneOffTests(skiatest::Reporter * reporter)345 static void oneOffTests(skiatest::Reporter* reporter) {
346     for (int outer = 0; outer < testSetCount - 1; ++outer) {
347         for (int inner = outer + 1; inner < testSetCount; ++inner) {
348             oneOff(reporter, outer, inner);
349         }
350     }
351 }
352 
DEF_TEST(PathOpsConicIntersectionOneOff,reporter)353 DEF_TEST(PathOpsConicIntersectionOneOff, reporter) {
354     oneOff(reporter, 0, 1);
355 }
356 
DEF_TEST(PathOpsConicIntersection,reporter)357 DEF_TEST(PathOpsConicIntersection, reporter) {
358     oneOffTests(reporter);
359 }
360