1/*
2 * Copyright 2011 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
8#include "SkPathMeasure.h"
9#include "Test.h"
10
11static void test_small_segment3() {
12    SkPath path;
13    const SkPoint pts[] = {
14        { 0, 0 },
15        { 100000000000.0f, 100000000000.0f }, { 0, 0 }, { 10, 10 },
16        { 10, 10 }, { 0, 0 }, { 10, 10 }
17    };
18
19    path.moveTo(pts[0]);
20    for (size_t i = 1; i < SK_ARRAY_COUNT(pts); i += 3) {
21        path.cubicTo(pts[i], pts[i + 1], pts[i + 2]);
22    }
23
24    SkPathMeasure meas(path, false);
25    meas.getLength();
26}
27
28static void test_small_segment2() {
29    SkPath path;
30    const SkPoint pts[] = {
31        { 0, 0 },
32        { 100000000000.0f, 100000000000.0f }, { 0, 0 },
33        { 10, 10 }, { 0, 0 },
34    };
35
36    path.moveTo(pts[0]);
37    for (size_t i = 1; i < SK_ARRAY_COUNT(pts); i += 2) {
38        path.quadTo(pts[i], pts[i + 1]);
39    }
40    SkPathMeasure meas(path, false);
41    meas.getLength();
42}
43
44static void test_small_segment() {
45    SkPath path;
46    const SkPoint pts[] = {
47        { 100000, 100000},
48        // big jump between these points, makes a big segment
49        { 1.0005f, 0.9999f },
50        // tiny (non-zero) jump between these points
51        { SK_Scalar1, SK_Scalar1 },
52    };
53
54    path.moveTo(pts[0]);
55    for (size_t i = 1; i < SK_ARRAY_COUNT(pts); ++i) {
56        path.lineTo(pts[i]);
57    }
58    SkPathMeasure meas(path, false);
59
60    /*  this would assert (before a fix) because we added a segment with
61        the same length as the prev segment, due to the follow (bad) pattern
62
63        d = distance(pts[0], pts[1]);
64        distance += d;
65        seg->fDistance = distance;
66
67        SkASSERT(d > 0);    // TRUE
68        SkASSERT(seg->fDistance > prevSeg->fDistance);  // FALSE
69
70        This 2nd assert failes because (distance += d) didn't affect distance
71        because distance >>> d.
72     */
73    meas.getLength();
74}
75
76DEF_TEST(PathMeasure, reporter) {
77    SkPath  path;
78
79    path.moveTo(0, 0);
80    path.lineTo(SK_Scalar1, 0);
81    path.lineTo(SK_Scalar1, SK_Scalar1);
82    path.lineTo(0, SK_Scalar1);
83
84    SkPathMeasure   meas(path, true);
85    SkScalar        length = meas.getLength();
86    SkASSERT(length == SK_Scalar1*4);
87
88    path.reset();
89    path.moveTo(0, 0);
90    path.lineTo(SK_Scalar1*3, SK_Scalar1*4);
91    meas.setPath(&path, false);
92    length = meas.getLength();
93    REPORTER_ASSERT(reporter, length == SK_Scalar1*5);
94
95    path.reset();
96    path.addCircle(0, 0, SK_Scalar1);
97    meas.setPath(&path, true);
98    length = meas.getLength();
99//    SkDebugf("circle arc-length = %g\n", length);
100
101    // Test the behavior following a close not followed by a move.
102    path.reset();
103    path.lineTo(SK_Scalar1, 0);
104    path.lineTo(SK_Scalar1, SK_Scalar1);
105    path.lineTo(0, SK_Scalar1);
106    path.close();
107    path.lineTo(-SK_Scalar1, 0);
108    meas.setPath(&path, false);
109    length = meas.getLength();
110    REPORTER_ASSERT(reporter, length == SK_Scalar1 * 4);
111    meas.nextContour();
112    length = meas.getLength();
113    REPORTER_ASSERT(reporter, length == SK_Scalar1);
114    SkPoint position;
115    SkVector tangent;
116    REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
117    REPORTER_ASSERT(reporter,
118        SkScalarNearlyEqual(position.fX,
119                            -SK_ScalarHalf,
120                            0.0001f));
121    REPORTER_ASSERT(reporter, position.fY == 0);
122    REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1);
123    REPORTER_ASSERT(reporter, tangent.fY == 0);
124
125    // Test degenerate paths
126    path.reset();
127    path.moveTo(0, 0);
128    path.lineTo(0, 0);
129    path.lineTo(SK_Scalar1, 0);
130    path.quadTo(SK_Scalar1, 0, SK_Scalar1, 0);
131    path.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1 * 2);
132    path.cubicTo(SK_Scalar1, SK_Scalar1 * 2,
133                 SK_Scalar1, SK_Scalar1 * 2,
134                 SK_Scalar1, SK_Scalar1 * 2);
135    path.cubicTo(SK_Scalar1*2, SK_Scalar1 * 2,
136                 SK_Scalar1*3, SK_Scalar1 * 2,
137                 SK_Scalar1*4, SK_Scalar1 * 2);
138    meas.setPath(&path, false);
139    length = meas.getLength();
140    REPORTER_ASSERT(reporter, length == SK_Scalar1 * 6);
141    REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
142    REPORTER_ASSERT(reporter,
143        SkScalarNearlyEqual(position.fX,
144                            SK_ScalarHalf,
145                            0.0001f));
146    REPORTER_ASSERT(reporter, position.fY == 0);
147    REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
148    REPORTER_ASSERT(reporter, tangent.fY == 0);
149    REPORTER_ASSERT(reporter, meas.getPosTan(2.5f, &position, &tangent));
150    REPORTER_ASSERT(reporter,
151        SkScalarNearlyEqual(position.fX, SK_Scalar1, 0.0001f));
152    REPORTER_ASSERT(reporter,
153        SkScalarNearlyEqual(position.fY, 1.5f));
154    REPORTER_ASSERT(reporter, tangent.fX == 0);
155    REPORTER_ASSERT(reporter, tangent.fY == SK_Scalar1);
156    REPORTER_ASSERT(reporter, meas.getPosTan(4.5f, &position, &tangent));
157    REPORTER_ASSERT(reporter,
158        SkScalarNearlyEqual(position.fX,
159                            2.5f,
160                            0.0001f));
161    REPORTER_ASSERT(reporter,
162        SkScalarNearlyEqual(position.fY,
163                            2.0f,
164                            0.0001f));
165    REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
166    REPORTER_ASSERT(reporter, tangent.fY == 0);
167
168    path.reset();
169    path.moveTo(0, 0);
170    path.lineTo(SK_Scalar1, 0);
171    path.moveTo(SK_Scalar1, SK_Scalar1);
172    path.moveTo(SK_Scalar1 * 2, SK_Scalar1 * 2);
173    path.lineTo(SK_Scalar1, SK_Scalar1 * 2);
174    meas.setPath(&path, false);
175    length = meas.getLength();
176    REPORTER_ASSERT(reporter, length == SK_Scalar1);
177    REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
178    REPORTER_ASSERT(reporter,
179        SkScalarNearlyEqual(position.fX,
180                            SK_ScalarHalf,
181                            0.0001f));
182    REPORTER_ASSERT(reporter, position.fY == 0);
183    REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
184    REPORTER_ASSERT(reporter, tangent.fY == 0);
185    meas.nextContour();
186    length = meas.getLength();
187    REPORTER_ASSERT(reporter, length == SK_Scalar1);
188    REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
189    REPORTER_ASSERT(reporter,
190        SkScalarNearlyEqual(position.fX,
191                            1.5f,
192                            0.0001f));
193    REPORTER_ASSERT(reporter,
194        SkScalarNearlyEqual(position.fY,
195                            2.0f,
196                            0.0001f));
197    REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1);
198    REPORTER_ASSERT(reporter, tangent.fY == 0);
199
200    test_small_segment();
201    test_small_segment2();
202    test_small_segment3();
203}
204