/src/mozilla-central/layout/painting/DottedCornerFinder.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "DottedCornerFinder.h" |
8 | | |
9 | | #include "mozilla/Move.h" |
10 | | #include "BorderCache.h" |
11 | | #include "BorderConsts.h" |
12 | | |
13 | | namespace mozilla { |
14 | | |
15 | | using namespace gfx; |
16 | | |
17 | | static inline Float |
18 | | Square(Float x) |
19 | 0 | { |
20 | 0 | return x * x; |
21 | 0 | } |
22 | | |
23 | | static Point |
24 | | PointRotateCCW90(const Point& aP) |
25 | 0 | { |
26 | 0 | return Point(aP.y, -aP.x); |
27 | 0 | } |
28 | | |
29 | | struct BestOverlap |
30 | | { |
31 | | Float overlap; |
32 | | size_t count; |
33 | | |
34 | | BestOverlap() |
35 | | : overlap(0.0f) |
36 | | , count(0) |
37 | 0 | { |
38 | 0 | } |
39 | | |
40 | | BestOverlap(Float aOverlap, size_t aCount) |
41 | | : overlap(aOverlap) |
42 | | , count(aCount) |
43 | 0 | { |
44 | 0 | } |
45 | | }; |
46 | | |
47 | | static const size_t DottedCornerCacheSize = 256; |
48 | | nsDataHashtable<FourFloatsHashKey, BestOverlap> DottedCornerCache; |
49 | | |
50 | | DottedCornerFinder::DottedCornerFinder(const Bezier& aOuterBezier, |
51 | | const Bezier& aInnerBezier, |
52 | | Corner aCorner, |
53 | | Float aBorderRadiusX, |
54 | | Float aBorderRadiusY, |
55 | | const Point& aC0, |
56 | | Float aR0, |
57 | | const Point& aCn, |
58 | | Float aRn, |
59 | | const Size& aCornerDim) |
60 | | : mOuterBezier(aOuterBezier) |
61 | | , mInnerBezier(aInnerBezier) |
62 | | , mCorner(aCorner) |
63 | | , mNormalSign((aCorner == C_TL || aCorner == C_BR) ? -1.0f : 1.0f) |
64 | | , mC0(aC0) |
65 | | , mCn(aCn) |
66 | | , mR0(aR0) |
67 | | , mRn(aRn) |
68 | | , mMaxR(std::max(aR0, aRn)) |
69 | | , mCenterCurveOrigin(mC0.x, mCn.y) |
70 | | , mCenterCurveR(0.0) |
71 | | , mInnerCurveOrigin(mInnerBezier.mPoints[0].x, mInnerBezier.mPoints[3].y) |
72 | | , mBestOverlap(0.0f) |
73 | | , mHasZeroBorderWidth(false) |
74 | | , mHasMore(true) |
75 | | , mMaxCount(aCornerDim.width + aCornerDim.height) |
76 | | , mType(OTHER) |
77 | | , mI(0) |
78 | | , mCount(0) |
79 | 0 | { |
80 | 0 | NS_ASSERTION(mR0 > 0.0f || mRn > 0.0f, |
81 | 0 | "At least one side should have non-zero radius."); |
82 | 0 |
|
83 | 0 | mInnerWidth = fabs(mInnerBezier.mPoints[0].x - mInnerBezier.mPoints[3].x); |
84 | 0 | mInnerHeight = fabs(mInnerBezier.mPoints[0].y - mInnerBezier.mPoints[3].y); |
85 | 0 |
|
86 | 0 | DetermineType(aBorderRadiusX, aBorderRadiusY); |
87 | 0 |
|
88 | 0 | Reset(); |
89 | 0 | } |
90 | | |
91 | | static bool |
92 | | IsSingleCurve(Float aMinR, |
93 | | Float aMaxR, |
94 | | Float aMinBorderRadius, |
95 | | Float aMaxBorderRadius) |
96 | 0 | { |
97 | 0 | return aMinR > 0.0f && aMinBorderRadius > aMaxR * 4.0f && |
98 | 0 | aMinBorderRadius / aMaxBorderRadius > 0.5f; |
99 | 0 | } |
100 | | |
101 | | void |
102 | | DottedCornerFinder::DetermineType(Float aBorderRadiusX, Float aBorderRadiusY) |
103 | 0 | { |
104 | 0 | // Calculate parameters for the center curve before swap. |
105 | 0 | Float centerCurveWidth = fabs(mC0.x - mCn.x); |
106 | 0 | Float centerCurveHeight = fabs(mC0.y - mCn.y); |
107 | 0 | Point cornerPoint(mCn.x, mC0.y); |
108 | 0 |
|
109 | 0 | bool swapped = false; |
110 | 0 | if (mR0 < mRn) { |
111 | 0 | // Always draw from wider side to thinner side. |
112 | 0 | Swap(mC0, mCn); |
113 | 0 | Swap(mR0, mRn); |
114 | 0 | Swap(mInnerBezier.mPoints[0], mInnerBezier.mPoints[3]); |
115 | 0 | Swap(mInnerBezier.mPoints[1], mInnerBezier.mPoints[2]); |
116 | 0 | Swap(mOuterBezier.mPoints[0], mOuterBezier.mPoints[3]); |
117 | 0 | Swap(mOuterBezier.mPoints[1], mOuterBezier.mPoints[2]); |
118 | 0 | mNormalSign = -mNormalSign; |
119 | 0 | swapped = true; |
120 | 0 | } |
121 | 0 |
|
122 | 0 | // See the comment at mType declaration for each condition. |
123 | 0 |
|
124 | 0 | Float minR = std::min(mR0, mRn); |
125 | 0 | Float minBorderRadius = std::min(aBorderRadiusX, aBorderRadiusY); |
126 | 0 | Float maxBorderRadius = std::max(aBorderRadiusX, aBorderRadiusY); |
127 | 0 | if (IsSingleCurve(minR, mMaxR, minBorderRadius, maxBorderRadius)) { |
128 | 0 | if (mR0 == mRn) { |
129 | 0 | Float borderLength; |
130 | 0 | if (minBorderRadius == maxBorderRadius) { |
131 | 0 | mType = PERFECT; |
132 | 0 | borderLength = M_PI * centerCurveHeight / 2.0f; |
133 | 0 |
|
134 | 0 | mCenterCurveR = centerCurveWidth; |
135 | 0 | } else { |
136 | 0 | mType = SINGLE_CURVE_AND_RADIUS; |
137 | 0 | borderLength = |
138 | 0 | GetQuarterEllipticArcLength(centerCurveWidth, centerCurveHeight); |
139 | 0 | } |
140 | 0 |
|
141 | 0 | Float diameter = mR0 * 2.0f; |
142 | 0 | size_t count = round(borderLength / diameter); |
143 | 0 | if (count % 2) { |
144 | 0 | count++; |
145 | 0 | } |
146 | 0 | mCount = count / 2 - 1; |
147 | 0 | if (mCount > 0) { |
148 | 0 | mBestOverlap = 1.0f - borderLength / (diameter * count); |
149 | 0 | } |
150 | 0 | } else { |
151 | 0 | mType = SINGLE_CURVE; |
152 | 0 | } |
153 | 0 | } |
154 | 0 |
|
155 | 0 | if (mType == SINGLE_CURVE_AND_RADIUS || mType == SINGLE_CURVE) { |
156 | 0 | Size cornerSize(centerCurveWidth, centerCurveHeight); |
157 | 0 | GetBezierPointsForCorner(&mCenterBezier, mCorner, cornerPoint, cornerSize); |
158 | 0 | if (swapped) { |
159 | 0 | Swap(mCenterBezier.mPoints[0], mCenterBezier.mPoints[3]); |
160 | 0 | Swap(mCenterBezier.mPoints[1], mCenterBezier.mPoints[2]); |
161 | 0 | } |
162 | 0 | } |
163 | 0 |
|
164 | 0 | if (minR == 0.0f) { |
165 | 0 | mHasZeroBorderWidth = true; |
166 | 0 | } |
167 | 0 |
|
168 | 0 | if ((mType == SINGLE_CURVE || mType == OTHER) && !mHasZeroBorderWidth) { |
169 | 0 | FindBestOverlap(minR, minBorderRadius, maxBorderRadius); |
170 | 0 | } |
171 | 0 | } |
172 | | |
173 | | bool |
174 | | DottedCornerFinder::HasMore(void) const |
175 | 0 | { |
176 | 0 | if (mHasZeroBorderWidth) { |
177 | 0 | return mI < mMaxCount && mHasMore; |
178 | 0 | } |
179 | 0 |
|
180 | 0 | return mI < mCount; |
181 | 0 | } |
182 | | |
183 | | DottedCornerFinder::Result |
184 | | DottedCornerFinder::Next(void) |
185 | 0 | { |
186 | 0 | mI++; |
187 | 0 |
|
188 | 0 | if (mType == PERFECT) { |
189 | 0 | Float phi = mI * 4.0f * mR0 * (1 - mBestOverlap) / mCenterCurveR; |
190 | 0 | if (mCorner == C_TL) { |
191 | 0 | phi = -M_PI / 2.0f - phi; |
192 | 0 | } else if (mCorner == C_TR) { |
193 | 0 | phi = -M_PI / 2.0f + phi; |
194 | 0 | } else if (mCorner == C_BR) { |
195 | 0 | phi = M_PI / 2.0f - phi; |
196 | 0 | } else { |
197 | 0 | phi = M_PI / 2.0f + phi; |
198 | 0 | } |
199 | 0 |
|
200 | 0 | Point C(mCenterCurveOrigin.x + mCenterCurveR * cos(phi), |
201 | 0 | mCenterCurveOrigin.y + mCenterCurveR * sin(phi)); |
202 | 0 | return DottedCornerFinder::Result(C, mR0); |
203 | 0 | } |
204 | 0 |
|
205 | 0 | // Find unfilled and filled circles. |
206 | 0 | (void)FindNext(mBestOverlap); |
207 | 0 | if (mHasMore) { |
208 | 0 | (void)FindNext(mBestOverlap); |
209 | 0 | } |
210 | 0 |
|
211 | 0 | return Result(mLastC, mLastR); |
212 | 0 | } |
213 | | |
214 | | void |
215 | | DottedCornerFinder::Reset(void) |
216 | 0 | { |
217 | 0 | mLastC = mC0; |
218 | 0 | mLastR = mR0; |
219 | 0 | mLastT = 0.0f; |
220 | 0 | mHasMore = true; |
221 | 0 | } |
222 | | |
223 | | void |
224 | | DottedCornerFinder::FindPointAndRadius(Point& C, |
225 | | Float& r, |
226 | | const Point& innerTangent, |
227 | | const Point& normal, |
228 | | Float t) |
229 | 0 | { |
230 | 0 | // Find radius for the given tangent point on the inner curve such that the |
231 | 0 | // circle is also tangent to the outer curve. |
232 | 0 |
|
233 | 0 | NS_ASSERTION(mType == OTHER, "Wrong mType"); |
234 | 0 |
|
235 | 0 | Float lower = 0.0f; |
236 | 0 | Float upper = mMaxR; |
237 | 0 | const Float DIST_MARGIN = 0.1f; |
238 | 0 | for (size_t i = 0; i < MAX_LOOP; i++) { |
239 | 0 | r = (upper + lower) / 2.0f; |
240 | 0 | C = innerTangent + normal * r; |
241 | 0 |
|
242 | 0 | Point Near = FindBezierNearestPoint(mOuterBezier, C, t); |
243 | 0 | Float distSquare = (C - Near).LengthSquare(); |
244 | 0 |
|
245 | 0 | if (distSquare > Square(r + DIST_MARGIN)) { |
246 | 0 | lower = r; |
247 | 0 | } else if (distSquare < Square(r - DIST_MARGIN)) { |
248 | 0 | upper = r; |
249 | 0 | } else { |
250 | 0 | break; |
251 | 0 | } |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | | Float |
256 | | DottedCornerFinder::FindNext(Float overlap) |
257 | 0 | { |
258 | 0 | Float lower = mLastT; |
259 | 0 | Float upper = 1.0f; |
260 | 0 | Float t; |
261 | 0 |
|
262 | 0 | Point C = mLastC; |
263 | 0 | Float r = 0.0f; |
264 | 0 |
|
265 | 0 | Float factor = (1.0f - overlap); |
266 | 0 |
|
267 | 0 | Float circlesDist = 0.0f; |
268 | 0 | Float expectedDist = 0.0f; |
269 | 0 |
|
270 | 0 | const Float DIST_MARGIN = 0.1f; |
271 | 0 | if (mType == SINGLE_CURVE_AND_RADIUS) { |
272 | 0 | r = mR0; |
273 | 0 |
|
274 | 0 | expectedDist = (r + mLastR) * factor; |
275 | 0 |
|
276 | 0 | // Find C_i on the center curve. |
277 | 0 | for (size_t i = 0; i < MAX_LOOP; i++) { |
278 | 0 | t = (upper + lower) / 2.0f; |
279 | 0 | C = GetBezierPoint(mCenterBezier, t); |
280 | 0 |
|
281 | 0 | // Check overlap along arc. |
282 | 0 | circlesDist = GetBezierLength(mCenterBezier, mLastT, t); |
283 | 0 | if (circlesDist < expectedDist - DIST_MARGIN) { |
284 | 0 | lower = t; |
285 | 0 | } else if (circlesDist > expectedDist + DIST_MARGIN) { |
286 | 0 | upper = t; |
287 | 0 | } else { |
288 | 0 | break; |
289 | 0 | } |
290 | 0 | } |
291 | 0 | } else if (mType == SINGLE_CURVE) { |
292 | 0 | // Find C_i on the center curve, and calculate r_i. |
293 | 0 | for (size_t i = 0; i < MAX_LOOP; i++) { |
294 | 0 | t = (upper + lower) / 2.0f; |
295 | 0 | C = GetBezierPoint(mCenterBezier, t); |
296 | 0 |
|
297 | 0 | Point Diff = GetBezierDifferential(mCenterBezier, t); |
298 | 0 | Float DiffLength = Diff.Length(); |
299 | 0 | if (DiffLength == 0.0f) { |
300 | 0 | // Basically this shouldn't happen. |
301 | 0 | // If differential is 0, we cannot calculate tangent circle, |
302 | 0 | // skip this point. |
303 | 0 | t = (t + upper) / 2.0f; |
304 | 0 | continue; |
305 | 0 | } |
306 | 0 | |
307 | 0 | Point normal = PointRotateCCW90(Diff / DiffLength) * (-mNormalSign); |
308 | 0 | r = CalculateDistanceToEllipticArc( |
309 | 0 | C, normal, mInnerCurveOrigin, mInnerWidth, mInnerHeight); |
310 | 0 |
|
311 | 0 | // Check overlap along arc. |
312 | 0 | circlesDist = GetBezierLength(mCenterBezier, mLastT, t); |
313 | 0 | expectedDist = (r + mLastR) * factor; |
314 | 0 | if (circlesDist < expectedDist - DIST_MARGIN) { |
315 | 0 | lower = t; |
316 | 0 | } else if (circlesDist > expectedDist + DIST_MARGIN) { |
317 | 0 | upper = t; |
318 | 0 | } else { |
319 | 0 | break; |
320 | 0 | } |
321 | 0 | } |
322 | 0 | } else { |
323 | 0 | Float distSquareMax = Square(mMaxR * 3.0f); |
324 | 0 | Float circlesDistSquare = 0.0f; |
325 | 0 |
|
326 | 0 | // Find C_i and r_i. |
327 | 0 | for (size_t i = 0; i < MAX_LOOP; i++) { |
328 | 0 | t = (upper + lower) / 2.0f; |
329 | 0 | Point innerTangent = GetBezierPoint(mInnerBezier, t); |
330 | 0 | if ((innerTangent - mLastC).LengthSquare() > distSquareMax) { |
331 | 0 | // It's clear that this tangent point is too far, skip it. |
332 | 0 | upper = t; |
333 | 0 | continue; |
334 | 0 | } |
335 | 0 | |
336 | 0 | Point Diff = GetBezierDifferential(mInnerBezier, t); |
337 | 0 | Float DiffLength = Diff.Length(); |
338 | 0 | if (DiffLength == 0.0f) { |
339 | 0 | // Basically this shouldn't happen. |
340 | 0 | // If differential is 0, we cannot calculate tangent circle, |
341 | 0 | // skip this point. |
342 | 0 | t = (t + upper) / 2.0f; |
343 | 0 | continue; |
344 | 0 | } |
345 | 0 | |
346 | 0 | Point normal = PointRotateCCW90(Diff / DiffLength) * mNormalSign; |
347 | 0 | FindPointAndRadius(C, r, innerTangent, normal, t); |
348 | 0 |
|
349 | 0 | // Check overlap with direct distance. |
350 | 0 | circlesDistSquare = (C - mLastC).LengthSquare(); |
351 | 0 | expectedDist = (r + mLastR) * factor; |
352 | 0 | if (circlesDistSquare < Square(expectedDist - DIST_MARGIN)) { |
353 | 0 | lower = t; |
354 | 0 | } else if (circlesDistSquare > Square(expectedDist + DIST_MARGIN)) { |
355 | 0 | upper = t; |
356 | 0 | } else { |
357 | 0 | break; |
358 | 0 | } |
359 | 0 | } |
360 | 0 |
|
361 | 0 | circlesDist = sqrt(circlesDistSquare); |
362 | 0 | } |
363 | 0 |
|
364 | 0 | if (mHasZeroBorderWidth) { |
365 | 0 | // When calculating circle around r=0, it may result in wrong radius that |
366 | 0 | // is bigger than previous circle. Detect it and stop calculating. |
367 | 0 | const Float R_MARGIN = 0.1f; |
368 | 0 | if (mLastR < R_MARGIN && r > mLastR) { |
369 | 0 | mHasMore = false; |
370 | 0 | mLastR = 0.0f; |
371 | 0 | return 0.0f; |
372 | 0 | } |
373 | 0 | } |
374 | 0 | |
375 | 0 | mLastT = t; |
376 | 0 | mLastC = C; |
377 | 0 | mLastR = r; |
378 | 0 |
|
379 | 0 | if (mHasZeroBorderWidth) { |
380 | 0 | const Float T_MARGIN = 0.001f; |
381 | 0 | if (mLastT >= 1.0f - T_MARGIN || |
382 | 0 | (mLastC - mCn).LengthSquare() < Square(mLastR)) { |
383 | 0 | mHasMore = false; |
384 | 0 | } |
385 | 0 | } |
386 | 0 |
|
387 | 0 | if (expectedDist == 0.0f) { |
388 | 0 | return 0.0f; |
389 | 0 | } |
390 | 0 | |
391 | 0 | return 1.0f - circlesDist * factor / expectedDist; |
392 | 0 | } |
393 | | |
394 | | void |
395 | | DottedCornerFinder::FindBestOverlap(Float aMinR, |
396 | | Float aMinBorderRadius, |
397 | | Float aMaxBorderRadius) |
398 | 0 | { |
399 | 0 | // If overlap is not calculateable, find it with binary search, |
400 | 0 | // such that there exists i that C_i == C_n with the given overlap. |
401 | 0 |
|
402 | 0 | FourFloats key(aMinR, mMaxR, aMinBorderRadius, aMaxBorderRadius); |
403 | 0 | BestOverlap best; |
404 | 0 | if (DottedCornerCache.Get(key, &best)) { |
405 | 0 | mCount = best.count; |
406 | 0 | mBestOverlap = best.overlap; |
407 | 0 | return; |
408 | 0 | } |
409 | 0 | |
410 | 0 | Float lower = 0.0f; |
411 | 0 | Float upper = 0.5f; |
412 | 0 | // Start from lower bound to find the minimum number of circles. |
413 | 0 | Float overlap = 0.0f; |
414 | 0 | mBestOverlap = overlap; |
415 | 0 | size_t targetCount = 0; |
416 | 0 |
|
417 | 0 | const Float OVERLAP_MARGIN = 0.1f; |
418 | 0 | for (size_t j = 0; j < MAX_LOOP; j++) { |
419 | 0 | Reset(); |
420 | 0 |
|
421 | 0 | size_t count; |
422 | 0 | Float actualOverlap; |
423 | 0 | if (!GetCountAndLastOverlap(overlap, &count, &actualOverlap)) { |
424 | 0 | if (j == 0) { |
425 | 0 | mCount = mMaxCount; |
426 | 0 | break; |
427 | 0 | } |
428 | 0 | } |
429 | 0 | |
430 | 0 | if (j == 0) { |
431 | 0 | if (count < 3 || (count == 3 && actualOverlap > 0.5f)) { |
432 | 0 | // |count == 3 && actualOverlap > 0.5f| means there could be |
433 | 0 | // a circle but it is too near from both ends. |
434 | 0 | // |
435 | 0 | // if actualOverlap == 0.0 |
436 | 0 | // 1 2 3 |
437 | 0 | // +-------+-------+-------+-------+ |
438 | 0 | // | ##### | ***** | ##### | ##### | |
439 | 0 | // |#######|*******|#######|#######| |
440 | 0 | // |###+###|***+***|###+###|###+###| |
441 | 0 | // |# C_0 #|* C_1 *|# C_2 #|# C_n #| |
442 | 0 | // | ##### | ***** | ##### | ##### | |
443 | 0 | // +-------+-------+-------+-------+ |
444 | 0 | // | |
445 | 0 | // V |
446 | 0 | // +-------+---+-------+---+-------+ |
447 | 0 | // | ##### | | ##### | | ##### | |
448 | 0 | // |#######| |#######| |#######| |
449 | 0 | // |###+###| |###+###| |###+###| Find the best overlap to place |
450 | 0 | // |# C_0 #| |# C_1 #| |# C_n #| C_1 at the middle of them |
451 | 0 | // | ##### | | ##### | | ##### | |
452 | 0 | // +-------+---+-------+---|-------+ |
453 | 0 | // |
454 | 0 | // if actualOverlap == 0.5 |
455 | 0 | // 1 2 3 |
456 | 0 | // +-------+-------+-------+---+ |
457 | 0 | // | ##### | ***** | ##### |## | |
458 | 0 | // |#######|*******|##### C_n #| |
459 | 0 | // |###+###|***+***|###+###+###| |
460 | 0 | // |# C_0 #|* C_1 *|# C_2 #|###| |
461 | 0 | // | ##### | ***** | ##### |## | |
462 | 0 | // +-------+-------+-------+---+ |
463 | 0 | // | |
464 | 0 | // V |
465 | 0 | // +-------+-+-------+-+-------+ |
466 | 0 | // | ##### | | ##### | | ##### | |
467 | 0 | // |#######| |#######| |#######| |
468 | 0 | // |###+###| |###+###| |###+###| Even if we place C_1 at the middle |
469 | 0 | // |# C_0 #| |# C_1 #| |# C_n #| of them, it's too near from them |
470 | 0 | // | ##### | | ##### | | ##### | |
471 | 0 | // +-------+-+-------+-|-------+ |
472 | 0 | // | |
473 | 0 | // V |
474 | 0 | // +-------+-----------+-------+ |
475 | 0 | // | ##### | | ##### | |
476 | 0 | // |#######| |#######| |
477 | 0 | // |###+###| |###+###| Do not draw any circle |
478 | 0 | // |# C_0 #| |# C_n #| |
479 | 0 | // | ##### | | ##### | |
480 | 0 | // +-------+-----------+-------+ |
481 | 0 | mCount = 0; |
482 | 0 | break; |
483 | 0 | } |
484 | 0 | |
485 | 0 | // targetCount should be 2n, as we're searching C_1 to C_n. |
486 | 0 | // |
487 | 0 | // targetCount = 4 |
488 | 0 | // mCount = 1 |
489 | 0 | // 1 2 3 4 |
490 | 0 | // +-------+-------+-------+-------+-------+ |
491 | 0 | // | ##### | ***** | ##### | ***** | ##### | |
492 | 0 | // |#######|*******|#######|*******|#######| |
493 | 0 | // |###+###|***+***|###+###|***+***|###+###| |
494 | 0 | // |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_n #| |
495 | 0 | // | ##### | ***** | ##### | ***** | ##### | |
496 | 0 | // +-------+-------+-------+-------+-------+ |
497 | 0 | // 1 |
498 | 0 | // |
499 | 0 | // targetCount = 6 |
500 | 0 | // mCount = 2 |
501 | 0 | // 1 2 3 4 5 6 |
502 | 0 | // +-------+-------+-------+-------+-------+-------+-------+ |
503 | 0 | // | ##### | ***** | ##### | ***** | ##### | ***** | ##### | |
504 | 0 | // |#######|*******|#######|*******|#######|*******|#######| |
505 | 0 | // |###+###|***+***|###+###|***+***|###+###|***+***|###+###| |
506 | 0 | // |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_4 #|* C_5 *|# C_n #| |
507 | 0 | // | ##### | ***** | ##### | ***** | ##### | ***** | ##### | |
508 | 0 | // +-------+-------+-------+-------+-------+-------+-------+ |
509 | 0 | // 1 2 |
510 | 0 | if (count % 2) { |
511 | 0 | targetCount = count + 1; |
512 | 0 | } else { |
513 | 0 | targetCount = count; |
514 | 0 | } |
515 | 0 |
|
516 | 0 | mCount = targetCount / 2 - 1; |
517 | 0 | } |
518 | 0 |
|
519 | 0 | if (count == targetCount) { |
520 | 0 | mBestOverlap = overlap; |
521 | 0 |
|
522 | 0 | if (fabs(actualOverlap - overlap) < OVERLAP_MARGIN) { |
523 | 0 | break; |
524 | 0 | } |
525 | 0 | |
526 | 0 | // We started from upper bound, no need to update range when j == 0. |
527 | 0 | if (j > 0) { |
528 | 0 | if (actualOverlap > overlap) { |
529 | 0 | lower = overlap; |
530 | 0 | } else { |
531 | 0 | upper = overlap; |
532 | 0 | } |
533 | 0 | } |
534 | 0 | } else { |
535 | 0 | // |j == 0 && count != targetCount| means that |targetCount = count + 1|, |
536 | 0 | // and we started from upper bound, no need to update range when j == 0. |
537 | 0 | if (j > 0) { |
538 | 0 | if (count > targetCount) { |
539 | 0 | upper = overlap; |
540 | 0 | } else { |
541 | 0 | lower = overlap; |
542 | 0 | } |
543 | 0 | } |
544 | 0 | } |
545 | 0 |
|
546 | 0 | overlap = (upper + lower) / 2.0f; |
547 | 0 | } |
548 | 0 |
|
549 | 0 | if (DottedCornerCache.Count() > DottedCornerCacheSize) { |
550 | 0 | DottedCornerCache.Clear(); |
551 | 0 | } |
552 | 0 | DottedCornerCache.Put(key, BestOverlap(mBestOverlap, mCount)); |
553 | 0 | } |
554 | | |
555 | | bool |
556 | | DottedCornerFinder::GetCountAndLastOverlap(Float aOverlap, |
557 | | size_t* aCount, |
558 | | Float* aActualOverlap) |
559 | 0 | { |
560 | 0 | // Return the number of circles and the last circles' overlap for the |
561 | 0 | // given overlap. |
562 | 0 |
|
563 | 0 | Reset(); |
564 | 0 |
|
565 | 0 | const Float T_MARGIN = 0.001f; |
566 | 0 | const Float DIST_MARGIN = 0.1f; |
567 | 0 | const Float DIST_MARGIN_SQUARE = Square(DIST_MARGIN); |
568 | 0 | for (size_t i = 0; i < mMaxCount; i++) { |
569 | 0 | Float actualOverlap = FindNext(aOverlap); |
570 | 0 | if (mLastT >= 1.0f - T_MARGIN || |
571 | 0 | (mLastC - mCn).LengthSquare() < DIST_MARGIN_SQUARE) { |
572 | 0 | *aCount = i + 1; |
573 | 0 | *aActualOverlap = actualOverlap; |
574 | 0 | return true; |
575 | 0 | } |
576 | 0 | } |
577 | 0 |
|
578 | 0 | return false; |
579 | 0 | } |
580 | | |
581 | | } // namespace mozilla |