45 /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */ |
45 /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */ |
46 static final int REC_LIMIT = 16; |
46 static final int REC_LIMIT = 16; |
47 static final double CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 initial |
47 static final double CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 initial |
48 static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT); |
48 static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT); |
49 |
49 |
|
50 static final double EPS = 1e-6d; |
|
51 |
50 // More than 24 bits of mantissa means we can no longer accurately |
52 // More than 24 bits of mantissa means we can no longer accurately |
51 // measure the number of times cycled through the dash array so we |
53 // measure the number of times cycled through the dash array so we |
52 // punt and override the phase to just be 0 past that point. |
54 // punt and override the phase to just be 0 past that point. |
53 static final double MAX_CYCLES = 16000000.0d; |
55 static final double MAX_CYCLES = 16000000.0d; |
54 |
56 |
267 } |
269 } |
268 } |
270 } |
269 |
271 |
270 private void emitSeg(double[] buf, int off, int type) { |
272 private void emitSeg(double[] buf, int off, int type) { |
271 switch (type) { |
273 switch (type) { |
|
274 case 4: |
|
275 out.lineTo(buf[off], buf[off + 1]); |
|
276 return; |
272 case 8: |
277 case 8: |
273 out.curveTo(buf[off ], buf[off + 1], |
278 out.curveTo(buf[off ], buf[off + 1], |
274 buf[off + 2], buf[off + 3], |
279 buf[off + 2], buf[off + 3], |
275 buf[off + 4], buf[off + 5]); |
280 buf[off + 4], buf[off + 5]); |
276 return; |
281 return; |
277 case 6: |
282 case 6: |
278 out.quadTo(buf[off ], buf[off + 1], |
283 out.quadTo(buf[off ], buf[off + 1], |
279 buf[off + 2], buf[off + 3]); |
284 buf[off + 2], buf[off + 3]); |
280 return; |
285 return; |
281 case 4: |
|
282 out.lineTo(buf[off], buf[off + 1]); |
|
283 return; |
|
284 default: |
286 default: |
285 } |
287 } |
286 } |
288 } |
287 |
289 |
288 private void emitFirstSegments() { |
290 private void emitFirstSegments() { |
359 if (orCode != 0) { |
361 if (orCode != 0) { |
360 final int sideCode = outcode0 & outcode1; |
362 final int sideCode = outcode0 & outcode1; |
361 |
363 |
362 // basic rejection criteria: |
364 // basic rejection criteria: |
363 if (sideCode == 0) { |
365 if (sideCode == 0) { |
364 // ovelap clip: |
366 // overlap clip: |
365 if (subdivide) { |
367 if (subdivide) { |
366 // avoid reentrance |
368 // avoid reentrance |
367 subdivide = false; |
369 subdivide = false; |
368 // subdivide curve => callback with subdivided parts: |
370 // subdivide curve => callback with subdivided parts: |
369 boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1, |
371 boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1, |
414 |
416 |
415 int _idx = idx; |
417 int _idx = idx; |
416 boolean _dashOn = dashOn; |
418 boolean _dashOn = dashOn; |
417 double _phase = phase; |
419 double _phase = phase; |
418 |
420 |
419 double leftInThisDashSegment, d; |
421 double leftInThisDashSegment, rem; |
420 |
422 |
421 while (true) { |
423 while (true) { |
422 d = _dash[_idx]; |
424 leftInThisDashSegment = _dash[_idx] - _phase; |
423 leftInThisDashSegment = d - _phase; |
425 rem = len - leftInThisDashSegment; |
424 |
426 |
425 if (len <= leftInThisDashSegment) { |
427 if (rem <= EPS) { |
426 _curCurvepts[0] = x1; |
428 _curCurvepts[0] = x1; |
427 _curCurvepts[1] = y1; |
429 _curCurvepts[1] = y1; |
428 |
430 |
429 goTo(_curCurvepts, 0, 4, _dashOn); |
431 goTo(_curCurvepts, 0, 4, _dashOn); |
430 |
432 |
431 // Advance phase within current dash segment |
433 // Advance phase within current dash segment |
432 _phase += len; |
434 _phase += len; |
433 |
435 |
434 // TODO: compare double values using epsilon: |
436 // compare values using epsilon: |
435 if (len == leftInThisDashSegment) { |
437 if (Math.abs(rem) <= EPS) { |
436 _phase = 0.0d; |
438 _phase = 0.0d; |
437 _idx = (_idx + 1) % _dashLen; |
439 _idx = (_idx + 1) % _dashLen; |
438 _dashOn = !_dashOn; |
440 _dashOn = !_dashOn; |
439 } |
441 } |
440 break; |
442 break; |
441 } |
443 } |
442 |
444 |
443 if (_phase == 0.0d) { |
445 _curCurvepts[0] = cx0 + leftInThisDashSegment * cx; |
444 _curCurvepts[0] = cx0 + d * cx; |
446 _curCurvepts[1] = cy0 + leftInThisDashSegment * cy; |
445 _curCurvepts[1] = cy0 + d * cy; |
|
446 } else { |
|
447 _curCurvepts[0] = cx0 + leftInThisDashSegment * cx; |
|
448 _curCurvepts[1] = cy0 + leftInThisDashSegment * cy; |
|
449 } |
|
450 |
447 |
451 goTo(_curCurvepts, 0, 4, _dashOn); |
448 goTo(_curCurvepts, 0, 4, _dashOn); |
452 |
449 |
453 len -= leftInThisDashSegment; |
450 len = rem; |
454 // Advance to next dash segment |
451 // Advance to next dash segment |
455 _idx = (_idx + 1) % _dashLen; |
452 _idx = (_idx + 1) % _dashLen; |
456 _dashOn = !_dashOn; |
453 _dashOn = !_dashOn; |
457 _phase = 0.0d; |
454 _phase = 0.0d; |
458 } |
455 } |
504 final long iterations = fullcycles * _dashLen; |
501 final long iterations = fullcycles * _dashLen; |
505 _idx = (int) (iterations + _idx) % _dashLen; |
502 _idx = (int) (iterations + _idx) % _dashLen; |
506 _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L; |
503 _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L; |
507 } |
504 } |
508 |
505 |
509 double leftInThisDashSegment, d; |
506 double leftInThisDashSegment, rem; |
510 |
507 |
511 while (true) { |
508 while (true) { |
512 d = _dash[_idx]; |
509 leftInThisDashSegment = _dash[_idx] - _phase; |
513 leftInThisDashSegment = d - _phase; |
510 rem = len - leftInThisDashSegment; |
514 |
511 |
515 if (len <= leftInThisDashSegment) { |
512 if (rem <= EPS) { |
516 // Advance phase within current dash segment |
513 // Advance phase within current dash segment |
517 _phase += len; |
514 _phase += len; |
518 |
515 |
519 // TODO: compare double values using epsilon: |
516 // compare values using epsilon: |
520 if (len == leftInThisDashSegment) { |
517 if (Math.abs(rem) <= EPS) { |
521 _phase = 0.0d; |
518 _phase = 0.0d; |
522 _idx = (_idx + 1) % _dashLen; |
519 _idx = (_idx + 1) % _dashLen; |
523 _dashOn = !_dashOn; |
520 _dashOn = !_dashOn; |
524 } |
521 } |
525 break; |
522 break; |
526 } |
523 } |
527 |
524 |
528 len -= leftInThisDashSegment; |
525 len = rem; |
529 // Advance to next dash segment |
526 // Advance to next dash segment |
530 _idx = (_idx + 1) % _dashLen; |
527 _idx = (_idx + 1) % _dashLen; |
531 _dashOn = !_dashOn; |
528 _dashOn = !_dashOn; |
532 _phase = 0.0d; |
529 _phase = 0.0d; |
533 } |
530 } |
577 } |
574 } |
578 |
575 |
579 goTo(_curCurvepts, curCurveoff + 2, type, _dashOn); |
576 goTo(_curCurvepts, curCurveoff + 2, type, _dashOn); |
580 |
577 |
581 _phase += _li.lastSegLen(); |
578 _phase += _li.lastSegLen(); |
582 if (_phase >= _dash[_idx]) { |
579 |
|
580 // compare values using epsilon: |
|
581 if (_phase + EPS >= _dash[_idx]) { |
583 _phase = 0.0d; |
582 _phase = 0.0d; |
584 _idx = (_idx + 1) % _dashLen; |
583 _idx = (_idx + 1) % _dashLen; |
585 _dashOn = !_dashOn; |
584 _dashOn = !_dashOn; |
586 } |
585 } |
587 // Save local state: |
586 // Save local state: |
936 if (orCode != 0) { |
935 if (orCode != 0) { |
937 final int sideCode = outcode0 & outcode1 & outcode2 & outcode3; |
936 final int sideCode = outcode0 & outcode1 & outcode2 & outcode3; |
938 |
937 |
939 // basic rejection criteria: |
938 // basic rejection criteria: |
940 if (sideCode == 0) { |
939 if (sideCode == 0) { |
941 // ovelap clip: |
940 // overlap clip: |
942 if (subdivide) { |
941 if (subdivide) { |
943 // avoid reentrance |
942 // avoid reentrance |
944 subdivide = false; |
943 subdivide = false; |
945 // subdivide curve => callback with subdivided parts: |
944 // subdivide curve => callback with subdivided parts: |
946 boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, x2, y2, x3, y3, |
945 boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, x2, y2, x3, y3, |
1022 if (orCode != 0) { |
1021 if (orCode != 0) { |
1023 final int sideCode = outcode0 & outcode1 & outcode2; |
1022 final int sideCode = outcode0 & outcode1 & outcode2; |
1024 |
1023 |
1025 // basic rejection criteria: |
1024 // basic rejection criteria: |
1026 if (sideCode == 0) { |
1025 if (sideCode == 0) { |
1027 // ovelap clip: |
1026 // overlap clip: |
1028 if (subdivide) { |
1027 if (subdivide) { |
1029 // avoid reentrance |
1028 // avoid reentrance |
1030 subdivide = false; |
1029 subdivide = false; |
1031 // subdivide curve => call lineTo() with subdivided curves: |
1030 // subdivide curve => call lineTo() with subdivided curves: |
1032 boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1, |
1031 boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1, |