Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
PathOpsConicIntersectionTest.cpp
Go to the documentation of this file.
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 */
10#include "src/core/SkGeometry.h"
17#include "tests/Test.h"
18
19#include <array>
20
21/*
22manually 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
28static 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
54const int testSetCount = (int) std::size(testSet);
55
56static 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
80
81static void writePng(const SkConic& c, const SkConic ch[2], const char* name) {
82 const int scale = 10;
83 SkConic conic, chopped[2];
84 for (int index = 0; index < 3; ++index) {
85 conic.fPts[index].fX = c.fPts[index].fX * scale;
86 conic.fPts[index].fY = c.fPts[index].fY * scale;
87 for (int chIndex = 0; chIndex < 2; ++chIndex) {
88 chopped[chIndex].fPts[index].fX = ch[chIndex].fPts[index].fX * scale;
89 chopped[chIndex].fPts[index].fY = ch[chIndex].fPts[index].fY * scale;
90 }
91 }
92 conic.fW = c.fW;
93 chopped[0].fW = ch[0].fW;
94 chopped[1].fW = ch[1].fW;
96 SkRect bounds;
97 conic.computeTightBounds(&bounds);
98 bounds.outset(10, 10);
100 SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height())));
101 SkCanvas canvas(bitmap);
103 paint.setAntiAlias(true);
105 canvas.translate(-bounds.fLeft, -bounds.fTop);
106 canvas.drawColor(SK_ColorWHITE);
107 SkPath path;
108 path.moveTo(conic.fPts[0]);
109 path.conicTo(conic.fPts[1], conic.fPts[2], conic.fW);
110 paint.setARGB(0x80, 0xFF, 0, 0);
111 canvas.drawPath(path, paint);
112 path.reset();
113 path.moveTo(chopped[0].fPts[0]);
114 path.conicTo(chopped[0].fPts[1], chopped[0].fPts[2], chopped[0].fW);
115 path.moveTo(chopped[1].fPts[0]);
116 path.conicTo(chopped[1].fPts[1], chopped[1].fPts[2], chopped[1].fW);
117 paint.setARGB(0x80, 0, 0, 0xFF);
118 canvas.drawPath(path, paint);
119 SkString filename("c:\\Users\\caryclark\\Documents\\");
120 filename.appendf("%s.png", name);
122}
123
124static void writeDPng(const SkDConic& dC, const char* name) {
125 const int scale = 5;
126 SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale },
127 {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale },
128 {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight };
131 bounds.setBounds(dConic);
132 bounds.fLeft -= 10;
133 bounds.fTop -= 10;
134 bounds.fRight += 10;
135 bounds.fBottom += 10;
136 bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul(
139 SkCanvas canvas(bitmap);
141 paint.setAntiAlias(true);
143 canvas.translate(SkDoubleToScalar(-bounds.fLeft), SkDoubleToScalar(-bounds.fTop));
144 canvas.drawColor(SK_ColorWHITE);
145 SkPath path;
146 path.moveTo(dConic.fPts[0].asSkPoint());
147 path.conicTo(dConic.fPts[1].asSkPoint(), dConic.fPts[2].asSkPoint(), dConic.fWeight);
148 paint.setARGB(0x80, 0xFF, 0, 0);
149 canvas.drawPath(path, paint);
150 path.reset();
151 const int chops = 2;
152 for (int tIndex = 0; tIndex < chops; ++tIndex) {
153 SkDConic chopped = dConic.subDivide(tIndex / (double) chops,
154 (tIndex + 1) / (double) chops);
155 path.moveTo(chopped.fPts[0].asSkPoint());
156 path.conicTo(chopped.fPts[1].asSkPoint(), chopped.fPts[2].asSkPoint(), chopped.fWeight);
157 }
158 paint.setARGB(0x80, 0, 0, 0xFF);
159 canvas.drawPath(path, paint);
160 SkString filename("c:\\Users\\caryclark\\Documents\\");
161 filename.appendf("%s.png", name);
162 ToolUtils::EncodeImageToPngFile(filename.c_str(), bitmap);
163}
164#endif
165
166static void chopBothWays(const SkDConic& dConic, double t, const char* name) {
167 SkConic conic;
168 for (int index = 0; index < 3; ++index) {
169 conic.fPts[index] = dConic.fPts[index].asSkPoint();
170 }
171 conic.fW = dConic.fWeight;
172 SkConic chopped[2];
173 SkDConic dChopped[2];
174 if (!conic.chopAt(SkDoubleToScalar(t), chopped)) {
175 return;
176 }
177 dChopped[0] = dConic.subDivide(0, t);
178 dChopped[1] = dConic.subDivide(t, 1);
179#if DEBUG_VISUALIZE_CONICS
180 dConic.dump();
181#endif
182 chopCompare(chopped, dChopped);
183#if DEBUG_VISUALIZE_CONICS
184 writePng(conic, chopped, name);
185#endif
186}
187
188#if DEBUG_VISUALIZE_CONICS
189const SkDConic frame0[] = {
190{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
191{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
192};
193
194const SkDConic frame1[] = {
195{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
196{{{{306.58801299999999, -227.983994}, {212.46499600000001, -262.24200400000001}, {95.551200899999998, 58.976398500000002}}}, 0.707107008f},
197{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f},
198{{{{134.08399674208422, -155.06258330544892}, {30.390000629402859, -143.55685905168704}, {23.185499199999999, -102.697998}}}, 0.923879623f},
199};
200
201const SkDConic frame2[] = {
202{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
203{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
204{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f},
205{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f},
206};
207
208const SkDConic frame3[] = {
209{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
210{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
211{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f},
212{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f},
213};
214
215const SkDConic frame4[] = {
216{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
217{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
218{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f},
219{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f},
220};
221
222const SkDConic frame5[] = {
223{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
224{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
225{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f},
226{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f},
227};
228
229const SkDConic frame6[] = {
230{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
231{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
232{{{{205.78973252799028, -158.12538713371103}, {190.33692178059735, -137.11320166154385}, {174.87004877564593, -111.2132534799228}}}, 0.858117759f},
233{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f},
234};
235
236const SkDConic* frames[] = {
237 frame0, frame1, frame2, frame3, frame4, frame5, frame6
238};
239
240const int frameSizes[] = { (int) std::size(frame0), (int) std::size(frame1),
241 (int) std::size(frame2), (int) std::size(frame3),
242 (int) std::size(frame4), (int) std::size(frame5),
243 (int) std::size(frame6),
244};
245
246static void writeFrames() {
247 const int scale = 5;
248
249 for (int index = 0; index < (int) std::size(frameSizes); ++index) {
251 bool boundsSet = false;
252 int frameSize = frameSizes[index];
253 for (int fIndex = 0; fIndex < frameSize; ++fIndex) {
254 const SkDConic& dC = frames[index][fIndex];
255 SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale },
256 {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale },
257 {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight };
258 SkDRect dBounds;
259 dBounds.setBounds(dConic);
260 if (!boundsSet) {
261 bounds = dBounds;
262 boundsSet = true;
263 } else {
264 bounds.add((SkDPoint&) dBounds.fLeft);
265 bounds.add((SkDPoint&) dBounds.fRight);
266 }
267 }
268 bounds.fLeft -= 10;
269 bounds.fTop -= 10;
270 bounds.fRight += 10;
271 bounds.fBottom += 10;
273 bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul(
276 SkCanvas canvas(bitmap);
278 paint.setAntiAlias(true);
280 canvas.translate(SkDoubleToScalar(-bounds.fLeft), SkDoubleToScalar(-bounds.fTop));
281 canvas.drawColor(SK_ColorWHITE);
282 for (int fIndex = 0; fIndex < frameSize; ++fIndex) {
283 const SkDConic& dC = frames[index][fIndex];
284 SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale },
285 {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale },
286 {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight };
287 SkPath path;
288 path.moveTo(dConic.fPts[0].asSkPoint());
289 path.conicTo(dConic.fPts[1].asSkPoint(), dConic.fPts[2].asSkPoint(), dConic.fWeight);
290 if (fIndex < 2) {
291 paint.setARGB(0x80, 0xFF, 0, 0);
292 } else {
293 paint.setARGB(0x80, 0, 0, 0xFF);
294 }
295 canvas.drawPath(path, paint);
296 }
297 SkString filename("c:\\Users\\caryclark\\Documents\\");
298 filename.appendf("f%d.png", index);
299 ToolUtils::EncodeImageToPngFile(filename.c_str(), bitmap);
300 }
301}
302#endif
303
304static void oneOff(skiatest::Reporter* reporter, const ConicPts& conic1, const ConicPts& conic2,
305 bool coin) {
306#if DEBUG_VISUALIZE_CONICS
307 writeFrames();
308#endif
309 SkDConic c1, c2;
310 c1.debugSet(conic1.fPts.fPts, conic1.fWeight);
311 c2.debugSet(conic2.fPts.fPts, conic2.fWeight);
312 chopBothWays(c1, 0.5, "c1");
313 chopBothWays(c2, 0.5, "c2");
314#if DEBUG_VISUALIZE_CONICS
315 writeDPng(c1, "d1");
316 writeDPng(c2, "d2");
317#endif
318 SkASSERT(ValidConic(c1));
319 SkASSERT(ValidConic(c2));
320 SkIntersections intersections;
321 intersections.intersect(c1, c2);
322 REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
323 double tt1, tt2;
324 SkDPoint xy1, xy2;
325 for (int pt3 = 0; pt3 < intersections.used(); ++pt3) {
326 tt1 = intersections[0][pt3];
327 xy1 = c1.ptAtT(tt1);
328 tt2 = intersections[1][pt3];
329 xy2 = c2.ptAtT(tt2);
330 const SkDPoint& iPt = intersections.pt(pt3);
334 }
335 reporter->bumpTestCount();
336}
337
338static void oneOff(skiatest::Reporter* reporter, int outer, int inner) {
339 const ConicPts& c1 = testSet[outer];
340 const ConicPts& c2 = testSet[inner];
341 oneOff(reporter, c1, c2, false);
342}
343
345 for (int outer = 0; outer < testSetCount - 1; ++outer) {
346 for (int inner = outer + 1; inner < testSetCount; ++inner) {
347 oneOff(reporter, outer, inner);
348 }
349 }
350}
351
352DEF_TEST(PathOpsConicIntersectionOneOff, reporter) {
353 oneOff(reporter, 0, 1);
354}
355
356DEF_TEST(PathOpsConicIntersection, reporter) {
358}
SkPoint fPts[2]
reporter
static void chopBothWays(const SkDConic &dConic, double t, const char *name)
static const ConicPts testSet[]
static void oneOff(skiatest::Reporter *reporter, const ConicPts &conic1, const ConicPts &conic2, bool coin)
static void oneOffTests(skiatest::Reporter *reporter)
static void chopCompare(const SkConic chopped[2], const SkDConic dChopped[2])
const int testSetCount
bool ValidConic(const SkDConic &conic)
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
bool roughly_equal(double x, double y)
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkDoubleToScalar(x)
Definition SkScalar.h:64
#define DEF_TEST(name, reporter)
Definition Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
Type::kYUV Type::kRGBA() int(0.7 *637)
void translate(SkScalar dx, SkScalar dy)
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition SkCanvas.h:1182
void drawPath(const SkPath &path, const SkPaint &paint)
int intersect(const SkDLine &, const SkDLine &)
const SkDPoint & pt(int index) const
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
const char * c_str() const
Definition SkString.h:133
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:550
const Paint & paint
const char * name
Definition fuchsia.cc:50
Optional< SkRect > bounds
Definition SkRecords.h:189
bool EncodeImageToPngFile(const char *path, const SkBitmap &src)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
const Scalar scale
SkScalar fWeight
SkDPoint fPts[kPointCount]
SkScalar fW
Definition SkGeometry.h:337
SkPoint fPts[3]
Definition SkGeometry.h:336
SkDPoint ptAtT(double t) const
void dump() const
SkDQuad fPts
SkDConic subDivide(double t1, double t2) const
void debugSet(const SkDPoint *pts, SkScalar weight)
SkScalar fWeight
void set(const SkPoint &pt)
bool approximatelyEqual(const SkDPoint &a) const
double fRight
void setBounds(const SkDConic &curve)
double fLeft
static SkImageInfo MakeN32Premul(int width, int height)
float fX
x-axis value
float fY
y-axis value