1
2
3
4
5 package math.geom2d.curve;
6
7 import java.util.ArrayList;
8 import java.util.Iterator;
9 import java.util.SortedSet;
10 import java.util.TreeSet;
11
12 import math.geom2d.Box2D;
13 import math.geom2d.Point2D;
14 import math.geom2d.Shape2D;
15 import math.geom2d.Vector2D;
16 import math.geom2d.line.StraightLine2D;
17 import math.geom2d.line.LinearShape2D;
18
19
20
21
22
23
24 public abstract class Curve2DUtils {
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 public final static double toUnitSegment(double t, double t0, double t1) {
40 if (t<=t0)
41 return 0;
42 if (t>=t1)
43 return 1;
44
45 if (t0==Double.NEGATIVE_INFINITY&&t1==Double.POSITIVE_INFINITY)
46 return Math.atan(t)/Math.PI+.5;
47
48 if (t0==Double.NEGATIVE_INFINITY)
49 return Math.atan(t-t1)*2/Math.PI+1;
50
51 if (t1==Double.POSITIVE_INFINITY)
52 return Math.atan(t-t0)*2/Math.PI;
53
54
55 return (t-t0)/(t1-t0);
56 }
57
58
59
60
61
62
63
64
65
66
67 public final static double fromUnitSegment(double t, double t0, double t1) {
68 if (t<=0)
69 return t0;
70 if (t>=1)
71 return t1;
72
73 if (t0==Double.NEGATIVE_INFINITY&&t1==Double.POSITIVE_INFINITY)
74 return Math.tan((t-.5)*Math.PI);
75
76 if (t0==Double.NEGATIVE_INFINITY)
77 return Math.tan((t-1)*Math.PI/2)+t1;
78
79 if (t1==Double.POSITIVE_INFINITY)
80 return Math.tan(t*Math.PI/2)+t0;
81
82
83 return t*(t1-t0)+t0;
84 }
85
86
87
88
89
90
91
92 public final static CurveSet2D<? extends Curve2D>
93 clipCurve(Curve2D curve, Box2D box) {
94
95
96 if (curve instanceof ContinuousCurve2D)
97 return new CurveArray2D<Curve2D>(Curve2DUtils.clipContinuousCurve(
98 (ContinuousCurve2D) curve, box).getCurves());
99
100
101 if (curve instanceof CurveSet2D)
102 return Curve2DUtils.clipCurveSet((CurveSet2D<?>) curve, box);
103
104
105 System.err.println("Unknown curve class in Box2D.clipCurve()");
106 return new CurveArray2D<Curve2D>();
107 }
108
109
110
111
112 public final static CurveSet2D<? extends Curve2D> clipCurveSet(
113 CurveSet2D<?> curveSet, Box2D box) {
114
115 CurveArray2D<Curve2D> result = new CurveArray2D<Curve2D>();
116 CurveSet2D<?> clipped;
117
118
119 for (Curve2D curve : curveSet) {
120 clipped = Curve2DUtils.clipCurve(curve, box);
121 for (Curve2D clippedPart : clipped)
122 result.addCurve(clippedPart);
123 }
124
125
126 return result;
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 public final static CurveSet2D<ContinuousCurve2D> clipContinuousCurve(
152 ContinuousCurve2D curve, Box2D box) {
153
154
155 CurveArray2D<ContinuousCurve2D> res =
156 new CurveArray2D<ContinuousCurve2D>();
157
158
159
160
161 ArrayList<Point2D> points = new ArrayList<Point2D>();
162
163
164 for (LinearShape2D edge : box.getEdges())
165 points.addAll(curve.getIntersections(edge));
166
167
168
169 SortedSet<Double> set = new TreeSet<Double>();
170 for (Point2D p : points)
171 set.add(new Double(curve.getPosition(p)));
172
173
174 Iterator<Double> iter = set.iterator();
175
176
177
178
179 int nInter = set.size();
180 double[] positions = new double[nInter+2];
181 double[] between = new double[nInter+1];
182
183
184 positions[0] = curve.getT0();
185 for (int i = 0; i<nInter; i++)
186 positions[i+1] = iter.next();
187 positions[nInter+1] = curve.getT1();
188
189
190 for (int i = 0; i<nInter+1; i++)
191 between[i] = choosePosition(positions[i], positions[i+1]);
192
193
194 ArrayList<Double> toRemove = new ArrayList<Double>();
195
196
197
198 for (int i = 0; i<nInter; i++) {
199 Point2D p1 = curve.getPoint(between[i]);
200 Point2D p2 = curve.getPoint(between[i+1]);
201 boolean b1 = box.contains(p1);
202 boolean b2 = box.contains(p2);
203 if (b1==b2)
204 toRemove.add(positions[i+1]);
205 }
206
207
208 set.removeAll(toRemove);
209
210
211 iter = set.iterator();
212
213
214
215
216
217 if (set.size()==0) {
218
219 Point2D point;
220 if (curve.isBounded()) {
221 point = curve.getFirstPoint();
222 } else {
223 double pos = choosePosition(curve.getT0(), curve.getT1());
224 point = curve.getPoint(pos);
225 }
226
227
228 if (box.contains(point))
229 res.addCurve(curve);
230 return res;
231 }
232
233
234
235
236 boolean inside = false;
237 boolean touch = false;
238
239
240 double t0 = curve.getT0();
241 if (isLeftInfinite(curve)) {
242
243 double pos = choosePosition(t0, set.iterator().next());
244 inside = box.contains(curve.getPoint(pos));
245 } else {
246
247 Point2D point = curve.getFirstPoint();
248 inside = box.contains(point);
249
250
251
252 if (box.getBoundary().contains(point)) {
253 touch = true;
254
255 double pos = choosePosition(t0, iter.next());
256 while (Math.abs(pos-t0)<Shape2D.ACCURACY&&iter.hasNext())
257 pos = choosePosition(t0, iter.next());
258 if (Math.abs(pos-t0)<Shape2D.ACCURACY)
259 pos = choosePosition(t0, curve.getT1());
260 point = curve.getPoint(pos);
261
262
263 set.remove(t0);
264
265
266
267 if (box.contains(point)) {
268 pos = set.iterator().next();
269 res.addCurve((ContinuousCurve2D)curve.getSubCurve(t0, pos));
270 set.remove(pos);
271 }
272
273
274 iter = set.iterator();
275
276 inside = false;
277 }
278 }
279
280
281 double pos0 = Double.NaN;
282 if (inside&&!touch)
283 if (curve.isClosed())
284 pos0 = iter.next();
285 else
286 res.addCurve((ContinuousCurve2D)curve.getSubCurve(curve.getT0(), iter.next()));
287
288
289
290 double pos1, pos2;
291 while (iter.hasNext()) {
292 pos1 = iter.next().doubleValue();
293 if (iter.hasNext())
294 pos2 = iter.next().doubleValue();
295 else
296 pos2 = curve.isClosed()&&!touch ? pos0 : curve.getT1();
297 res.addCurve((ContinuousCurve2D)curve.getSubCurve(pos1, pos2));
298 }
299
300 return res;
301 }
302
303
304
305
306
307 public final static CurveSet2D<SmoothCurve2D> clipSmoothCurve(
308 SmoothCurve2D curve, Box2D box) {
309 CurveArray2D<SmoothCurve2D> result = new CurveArray2D<SmoothCurve2D>();
310 for (ContinuousCurve2D cont : Curve2DUtils.clipContinuousCurve(curve,
311 box))
312 if (cont instanceof SmoothCurve2D)
313 result.addCurve((SmoothCurve2D) cont);
314
315 return result;
316 }
317
318
319
320
321
322 public final static CurveSet2D<SmoothCurve2D> clipSmoothCurve(
323 SmoothCurve2D curve, StraightLine2D line) {
324
325
326 ArrayList<Point2D> list = new ArrayList<Point2D>();
327 list.addAll(curve.getIntersections(line));
328
329
330
331
332 SortedSet<java.lang.Double> set = new TreeSet<java.lang.Double>();
333 double position;
334 Vector2D vector = line.getVector();
335 for (Point2D point : list) {
336
337
338 position = curve.project(point);
339
340
341 Vector2D tangent = curve.getTangent(position);
342 if (Vector2D.isColinear(tangent, vector)) {
343
344 double curv = curve.getCurvature(position);
345 if (Math.abs(curv)>Shape2D.ACCURACY)
346 continue;
347 }
348 set.add(new java.lang.Double(position));
349 }
350
351
352 CurveArray2D<SmoothCurve2D> res = new CurveArray2D<SmoothCurve2D>();
353
354
355 Point2D point1;
356 if (Double.isInfinite(curve.getT0()))
357 point1 = curve.getPoint(-1000);
358 else
359 point1 = curve.getFirstPoint();
360
361
362 double pos1, pos2;
363 Iterator<java.lang.Double> iter = set.iterator();
364
365
366
367 if (!iter.hasNext()) {
368
369
370 double t0 = curve.getT0();
371 if (t0==Double.NEGATIVE_INFINITY)
372 t0 = -100;
373 while (line.contains(point1)) {
374 double t1 = curve.getT1();
375 if (t1==Double.POSITIVE_INFINITY)
376 t1 = +100;
377 t0 = (t0+t1)/2;
378 point1 = curve.getPoint(t0);
379 }
380 if (line.getSignedDistance(point1)<0)
381 res.addCurve(curve);
382 return res;
383 }
384
385
386 if (line.getSignedDistance(point1)<0&&!line.contains(point1)) {
387 pos1 = iter.next().doubleValue();
388 res.addCurve((SmoothCurve2D)curve.getSubCurve(curve.getT0(), pos1));
389 }
390
391
392 while (iter.hasNext()) {
393 pos1 = iter.next().doubleValue();
394 if (iter.hasNext())
395 pos2 = iter.next().doubleValue();
396 else
397 pos2 = curve.getT1();
398 res.addCurve((SmoothCurve2D)curve.getSubCurve(pos1, pos2));
399 }
400
401 return res;
402 }
403
404 public final static int findNextCurveIndex(double[] positions, double pos) {
405 int ind = -1;
406 double posMin = java.lang.Double.MAX_VALUE;
407 for (int i = 0; i<positions.length; i++) {
408
409 if (java.lang.Double.isNaN(positions[i]))
410 continue;
411
412 if (positions[i]-pos<Shape2D.ACCURACY)
413 continue;
414
415
416 if (positions[i]<posMin) {
417 ind = i;
418 posMin = positions[i];
419 }
420 }
421
422 if (ind!=-1)
423 return ind;
424
425
426
427 for (int i = 0; i<positions.length; i++) {
428 if (java.lang.Double.isNaN(positions[i]))
429 continue;
430 if (positions[i]-posMin<Shape2D.ACCURACY) {
431 ind = i;
432 posMin = positions[i];
433 }
434 }
435 return ind;
436 }
437
438
439
440
441
442
443
444
445
446 public final static double choosePosition(double t0, double t1) {
447 if (Double.isInfinite(t0)) {
448 if (Double.isInfinite(t1))
449 return 0;
450 return t1-10;
451 }
452
453 if (Double.isInfinite(t1))
454 return t0+10;
455
456 return (t0+t1)/2;
457 }
458
459 public final static boolean isLeftInfinite(Curve2D curve) {
460
461 if(curve.isBounded()) return false;
462
463
464 ContinuousCurve2D cont = curve.getContinuousCurves().iterator().next();
465 SmoothCurve2D smooth = cont.getSmoothPieces().iterator().next();
466
467
468 return Double.isInfinite(smooth.getT0());
469 }
470
471 public final static boolean isRightInfinite(Curve2D curve) {
472
473 if(curve.isBounded()) return false;
474
475
476 SmoothCurve2D lastCurve = null;
477 for(ContinuousCurve2D cont : curve.getContinuousCurves())
478 for(SmoothCurve2D smooth : cont.getSmoothPieces())
479 lastCurve = smooth;
480
481
482 return Double.isInfinite(lastCurve.getT1());
483 }
484 }