80 public DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) {
81 return tracerCPDetector.init(out);
82 }
83
84 public DPathConsumer2D traceFiller(DPathConsumer2D out) {
85 return tracerFiller.init(out);
86 }
87
88 public DPathConsumer2D traceStroker(DPathConsumer2D out) {
89 return tracerStroker.init(out);
90 }
91
92 public DPathConsumer2D traceDasher(DPathConsumer2D out) {
93 return tracerDasher.init(out);
94 }
95
96 public DPathConsumer2D detectClosedPath(DPathConsumer2D out) {
97 return cpDetector.init(out);
98 }
99
100 public DPathConsumer2D pathClipper(DPathConsumer2D out,
101 final double rdrOffX,
102 final double rdrOffY)
103 {
104 return pathClipper.init(out, rdrOffX, rdrOffY);
105 }
106
107 public DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out,
108 BaseTransform at,
109 final double rdrOffX,
110 final double rdrOffY)
111 {
112 if (at == null) {
113 return out;
114 }
115 final double mxx = at.getMxx();
116 final double mxy = at.getMxy();
117 final double myx = at.getMyx();
118 final double myy = at.getMyy();
119
120 if (mxy == 0.0d && myx == 0.0d) {
121 if (mxx == 1.0d && myy == 1.0d) {
122 return out;
123 } else {
124 // Scale only
125 if (rdrCtx.doClip) {
126 // adjust clip rectangle (ymin, ymax, xmin, xmax):
127 adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
128 adjustClipScale(rdrCtx.clipRect, mxx, myy);
129 }
130 return dt_DeltaScaleFilter.init(out, mxx, myy);
131 }
132 } else {
133 if (rdrCtx.doClip) {
134 // adjust clip rectangle (ymin, ymax, xmin, xmax):
135 adjustClipOffset(rdrCtx.clipRect, rdrOffX, rdrOffY);
136 adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
137 }
138 return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
139 }
140 }
141
142 private static void adjustClipOffset(final double[] clipRect,
143 final double rdrOffX,
144 final double rdrOffY)
145 {
146 clipRect[0] += rdrOffY;
147 clipRect[1] += rdrOffY;
148 clipRect[2] += rdrOffX;
149 clipRect[3] += rdrOffX;
150 }
151
152 private static void adjustClipScale(final double[] clipRect,
153 final double mxx, final double myy)
154 {
155 // Adjust the clipping rectangle (iv_DeltaScaleFilter):
156 clipRect[0] /= myy;
157 clipRect[1] /= myy;
158 clipRect[2] /= mxx;
159 clipRect[3] /= mxx;
160 }
161
162 private static void adjustClipInverseDelta(final double[] clipRect,
163 final double mxx, final double mxy,
164 final double myx, final double myy)
165 {
166 // Adjust the clipping rectangle (iv_DeltaTransformFilter):
167 final double det = mxx * myy - mxy * myx;
168 final double imxx = myy / det;
169 final double imxy = -mxy / det;
170 final double imyx = -myx / det;
171 final double imyy = mxx / det;
172
173 double xmin, xmax, ymin, ymax;
174 double x, y;
175 // xmin, ymin:
176 x = clipRect[2] * imxx + clipRect[0] * imxy;
177 y = clipRect[2] * imyx + clipRect[0] * imyy;
178
179 xmin = xmax = x;
180 ymin = ymax = y;
181
182 // xmax, ymin:
187 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
188
189 // xmin, ymax:
190 x = clipRect[2] * imxx + clipRect[1] * imxy;
191 y = clipRect[2] * imyx + clipRect[1] * imyy;
192
193 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
194 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
195
196 // xmax, ymax:
197 x = clipRect[3] * imxx + clipRect[1] * imxy;
198 y = clipRect[3] * imyx + clipRect[1] * imyy;
199
200 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
201 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
202
203 clipRect[0] = ymin;
204 clipRect[1] = ymax;
205 clipRect[2] = xmin;
206 clipRect[3] = xmax;
207 }
208
209 public DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out,
210 BaseTransform at)
211 {
212 if (at == null) {
213 return out;
214 }
215 double mxx = at.getMxx();
216 double mxy = at.getMxy();
217 double myx = at.getMyx();
218 double myy = at.getMyy();
219
220 if (mxy == 0.0d && myx == 0.0d) {
221 if (mxx == 1.0d && myy == 1.0d) {
222 return out;
223 } else {
224 return iv_DeltaScaleFilter.init(out, 1.0d/mxx, 1.0d/myy);
225 }
226 } else {
227 final double det = mxx * myy - mxy * myx;
228 return iv_DeltaTransformFilter.init(out,
229 myy / det,
230 -mxy / det,
231 -myx / det,
232 mxx / det);
233 }
234 }
235
236 static final class DeltaScaleFilter implements DPathConsumer2D {
237 private DPathConsumer2D out;
238 private double sx, sy;
239
240 DeltaScaleFilter() {}
241
242 DeltaScaleFilter init(DPathConsumer2D out,
243 double mxx, double myy)
244 {
499 private double cx0, cy0;
500
501 // The current point OUTSIDE
502 private double cox0, coy0;
503
504 private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER;
505 private final CurveClipSplitter curveSplitter;
506
507 PathClipFilter(final DRendererContext rdrCtx) {
508 this.clipRect = rdrCtx.clipRect;
509 this.curveSplitter = rdrCtx.curveClipSplitter;
510
511 this.stack = (rdrCtx.stats != null) ?
512 new IndexStack(rdrCtx,
513 rdrCtx.stats.stat_pcf_idxstack_indices,
514 rdrCtx.stats.hist_pcf_idxstack_indices,
515 rdrCtx.stats.stat_array_pcf_idxstack_indices)
516 : new IndexStack(rdrCtx);
517 }
518
519 PathClipFilter init(final DPathConsumer2D out,
520 final double rdrOffX,
521 final double rdrOffY)
522 {
523 this.out = out;
524
525 // add a small rounding error:
526 final double margin = 1e-3d;
527
528 final double[] _clipRect = this.clipRect;
529 // Adjust the clipping rectangle with the renderer offsets
530 _clipRect[0] -= margin - rdrOffY;
531 _clipRect[1] += margin + rdrOffY;
532 _clipRect[2] -= margin - rdrOffX;
533 _clipRect[3] += margin + rdrOffX;
534
535 if (MarlinConst.DO_CLIP_SUBDIVIDER) {
536 // adjust padded clip rectangle:
537 curveSplitter.init();
538 }
539
540 this.init_corners = true;
541 this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
542
543 return this; // fluent API
544 }
545
546 /**
547 * Disposes this instance:
548 * clean up before reusing this instance
549 */
550 void dispose() {
551 stack.dispose();
552 }
553
554 private void finishPath() {
832
833 if (outside) {
834 finish();
835 }
836 // clipping disabled:
837 out.quadTo(x1, y1, xe, ye);
838 this.cx0 = xe;
839 this.cy0 = ye;
840 }
841 }
842
843 static final class CurveClipSplitter {
844
845 static final double LEN_TH = MarlinProperties.getSubdividerMinLength();
846 static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0d);
847
848 private static final boolean TRACE = false;
849
850 private static final int MAX_N_CURVES = 3 * 4;
851
852 // clip rectangle (ymin, ymax, xmin, xmax):
853 final double[] clipRect;
854
855 // clip rectangle (ymin, ymax, xmin, xmax) including padding:
856 final double[] clipRectPad = new double[4];
857 private boolean init_clipRectPad = false;
858
859 // This is where the curve to be processed is put. We give it
860 // enough room to store all curves.
861 final double[] middle = new double[MAX_N_CURVES * 8 + 2];
862 // t values at subdivision points
863 private final double[] subdivTs = new double[MAX_N_CURVES];
864
865 // dirty curve
866 private final DCurve curve;
867
868 CurveClipSplitter(final DRendererContext rdrCtx) {
869 this.clipRect = rdrCtx.clipRect;
870 this.curve = rdrCtx.curve;
871 }
872
873 void init() {
874 this.init_clipRectPad = true;
875 }
876
877 private void initPaddedClip() {
878 // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
879 // adjust padded clip rectangle (ymin, ymax, xmin, xmax):
880 // add a rounding error (curve subdivision ~ 0.1px):
881 final double[] _clipRect = clipRect;
882 final double[] _clipRectPad = clipRectPad;
883
884 _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING;
885 _clipRectPad[1] = _clipRect[1] + CLIP_RECT_PADDING;
886 _clipRectPad[2] = _clipRect[2] - CLIP_RECT_PADDING;
887 _clipRectPad[3] = _clipRect[3] + CLIP_RECT_PADDING;
888
889 if (TRACE) {
890 MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] "
891 + "Y ["+ _clipRectPad[0] + " .. " + _clipRectPad[1] +"]");
892 }
893 }
894
895 boolean splitLine(final double x0, final double y0,
896 final double x1, final double y1,
897 final int outCodeOR,
898 final DPathConsumer2D out)
899 {
900 if (TRACE) {
901 MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")");
902 }
903
904 if (DO_CHECK_LENGTH && DHelpers.fastLineLen(x0, y0, x1, y1) <= LEN_TH) {
905 return false;
906 }
907
908 final double[] mid = middle;
909 mid[0] = x0; mid[1] = y0;
910 mid[2] = x1; mid[3] = y1;
911
912 return subdivideAtIntersections(4, outCodeOR, out);
913 }
914
915 boolean splitQuad(final double x0, final double y0,
916 final double x1, final double y1,
917 final double x2, final double y2,
918 final int outCodeOR,
919 final DPathConsumer2D out)
920 {
921 if (TRACE) {
922 MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")");
923 }
924
925 if (DO_CHECK_LENGTH && DHelpers.fastQuadLen(x0, y0, x1, y1, x2, y2) <= LEN_TH) {
926 return false;
927 }
928
929 final double[] mid = middle;
930 mid[0] = x0; mid[1] = y0;
931 mid[2] = x1; mid[3] = y1;
932 mid[4] = x2; mid[5] = y2;
933
934 return subdivideAtIntersections(6, outCodeOR, out);
935 }
936
937 boolean splitCurve(final double x0, final double y0,
938 final double x1, final double y1,
939 final double x2, final double y2,
940 final double x3, final double y3,
941 final int outCodeOR,
942 final DPathConsumer2D out)
943 {
944 if (TRACE) {
945 MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")");
946 }
947
948 if (DO_CHECK_LENGTH && DHelpers.fastCurvelen(x0, y0, x1, y1, x2, y2, x3, y3) <= LEN_TH) {
949 return false;
950 }
951
952 final double[] mid = middle;
953 mid[0] = x0; mid[1] = y0;
954 mid[2] = x1; mid[3] = y1;
955 mid[4] = x2; mid[5] = y2;
956 mid[6] = x3; mid[7] = y3;
957
958 return subdivideAtIntersections(8, outCodeOR, out);
959 }
960
961 private boolean subdivideAtIntersections(final int type, final int outCodeOR,
962 final DPathConsumer2D out)
963 {
964 final double[] mid = middle;
965 final double[] subTs = subdivTs;
966
967 if (init_clipRectPad) {
968 init_clipRectPad = false;
969 initPaddedClip();
970 }
971
972 final int nSplits = DHelpers.findClipPoints(curve, mid, subTs, type,
973 outCodeOR, clipRectPad);
974
975 if (TRACE) {
976 MarlinUtils.logInfo("nSplits: "+ nSplits);
977 MarlinUtils.logInfo("subTs: " + Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits)));
978 }
979 if (nSplits == 0) {
980 // only curve support shortcut
981 return false;
982 }
983 double prevT = 0.0d;
984
985 for (int i = 0, off = 0; i < nSplits; i++, off += type) {
986 final double t = subTs[i];
987
988 DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
989 mid, off, mid, off, type);
990 prevT = t;
991 }
992
993 for (int i = 0, off = 0; i <= nSplits; i++, off += type) {
994 if (TRACE) {
995 MarlinUtils.logInfo("Part Curve " + Arrays.toString(Arrays.copyOfRange(mid, off, off + type)));
996 }
|
80 public DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) {
81 return tracerCPDetector.init(out);
82 }
83
84 public DPathConsumer2D traceFiller(DPathConsumer2D out) {
85 return tracerFiller.init(out);
86 }
87
88 public DPathConsumer2D traceStroker(DPathConsumer2D out) {
89 return tracerStroker.init(out);
90 }
91
92 public DPathConsumer2D traceDasher(DPathConsumer2D out) {
93 return tracerDasher.init(out);
94 }
95
96 public DPathConsumer2D detectClosedPath(DPathConsumer2D out) {
97 return cpDetector.init(out);
98 }
99
100 public DPathConsumer2D pathClipper(DPathConsumer2D out)
101 {
102 return pathClipper.init(out);
103 }
104
105 public DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out,
106 BaseTransform at)
107 {
108 if (at == null) {
109 return out;
110 }
111 final double mxx = at.getMxx();
112 final double mxy = at.getMxy();
113 final double myx = at.getMyx();
114 final double myy = at.getMyy();
115
116 if (mxy == 0.0d && myx == 0.0d) {
117 if (mxx == 1.0d && myy == 1.0d) {
118 return out;
119 } else {
120 // Scale only
121 if (rdrCtx.doClip) {
122 // adjust clip rectangle (ymin, ymax, xmin, xmax):
123 rdrCtx.clipInvScale = adjustClipScale(rdrCtx.clipRect,
124 mxx, myy);
125 }
126 return dt_DeltaScaleFilter.init(out, mxx, myy);
127 }
128 } else {
129 if (rdrCtx.doClip) {
130 // adjust clip rectangle (ymin, ymax, xmin, xmax):
131 rdrCtx.clipInvScale = adjustClipInverseDelta(rdrCtx.clipRect,
132 mxx, mxy, myx, myy);
133 }
134 return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
135 }
136 }
137
138 private static double adjustClipScale(final double[] clipRect,
139 final double mxx, final double myy)
140 {
141 // Adjust the clipping rectangle (iv_DeltaScaleFilter):
142 final double scaleY = 1.0d / myy;
143 clipRect[0] *= scaleY;
144 clipRect[1] *= scaleY;
145
146 if (clipRect[1] < clipRect[0]) {
147 double tmp = clipRect[0];
148 clipRect[0] = clipRect[1];
149 clipRect[1] = tmp;
150 }
151
152 final double scaleX = 1.0d / mxx;
153 clipRect[2] *= scaleX;
154 clipRect[3] *= scaleX;
155
156 if (clipRect[3] < clipRect[2]) {
157 double tmp = clipRect[2];
158 clipRect[2] = clipRect[3];
159 clipRect[3] = tmp;
160 }
161
162 if (MarlinConst.DO_LOG_CLIP) {
163 MarlinUtils.logInfo("clipRect (ClipScale): "
164 + Arrays.toString(clipRect));
165 }
166 return 0.5d * (Math.abs(scaleX) + Math.abs(scaleY));
167 }
168
169 private static double adjustClipInverseDelta(final double[] clipRect,
170 final double mxx, final double mxy,
171 final double myx, final double myy)
172 {
173 // Adjust the clipping rectangle (iv_DeltaTransformFilter):
174 final double det = mxx * myy - mxy * myx;
175 final double imxx = myy / det;
176 final double imxy = -mxy / det;
177 final double imyx = -myx / det;
178 final double imyy = mxx / det;
179
180 double xmin, xmax, ymin, ymax;
181 double x, y;
182 // xmin, ymin:
183 x = clipRect[2] * imxx + clipRect[0] * imxy;
184 y = clipRect[2] * imyx + clipRect[0] * imyy;
185
186 xmin = xmax = x;
187 ymin = ymax = y;
188
189 // xmax, ymin:
194 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
195
196 // xmin, ymax:
197 x = clipRect[2] * imxx + clipRect[1] * imxy;
198 y = clipRect[2] * imyx + clipRect[1] * imyy;
199
200 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
201 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
202
203 // xmax, ymax:
204 x = clipRect[3] * imxx + clipRect[1] * imxy;
205 y = clipRect[3] * imyx + clipRect[1] * imyy;
206
207 if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
208 if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
209
210 clipRect[0] = ymin;
211 clipRect[1] = ymax;
212 clipRect[2] = xmin;
213 clipRect[3] = xmax;
214
215 if (MarlinConst.DO_LOG_CLIP) {
216 MarlinUtils.logInfo("clipRect (ClipInverseDelta): "
217 + Arrays.toString(clipRect));
218 }
219
220 final double scaleX = Math.sqrt(imxx * imxx + imxy * imxy);
221 final double scaleY = Math.sqrt(imyx * imyx + imyy * imyy);
222
223 return 0.5d * (scaleX + scaleY);
224 }
225
226 public DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out,
227 BaseTransform at)
228 {
229 if (at == null) {
230 return out;
231 }
232 double mxx = at.getMxx();
233 double mxy = at.getMxy();
234 double myx = at.getMyx();
235 double myy = at.getMyy();
236
237 if (mxy == 0.0d && myx == 0.0d) {
238 if (mxx == 1.0d && myy == 1.0d) {
239 return out;
240 } else {
241 return iv_DeltaScaleFilter.init(out, 1.0d / mxx, 1.0d / myy);
242 }
243 } else {
244 final double det = mxx * myy - mxy * myx;
245 return iv_DeltaTransformFilter.init(out,
246 myy / det,
247 -mxy / det,
248 -myx / det,
249 mxx / det);
250 }
251 }
252
253 static final class DeltaScaleFilter implements DPathConsumer2D {
254 private DPathConsumer2D out;
255 private double sx, sy;
256
257 DeltaScaleFilter() {}
258
259 DeltaScaleFilter init(DPathConsumer2D out,
260 double mxx, double myy)
261 {
516 private double cx0, cy0;
517
518 // The current point OUTSIDE
519 private double cox0, coy0;
520
521 private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER;
522 private final CurveClipSplitter curveSplitter;
523
524 PathClipFilter(final DRendererContext rdrCtx) {
525 this.clipRect = rdrCtx.clipRect;
526 this.curveSplitter = rdrCtx.curveClipSplitter;
527
528 this.stack = (rdrCtx.stats != null) ?
529 new IndexStack(rdrCtx,
530 rdrCtx.stats.stat_pcf_idxstack_indices,
531 rdrCtx.stats.hist_pcf_idxstack_indices,
532 rdrCtx.stats.stat_array_pcf_idxstack_indices)
533 : new IndexStack(rdrCtx);
534 }
535
536 PathClipFilter init(final DPathConsumer2D out) {
537 this.out = out;
538
539 if (MarlinConst.DO_CLIP_SUBDIVIDER) {
540 // adjust padded clip rectangle:
541 curveSplitter.init();
542 }
543
544 this.init_corners = true;
545 this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
546
547 return this; // fluent API
548 }
549
550 /**
551 * Disposes this instance:
552 * clean up before reusing this instance
553 */
554 void dispose() {
555 stack.dispose();
556 }
557
558 private void finishPath() {
836
837 if (outside) {
838 finish();
839 }
840 // clipping disabled:
841 out.quadTo(x1, y1, xe, ye);
842 this.cx0 = xe;
843 this.cy0 = ye;
844 }
845 }
846
847 static final class CurveClipSplitter {
848
849 static final double LEN_TH = MarlinProperties.getSubdividerMinLength();
850 static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0d);
851
852 private static final boolean TRACE = false;
853
854 private static final int MAX_N_CURVES = 3 * 4;
855
856 private final DRendererContext rdrCtx;
857
858 // scaled length threshold:
859 private double minLength;
860
861 // clip rectangle (ymin, ymax, xmin, xmax):
862 final double[] clipRect;
863
864 // clip rectangle (ymin, ymax, xmin, xmax) including padding:
865 final double[] clipRectPad = new double[4];
866 private boolean init_clipRectPad = false;
867
868 // This is where the curve to be processed is put. We give it
869 // enough room to store all curves.
870 final double[] middle = new double[MAX_N_CURVES * 8 + 2];
871 // t values at subdivision points
872 private final double[] subdivTs = new double[MAX_N_CURVES];
873
874 // dirty curve
875 private final DCurve curve;
876
877 CurveClipSplitter(final DRendererContext rdrCtx) {
878 this.rdrCtx = rdrCtx;
879 this.clipRect = rdrCtx.clipRect;
880 this.curve = rdrCtx.curve;
881 }
882
883 void init() {
884 this.init_clipRectPad = true;
885
886 if (DO_CHECK_LENGTH) {
887 this.minLength = (this.rdrCtx.clipInvScale == 0.0d) ? LEN_TH
888 : (LEN_TH * this.rdrCtx.clipInvScale);
889
890 if (MarlinConst.DO_LOG_CLIP) {
891 MarlinUtils.logInfo("CurveClipSplitter.minLength = "
892 + minLength);
893 }
894 }
895 }
896
897 private void initPaddedClip() {
898 // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
899 // adjust padded clip rectangle (ymin, ymax, xmin, xmax):
900 // add a rounding error (curve subdivision ~ 0.1px):
901 final double[] _clipRect = clipRect;
902 final double[] _clipRectPad = clipRectPad;
903
904 _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING;
905 _clipRectPad[1] = _clipRect[1] + CLIP_RECT_PADDING;
906 _clipRectPad[2] = _clipRect[2] - CLIP_RECT_PADDING;
907 _clipRectPad[3] = _clipRect[3] + CLIP_RECT_PADDING;
908
909 if (TRACE) {
910 MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] "
911 + "Y [" + _clipRectPad[0] + " .. " + _clipRectPad[1] +"]");
912 }
913 }
914
915 boolean splitLine(final double x0, final double y0,
916 final double x1, final double y1,
917 final int outCodeOR,
918 final DPathConsumer2D out)
919 {
920 if (TRACE) {
921 MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")");
922 }
923
924 if (DO_CHECK_LENGTH && DHelpers.fastLineLen(x0, y0, x1, y1) <= minLength) {
925 return false;
926 }
927
928 final double[] mid = middle;
929 mid[0] = x0; mid[1] = y0;
930 mid[2] = x1; mid[3] = y1;
931
932 return subdivideAtIntersections(4, outCodeOR, out);
933 }
934
935 boolean splitQuad(final double x0, final double y0,
936 final double x1, final double y1,
937 final double x2, final double y2,
938 final int outCodeOR,
939 final DPathConsumer2D out)
940 {
941 if (TRACE) {
942 MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")");
943 }
944
945 if (DO_CHECK_LENGTH && DHelpers.fastQuadLen(x0, y0, x1, y1, x2, y2) <= minLength) {
946 return false;
947 }
948
949 final double[] mid = middle;
950 mid[0] = x0; mid[1] = y0;
951 mid[2] = x1; mid[3] = y1;
952 mid[4] = x2; mid[5] = y2;
953
954 return subdivideAtIntersections(6, outCodeOR, out);
955 }
956
957 boolean splitCurve(final double x0, final double y0,
958 final double x1, final double y1,
959 final double x2, final double y2,
960 final double x3, final double y3,
961 final int outCodeOR,
962 final DPathConsumer2D out)
963 {
964 if (TRACE) {
965 MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")");
966 }
967
968 if (DO_CHECK_LENGTH && DHelpers.fastCurvelen(x0, y0, x1, y1, x2, y2, x3, y3) <= minLength) {
969 return false;
970 }
971
972 final double[] mid = middle;
973 mid[0] = x0; mid[1] = y0;
974 mid[2] = x1; mid[3] = y1;
975 mid[4] = x2; mid[5] = y2;
976 mid[6] = x3; mid[7] = y3;
977
978 return subdivideAtIntersections(8, outCodeOR, out);
979 }
980
981 private boolean subdivideAtIntersections(final int type, final int outCodeOR,
982 final DPathConsumer2D out)
983 {
984 final double[] mid = middle;
985 final double[] subTs = subdivTs;
986
987 if (init_clipRectPad) {
988 init_clipRectPad = false;
989 initPaddedClip();
990 }
991
992 final int nSplits = DHelpers.findClipPoints(curve, mid, subTs, type,
993 outCodeOR, clipRectPad);
994
995 if (TRACE) {
996 MarlinUtils.logInfo("nSplits: " + nSplits);
997 MarlinUtils.logInfo("subTs: " + Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits)));
998 }
999 if (nSplits == 0) {
1000 // only curve support shortcut
1001 return false;
1002 }
1003 double prevT = 0.0d;
1004
1005 for (int i = 0, off = 0; i < nSplits; i++, off += type) {
1006 final double t = subTs[i];
1007
1008 DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
1009 mid, off, mid, off, type);
1010 prevT = t;
1011 }
1012
1013 for (int i = 0, off = 0; i <= nSplits; i++, off += type) {
1014 if (TRACE) {
1015 MarlinUtils.logInfo("Part Curve " + Arrays.toString(Arrays.copyOfRange(mid, off, off + type)));
1016 }
|