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