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.euclidean.oned.Euclidean1D;
27 import org.hipparchus.geometry.euclidean.oned.IntervalsSet;
28 import org.hipparchus.geometry.euclidean.oned.OrientedPoint;
29 import org.hipparchus.geometry.euclidean.oned.SubOrientedPoint;
30 import org.hipparchus.geometry.euclidean.oned.Vector1D;
31 import org.hipparchus.geometry.partitioning.Embedding;
32 import org.hipparchus.geometry.partitioning.Hyperplane;
33 import org.hipparchus.geometry.partitioning.RegionFactory;
34 import org.hipparchus.geometry.partitioning.Transform;
35 import org.hipparchus.util.FastMath;
36 import org.hipparchus.util.MathArrays;
37 import org.hipparchus.util.MathUtils;
38 import org.hipparchus.util.SinCos;
39
40
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 public class Line
66 implements Hyperplane<Euclidean2D, Vector2D, Line, SubLine>,
67 Embedding<Euclidean2D, Vector2D, Euclidean1D, Vector1D> {
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 @Override
230 public Vector1D toSubSpace(final Vector2D point) {
231 return new Vector1D(MathArrays.linearCombination(cos, point.getX(), sin, point.getY()));
232 }
233
234
235 @Override
236 public Vector2D toSpace(final Vector1D point) {
237 final double abscissa = point.getX();
238 return new Vector2D(MathArrays.linearCombination(abscissa, cos, -originOffset, sin),
239 MathArrays.linearCombination(abscissa, sin, originOffset, cos));
240 }
241
242
243
244
245
246
247 public Vector2D intersection(final Line other) {
248 final double d = MathArrays.linearCombination(sin, other.cos, -other.sin, cos);
249 if (FastMath.abs(d) < tolerance) {
250 return null;
251 }
252 return new Vector2D(MathArrays.linearCombination(cos, other.originOffset, -other.cos, originOffset) / d,
253 MathArrays.linearCombination(sin, other.originOffset, -other.sin, originOffset) / d);
254 }
255
256
257
258 @Override
259 public Vector2D project(Vector2D point) {
260 return toSpace(toSubSpace(point));
261 }
262
263
264
265 @Override
266 public double getTolerance() {
267 return tolerance;
268 }
269
270
271 @Override
272 public SubLine wholeHyperplane() {
273 return new SubLine(this, new IntervalsSet(tolerance));
274 }
275
276
277 @Override
278 public SubLine emptyHyperplane() {
279 final RegionFactory<Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> factory = new RegionFactory<>();
280 return new SubLine(this, factory.getComplement(new IntervalsSet(tolerance)));
281 }
282
283
284
285
286
287 @Override
288 public PolygonsSet wholeSpace() {
289 return new PolygonsSet(tolerance);
290 }
291
292
293
294
295
296
297
298
299
300
301
302 public double getOffset(final Line line) {
303 return originOffset +
304 (MathArrays.linearCombination(cos, line.cos, sin, line.sin) > 0 ? -line.originOffset : line.originOffset);
305 }
306
307
308 @Override
309 public double getOffset(final Vector2D point) {
310 return MathArrays.linearCombination(sin, point.getX(), -cos, point.getY(), 1.0, originOffset);
311 }
312
313
314 @Override
315 public Vector2D moveToOffset(final Vector2D point, final double offset) {
316 final double delta = offset - getOffset(point);
317 return new Vector2D(point.getX() + delta * sin, point.getY() - delta * cos);
318 }
319
320
321 @Override
322 public Vector2D arbitraryPoint() {
323 return getPointAt(Vector1D.ZERO, 0);
324 }
325
326
327 @Override
328 public boolean sameOrientationAs(final Line other) {
329 return MathArrays.linearCombination(sin, other.sin, cos, other.cos) >= 0.0;
330 }
331
332
333
334
335
336
337
338 public Vector2D getPointAt(final Vector1D abscissa, final double offset) {
339 final double x = abscissa.getX();
340 final double dOffset = offset - originOffset;
341 return new Vector2D(MathArrays.linearCombination(x, cos, dOffset, sin),
342 MathArrays.linearCombination(x, sin, -dOffset, cos));
343 }
344
345
346
347
348
349 public boolean contains(final Vector2D p) {
350 return FastMath.abs(getOffset(p)) < tolerance;
351 }
352
353
354
355
356
357
358
359
360
361 public double distance(final Vector2D p) {
362 return FastMath.abs(getOffset(p));
363 }
364
365
366
367
368
369
370 public boolean isParallelTo(final Line line) {
371 return FastMath.abs(MathArrays.linearCombination(sin, line.cos, -cos, line.sin)) < tolerance;
372 }
373
374
375
376
377 public void translateToPoint(final Vector2D p) {
378 originOffset = MathArrays.linearCombination(cos, p.getY(), -sin, p.getX());
379 }
380
381
382
383
384 public double getAngle() {
385 return MathUtils.normalizeAngle(angle, FastMath.PI);
386 }
387
388
389
390
391 public void setAngle(final double angle) {
392 unlinkReverse();
393 this.angle = MathUtils.normalizeAngle(angle, FastMath.PI);
394 final SinCos sinCos = FastMath.sinCos(this.angle);
395 cos = sinCos.cos();
396 sin = sinCos.sin();
397 }
398
399
400
401
402 public double getOriginOffset() {
403 return originOffset;
404 }
405
406
407
408
409 public void setOriginOffset(final double offset) {
410 unlinkReverse();
411 originOffset = offset;
412 }
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428 public static Transform<Euclidean2D, Vector2D, Line, SubLine,
429 Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> getTransform(final double cXX,
430 final double cYX,
431 final double cXY,
432 final double cYY,
433 final double cX1,
434 final double cY1)
435 throws MathIllegalArgumentException {
436 return new LineTransform(cXX, cYX, cXY, cYY, cX1, cY1);
437 }
438
439
440
441
442
443
444
445
446 private static class LineTransform
447 implements Transform<Euclidean2D, Vector2D, Line, SubLine, Euclidean1D, Vector1D, OrientedPoint, SubOrientedPoint> {
448
449
450 private final double cXX;
451
452
453 private final double cYX;
454
455
456 private final double cXY;
457
458
459 private final double cYY;
460
461
462 private final double cX1;
463
464
465 private final double cY1;
466
467
468 private final double c1Y;
469
470
471 private final double c1X;
472
473
474 private final double c11;
475
476
477
478
479
480
481
482
483
484
485 LineTransform(final double cXX, final double cYX, final double cXY,
486 final double cYY, final double cX1, final double cY1)
487 throws MathIllegalArgumentException {
488
489 this.cXX = cXX;
490 this.cYX = cYX;
491 this.cXY = cXY;
492 this.cYY = cYY;
493 this.cX1 = cX1;
494 this.cY1 = cY1;
495
496 c1Y = MathArrays.linearCombination(cXY, cY1, -cYY, cX1);
497 c1X = MathArrays.linearCombination(cXX, cY1, -cYX, cX1);
498 c11 = MathArrays.linearCombination(cXX, cYY, -cYX, cXY);
499
500 if (FastMath.abs(c11) < 1.0e-20) {
501 throw new MathIllegalArgumentException(LocalizedGeometryFormats.NON_INVERTIBLE_TRANSFORM);
502 }
503
504 }
505
506
507 @Override
508 public Vector2D apply(final Vector2D point) {
509 final double x = point.getX();
510 final double y = point.getY();
511 return new Vector2D(MathArrays.linearCombination(cXX, x, cXY, y, cX1, 1),
512 MathArrays.linearCombination(cYX, x, cYY, y, cY1, 1));
513 }
514
515
516 @Override
517 public Line apply(final Line hyperplane) {
518 final double rOffset = MathArrays.linearCombination(c1X, hyperplane.cos, c1Y, hyperplane.sin, c11, hyperplane.originOffset);
519 final double rCos = MathArrays.linearCombination(cXX, hyperplane.cos, cXY, hyperplane.sin);
520 final double rSin = MathArrays.linearCombination(cYX, hyperplane.cos, cYY, hyperplane.sin);
521 final double inv = 1.0 / FastMath.sqrt(rSin * rSin + rCos * rCos);
522 return new Line(FastMath.PI + FastMath.atan2(-rSin, -rCos),
523 inv * rCos, inv * rSin,
524 inv * rOffset, hyperplane.tolerance);
525 }
526
527
528 @Override
529 public SubOrientedPoint apply(final SubOrientedPoint sub, final Line original, final Line transformed) {
530 final OrientedPoint op = sub.getHyperplane();
531 final Vector1D newLoc = transformed.toSubSpace(apply(original.toSpace(op.getLocation())));
532 return new OrientedPoint(newLoc, op.isDirect(), original.tolerance).wholeHyperplane();
533 }
534
535 }
536
537 }