1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.hipparchus.geometry.euclidean.twod;
23
24 import org.hipparchus.exception.MathIllegalArgumentException;
25 import org.hipparchus.geometry.LocalizedGeometryFormats;
26 import org.hipparchus.geometry.Point;
27 import org.hipparchus.geometry.Vector;
28 import org.hipparchus.geometry.euclidean.oned.Euclidean1D;
29 import org.hipparchus.geometry.euclidean.oned.IntervalsSet;
30 import org.hipparchus.geometry.euclidean.oned.OrientedPoint;
31 import org.hipparchus.geometry.euclidean.oned.Vector1D;
32 import org.hipparchus.geometry.partitioning.Embedding;
33 import org.hipparchus.geometry.partitioning.Hyperplane;
34 import org.hipparchus.geometry.partitioning.RegionFactory;
35 import org.hipparchus.geometry.partitioning.SubHyperplane;
36 import org.hipparchus.geometry.partitioning.Transform;
37 import org.hipparchus.util.FastMath;
38 import org.hipparchus.util.MathArrays;
39 import org.hipparchus.util.MathUtils;
40 import org.hipparchus.util.SinCos;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public class Line implements Hyperplane<Euclidean2D>, Embedding<Euclidean2D, Euclidean1D> {
68
69
70 private double angle;
71
72
73 private double cos;
74
75
76 private double sin;
77
78
79 private double originOffset;
80
81
82 private final double tolerance;
83
84
85 private Line reverse;
86
87
88
89
90
91
92
93 public Line(final Vector2D p1, final Vector2D p2, final double tolerance) {
94 reset(p1, p2);
95 this.tolerance = tolerance;
96 }
97
98
99
100
101
102
103 public Line(final Vector2D p, final double angle, final double tolerance) {
104 reset(p, angle);
105 this.tolerance = tolerance;
106 }
107
108
109
110
111
112
113
114
115 private Line(final double angle, final double cos, final double sin,
116 final double originOffset, final double tolerance) {
117 this.angle = angle;
118 this.cos = cos;
119 this.sin = sin;
120 this.originOffset = originOffset;
121 this.tolerance = tolerance;
122 this.reverse = null;
123 }
124
125
126
127
128
129
130 public Line(final Line line) {
131 angle = MathUtils.normalizeAngle(line.angle, FastMath.PI);
132 cos = line.cos;
133 sin = line.sin;
134 originOffset = line.originOffset;
135 tolerance = line.tolerance;
136 reverse = null;
137 }
138
139
140 @Override
141 public Line copySelf() {
142 return new Line(this);
143 }
144
145
146
147
148
149
150 public void reset(final Vector2D p1, final Vector2D p2) {
151 unlinkReverse();
152 final double dx = p2.getX() - p1.getX();
153 final double dy = p2.getY() - p1.getY();
154 final double d = FastMath.hypot(dx, dy);
155 if (d == 0.0) {
156 angle = 0.0;
157 cos = 1.0;
158 sin = 0.0;
159 originOffset = p1.getY();
160 } else {
161 angle = FastMath.PI + FastMath.atan2(-dy, -dx);
162 cos = dx / d;
163 sin = dy / d;
164 originOffset = MathArrays.linearCombination(p2.getX(), p1.getY(), -p1.getX(), p2.getY()) / d;
165 }
166 }
167
168
169
170
171
172 public void reset(final Vector2D p, final double alpha) {
173 unlinkReverse();
174 final SinCos sinCos = FastMath.sinCos(alpha);
175 this.angle = MathUtils.normalizeAngle(alpha, FastMath.PI);
176 cos = sinCos.cos();
177 sin = sinCos.sin();
178 originOffset = MathArrays.linearCombination(cos, p.getY(), -sin, p.getX());
179 }
180
181
182
183 public void revertSelf() {
184 unlinkReverse();
185 if (angle < FastMath.PI) {
186 angle += FastMath.PI;
187 } else {
188 angle -= FastMath.PI;
189 }
190 cos = -cos;
191 sin = -sin;
192 originOffset = -originOffset;
193 }
194
195
196
197 private void unlinkReverse() {
198 if (reverse != null) {
199 reverse.reverse = null;
200 }
201 reverse = null;
202 }
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 public Line getReverse() {
220 if (reverse == null) {
221 reverse = new Line((angle < FastMath.PI) ? (angle + FastMath.PI) : (angle - FastMath.PI),
222 -cos, -sin, -originOffset, tolerance);
223 reverse.reverse = this;
224 }
225 return reverse;
226 }
227
228
229
230
231
232
233 public Vector1D toSubSpace(Vector<Euclidean2D, Vector2D> vector) {
234 return toSubSpace((Point<Euclidean2D>) vector);
235 }
236
237
238
239
240
241
242 public Vector2D toSpace(Vector<Euclidean1D, Vector1D> vector) {
243 return toSpace((Point<Euclidean1D>) vector);
244 }
245
246
247 @Override
248 public Vector1D toSubSpace(final Point<Euclidean2D> point) {
249 Vector2D p2 = (Vector2D) point;
250 return new Vector1D(MathArrays.linearCombination(cos, p2.getX(), sin, p2.getY()));
251 }
252
253
254 @Override
255 public Vector2D toSpace(final Point<Euclidean1D> point) {
256 final double abscissa = ((Vector1D) point).getX();
257 return new Vector2D(MathArrays.linearCombination(abscissa, cos, -originOffset, sin),
258 MathArrays.linearCombination(abscissa, sin, originOffset, cos));
259 }
260
261
262
263
264
265
266 public Vector2D intersection(final Line other) {
267 final double d = MathArrays.linearCombination(sin, other.cos, -other.sin, cos);
268 if (FastMath.abs(d) < tolerance) {
269 return null;
270 }
271 return new Vector2D(MathArrays.linearCombination(cos, other.originOffset, -other.cos, originOffset) / d,
272 MathArrays.linearCombination(sin, other.originOffset, -other.sin, originOffset) / d);
273 }
274
275
276
277 @Override
278 public Point<Euclidean2D> project(Point<Euclidean2D> point) {
279 return toSpace(toSubSpace(point));
280 }
281
282
283
284 @Override
285 public double getTolerance() {
286 return tolerance;
287 }
288
289
290 @Override
291 public SubLine wholeHyperplane() {
292 return new SubLine(this, new IntervalsSet(tolerance));
293 }
294
295
296 @Override
297 public SubLine emptyHyperplane() {
298 return new SubLine(this, new RegionFactory<Euclidean1D>().getComplement(new IntervalsSet(tolerance)));
299 }
300
301
302
303
304
305 @Override
306 public PolygonsSet wholeSpace() {
307 return new PolygonsSet(tolerance);
308 }
309
310
311
312
313
314
315
316
317
318
319
320 public double getOffset(final Line line) {
321 return originOffset +
322 (MathArrays.linearCombination(cos, line.cos, sin, line.sin) > 0 ? -line.originOffset : line.originOffset);
323 }
324
325
326
327
328
329 public double getOffset(Vector<Euclidean2D, Vector2D> vector) {
330 return getOffset((Point<Euclidean2D>) vector);
331 }
332
333
334 @Override
335 public double getOffset(final Point<Euclidean2D> point) {
336 Vector2D p2 = (Vector2D) point;
337 return MathArrays.linearCombination(sin, p2.getX(), -cos, p2.getY(), 1.0, originOffset);
338 }
339
340
341 @Override
342 public boolean sameOrientationAs(final Hyperplane<Euclidean2D> other) {
343 final Line otherL = (Line) other;
344 return MathArrays.linearCombination(sin, otherL.sin, cos, otherL.cos) >= 0.0;
345 }
346
347
348
349
350
351
352
353 public Vector2D getPointAt(final Vector1D abscissa, final double offset) {
354 final double x = abscissa.getX();
355 final double dOffset = offset - originOffset;
356 return new Vector2D(MathArrays.linearCombination(x, cos, dOffset, sin),
357 MathArrays.linearCombination(x, sin, -dOffset, cos));
358 }
359
360
361
362
363
364 public boolean contains(final Vector2D p) {
365 return FastMath.abs(getOffset(p)) < tolerance;
366 }
367
368
369
370
371
372
373
374
375
376 public double distance(final Vector2D p) {
377 return FastMath.abs(getOffset(p));
378 }
379
380
381
382
383
384
385 public boolean isParallelTo(final Line line) {
386 return FastMath.abs(MathArrays.linearCombination(sin, line.cos, -cos, line.sin)) < tolerance;
387 }
388
389
390
391
392 public void translateToPoint(final Vector2D p) {
393 originOffset = MathArrays.linearCombination(cos, p.getY(), -sin, p.getX());
394 }
395
396
397
398
399 public double getAngle() {
400 return MathUtils.normalizeAngle(angle, FastMath.PI);
401 }
402
403
404
405
406 public void setAngle(final double angle) {
407 unlinkReverse();
408 this.angle = MathUtils.normalizeAngle(angle, FastMath.PI);
409 final SinCos sinCos = FastMath.sinCos(this.angle);
410 cos = sinCos.cos();
411 sin = sinCos.sin();
412 }
413
414
415
416
417 public double getOriginOffset() {
418 return originOffset;
419 }
420
421
422
423
424 public void setOriginOffset(final double offset) {
425 unlinkReverse();
426 originOffset = offset;
427 }
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443 public static Transform<Euclidean2D, Euclidean1D> getTransform(final double cXX,
444 final double cYX,
445 final double cXY,
446 final double cYY,
447 final double cX1,
448 final double cY1)
449 throws MathIllegalArgumentException {
450 return new LineTransform(cXX, cYX, cXY, cYY, cX1, cY1);
451 }
452
453
454
455
456
457
458
459
460 private static class LineTransform implements Transform<Euclidean2D, Euclidean1D> {
461
462
463 private final double cXX;
464
465
466 private final double cYX;
467
468
469 private final double cXY;
470
471
472 private final double cYY;
473
474
475 private final double cX1;
476
477
478 private final double cY1;
479
480
481 private final double c1Y;
482
483
484 private final double c1X;
485
486
487 private final double c11;
488
489
490
491
492
493
494
495
496
497
498 LineTransform(final double cXX, final double cYX, final double cXY,
499 final double cYY, final double cX1, final double cY1)
500 throws MathIllegalArgumentException {
501
502 this.cXX = cXX;
503 this.cYX = cYX;
504 this.cXY = cXY;
505 this.cYY = cYY;
506 this.cX1 = cX1;
507 this.cY1 = cY1;
508
509 c1Y = MathArrays.linearCombination(cXY, cY1, -cYY, cX1);
510 c1X = MathArrays.linearCombination(cXX, cY1, -cYX, cX1);
511 c11 = MathArrays.linearCombination(cXX, cYY, -cYX, cXY);
512
513 if (FastMath.abs(c11) < 1.0e-20) {
514 throw new MathIllegalArgumentException(LocalizedGeometryFormats.NON_INVERTIBLE_TRANSFORM);
515 }
516
517 }
518
519
520 @Override
521 public Vector2D apply(final Point<Euclidean2D> point) {
522 final Vector2D p2D = (Vector2D) point;
523 final double x = p2D.getX();
524 final double y = p2D.getY();
525 return new Vector2D(MathArrays.linearCombination(cXX, x, cXY, y, cX1, 1),
526 MathArrays.linearCombination(cYX, x, cYY, y, cY1, 1));
527 }
528
529
530 @Override
531 public Line apply(final Hyperplane<Euclidean2D> hyperplane) {
532 final Line line = (Line) hyperplane;
533 final double rOffset = MathArrays.linearCombination(c1X, line.cos, c1Y, line.sin, c11, line.originOffset);
534 final double rCos = MathArrays.linearCombination(cXX, line.cos, cXY, line.sin);
535 final double rSin = MathArrays.linearCombination(cYX, line.cos, cYY, line.sin);
536 final double inv = 1.0 / FastMath.sqrt(rSin * rSin + rCos * rCos);
537 return new Line(FastMath.PI + FastMath.atan2(-rSin, -rCos),
538 inv * rCos, inv * rSin,
539 inv * rOffset, line.tolerance);
540 }
541
542
543 @Override
544 public SubHyperplane<Euclidean1D> apply(final SubHyperplane<Euclidean1D> sub,
545 final Hyperplane<Euclidean2D> original,
546 final Hyperplane<Euclidean2D> transformed) {
547 final OrientedPoint op = (OrientedPoint) sub.getHyperplane();
548 final Line originalLine = (Line) original;
549 final Line transformedLine = (Line) transformed;
550 final Vector1D newLoc =
551 transformedLine.toSubSpace(apply(originalLine.toSpace(op.getLocation())));
552 return new OrientedPoint(newLoc, op.isDirect(), originalLine.tolerance).wholeHyperplane();
553 }
554
555 }
556
557 }