1 package org.djunits.vecmat.d3;
2
3 import java.lang.reflect.Array;
4 import java.util.Arrays;
5 import java.util.Iterator;
6 import java.util.Objects;
7
8 import org.djunits.quantity.SIQuantity;
9 import org.djunits.quantity.def.Quantity;
10 import org.djunits.unit.UnitInterface;
11 import org.djunits.unit.si.SIUnit;
12 import org.djunits.util.Math2;
13 import org.djunits.util.MatrixMath;
14 import org.djunits.vecmat.d1.Vector1;
15 import org.djunits.vecmat.def.Vector;
16 import org.djunits.vecmat.operations.VectorTransposable;
17 import org.djutils.exceptions.Throw;
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 public abstract class Vector3<Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>, V extends Vector3<Q, U, V, SI, H>,
34 SI extends Vector3<SIQuantity, SIUnit, SI, ?, ?>, H extends Vector3<?, ?, ?, ?, ?>> extends Vector<Q, U, V, SI, H>
35 {
36
37 private static final long serialVersionUID = 600L;
38
39
40 private final double xSi;
41
42
43 private final double ySi;
44
45
46 private final double zSi;
47
48
49
50
51
52
53
54
55 protected Vector3(final double xInUnit, final double yInUnit, final double zInUnit, final U displayUnit)
56 {
57 super(displayUnit);
58 this.xSi = displayUnit.toBaseValue(xInUnit);
59 this.ySi = displayUnit.toBaseValue(yInUnit);
60 this.zSi = displayUnit.toBaseValue(zInUnit);
61 }
62
63
64
65
66
67
68
69
70 protected abstract V instantiateSi(double xSiNew, double ySiNew, double zSiNew);
71
72 @Override
73 public V instantiateSi(final double[] siNew)
74 {
75 Throw.when(siNew.length != 3, IllegalArgumentException.class, "Size of new data for Vector3 != 3, but %d",
76 siNew.length);
77 return instantiateSi(siNew[0], siNew[1], siNew[2]);
78 }
79
80 @Override
81 public int size()
82 {
83 return 3;
84 }
85
86 @Override
87 public double si(final int index) throws IndexOutOfBoundsException
88 {
89 return switch (index)
90 {
91 case 0 -> this.xSi;
92 case 1 -> this.ySi;
93 case 2 -> this.zSi;
94 default -> throw new IndexOutOfBoundsException("Cannot retrieve Vector3[" + index + "]");
95 };
96 }
97
98 @Override
99 public Iterator<Q> iterator()
100 {
101 final double[] si = new double[] {this.xSi, this.ySi, this.zSi};
102 final U frozenDisplayUnit = getDisplayUnit();
103 return Arrays.stream(si).mapToObj(v -> frozenDisplayUnit.ofSi(v).setDisplayUnit(frozenDisplayUnit)).iterator();
104 }
105
106 @Override
107 public Q[] getScalarArray()
108 {
109 final Q qx = x();
110 final Class<?> qClass = qx.getClass();
111 @SuppressWarnings("unchecked")
112 final Q[] out = (Q[]) Array.newInstance(qClass, 3);
113 out[0] = qx;
114 out[1] = y();
115 out[2] = z();
116 return out;
117 }
118
119
120
121
122
123 public double xSi()
124 {
125 return this.xSi;
126 }
127
128
129
130
131
132 public double ySi()
133 {
134 return this.ySi;
135 }
136
137
138
139
140
141 public double zSi()
142 {
143 return this.zSi;
144 }
145
146
147
148
149
150 @Override
151 public double[] si()
152 {
153 return new double[] {this.xSi, this.ySi, this.zSi};
154 }
155
156
157
158
159
160 public Q x()
161 {
162 return getDisplayUnit().ofSi(this.xSi).setDisplayUnit(getDisplayUnit());
163 }
164
165
166
167
168
169 public Q y()
170 {
171 return getDisplayUnit().ofSi(this.ySi).setDisplayUnit(getDisplayUnit());
172 }
173
174
175
176
177
178 public Q z()
179 {
180 return getDisplayUnit().ofSi(this.zSi).setDisplayUnit(getDisplayUnit());
181 }
182
183 @Override
184 public V scaleBy(final double factor)
185 {
186 return instantiateSi(this.xSi * factor, this.ySi * factor, this.zSi * factor);
187 }
188
189 @Override
190 public V add(final V other)
191 {
192 return instantiateSi(this.xSi + other.xSi(), this.ySi + other.ySi(), this.zSi + other.zSi());
193 }
194
195 @Override
196 public V subtract(final V other)
197 {
198 return instantiateSi(this.xSi - other.xSi(), this.ySi - other.ySi(), this.zSi - other.zSi());
199 }
200
201 @Override
202 public V negate()
203 {
204 return instantiateSi(-this.xSi, -this.ySi, -this.zSi);
205 }
206
207 @Override
208 public V abs()
209 {
210 return instantiateSi(Math.abs(this.xSi), Math.abs(this.ySi), Math.abs(this.zSi));
211 }
212
213 @Override
214 public Q normL1()
215 {
216 return getDisplayUnit().ofSi(Math.abs(this.xSi) + Math.abs(this.ySi) + Math.abs(this.zSi))
217 .setDisplayUnit(getDisplayUnit());
218 }
219
220 @Override
221 public Q normL2()
222 {
223 return getDisplayUnit().ofSi(Math.sqrt(this.xSi * this.xSi + this.ySi * this.ySi + this.zSi * this.zSi))
224 .setDisplayUnit(getDisplayUnit());
225 }
226
227 @Override
228 public Q normLp(final int p)
229 {
230 return getDisplayUnit().ofSi(Math.pow(
231 Math.pow(Math.abs(this.xSi), p) + Math.pow(Math.abs(this.ySi), p) + Math.pow(Math.abs(this.zSi), p), 1.0 / p))
232 .setDisplayUnit(getDisplayUnit());
233 }
234
235 @Override
236 public Q normLinf()
237 {
238 return getDisplayUnit().ofSi(Math2.maxAbs(this.xSi, this.ySi, this.zSi)).setDisplayUnit(getDisplayUnit());
239 }
240
241 @Override
242 public Q mean()
243 {
244 return getDisplayUnit().ofSi((this.xSi + this.ySi + this.zSi) / 3.0).setDisplayUnit(getDisplayUnit());
245 }
246
247 @Override
248 public Q min()
249 {
250 return getDisplayUnit().ofSi(Math2.min(this.xSi, this.ySi, this.zSi)).setDisplayUnit(getDisplayUnit());
251 }
252
253 @Override
254 public Q max()
255 {
256 return getDisplayUnit().ofSi(Math2.max(this.xSi, this.ySi, this.zSi)).setDisplayUnit(getDisplayUnit());
257 }
258
259 @Override
260 public Q mode()
261 {
262 return max();
263 }
264
265 @Override
266 public Q median()
267 {
268 return getDisplayUnit().ofSi(Math2.median(this.xSi, this.ySi, this.zSi)).setDisplayUnit(getDisplayUnit());
269 }
270
271 @Override
272 public Q sum()
273 {
274 return getDisplayUnit().ofSi(this.xSi + this.ySi + this.zSi).setDisplayUnit(getDisplayUnit());
275 }
276
277 @Override
278 public V add(final Q increment)
279 {
280 return instantiateSi(this.xSi + increment.si(), this.ySi + increment.si(), this.zSi + increment.si());
281 }
282
283 @Override
284 public V subtract(final Q decrement)
285 {
286 return instantiateSi(this.xSi - decrement.si(), this.ySi - decrement.si(), this.zSi - decrement.si());
287 }
288
289 @Override
290 public boolean isRelative()
291 {
292 return x().isRelative();
293 }
294
295
296
297
298
299 @Override
300 public abstract boolean isColumnVector();
301
302 @Override
303 public int hashCode()
304 {
305 return Objects.hash(this.xSi, this.ySi, this.zSi);
306 }
307
308 @SuppressWarnings("checkstyle:needbraces")
309 @Override
310 public boolean equals(final Object obj)
311 {
312 if (this == obj)
313 return true;
314 if (obj == null)
315 return false;
316 if (getClass() != obj.getClass())
317 return false;
318 Vector3<?, ?, ?, ?, ?> other = (Vector3<?, ?, ?, ?, ?>) obj;
319 return Double.doubleToLongBits(this.xSi) == Double.doubleToLongBits(other.xSi)
320 && Double.doubleToLongBits(this.ySi) == Double.doubleToLongBits(other.ySi)
321 && Double.doubleToLongBits(this.zSi) == Double.doubleToLongBits(other.zSi);
322 }
323
324 @Override
325 public String toString(final U withUnit)
326 {
327 var s = new StringBuilder();
328 s.append(isColumnVector() ? "Col" : "Row");
329 s.append("[");
330 s.append(withUnit.fromBaseValue(this.xSi));
331 s.append(", ");
332 s.append(withUnit.fromBaseValue(this.ySi));
333 s.append(", ");
334 s.append(withUnit.fromBaseValue(this.zSi));
335 s.append("] ");
336 s.append(withUnit.getDisplayAbbreviation());
337 return s.toString();
338 }
339
340 @Override
341 public String toString()
342 {
343 return toString(getDisplayUnit());
344 }
345
346
347
348
349
350
351
352
353
354
355
356
357 public static class Col<Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>>
358 extends Vector3<Q, U, Col<Q, U>, Col<SIQuantity, SIUnit>, Col<?, ?>> implements VectorTransposable<Row<Q, U>>
359 {
360
361 private static final long serialVersionUID = 600L;
362
363
364
365
366
367
368
369
370 public Col(final double xInUnit, final double yInUnit, final double zInUnit, final U displayUnit)
371 {
372 super(xInUnit, yInUnit, zInUnit, displayUnit);
373 }
374
375
376
377
378
379
380
381
382
383
384
385 public static <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> Vector3.Col<Q, U> of(final double xInUnit,
386 final double yInUnit, final double zInUnit, final U displayUnit)
387 {
388 return new Vector3.Col<>(xInUnit, yInUnit, zInUnit, displayUnit);
389 }
390
391 @Override
392 public boolean isColumnVector()
393 {
394 return true;
395 }
396
397 @Override
398 public int rows()
399 {
400 return 3;
401 }
402
403 @Override
404 public int cols()
405 {
406 return 1;
407 }
408
409 @Override
410 protected Vector3.Col<Q, U> instantiateSi(final double xSi, final double ySi, final double zSi)
411 {
412 return new Vector3.Col<>(xSi, ySi, zSi, getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
413 }
414
415 @Override
416 public Vector3.Col<SIQuantity, SIUnit> instantiateSi(final double[] siNew, final SIUnit siUnit)
417 {
418 Throw.when(siNew.length != 3, IllegalArgumentException.class, "Size of new data for Vector2 != 2, but %d",
419 siNew.length);
420 return new Vector3.Col<SIQuantity, SIUnit>(siNew[0], siNew[1], siNew[2], siUnit);
421 }
422
423 @Override
424 public double si(final int row, final int col) throws IndexOutOfBoundsException
425 {
426 checkRow(row);
427 checkCol(col);
428 return row == 0 ? xSi() : row == 1 ? ySi() : zSi();
429 }
430
431 @Override
432 public Vector1<Q, U> getRowVector(final int row)
433 {
434 checkRow(row);
435 return new Vector1<Q, U>(si(row, 0), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
436 }
437
438 @Override
439 public Vector1<Q, U> mgetRowVector(final int mRow)
440 {
441 mcheckRow(mRow);
442 return new Vector1<Q, U>(msi(mRow, 1), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
443 }
444
445 @Override
446 public Vector3.Col<Q, U> getColumnVector(final int col)
447 {
448 checkCol(col);
449 return instantiateSi(xSi(), ySi(), zSi()).setDisplayUnit(getDisplayUnit());
450 }
451
452 @Override
453 public Vector3.Col<Q, U> mgetColumnVector(final int mCol)
454 {
455 mcheckCol(mCol);
456 return instantiateSi(xSi(), ySi(), zSi()).setDisplayUnit(getDisplayUnit());
457 }
458
459 @Override
460 public double[] getRowSi(final int row)
461 {
462 checkRow(row);
463 return new double[] {si(row, 0)};
464 }
465
466 @Override
467 public double[] getColumnSi(final int col)
468 {
469 checkCol(col);
470 return new double[] {xSi(), ySi(), zSi()};
471 }
472
473 @Override
474 public Vector3.Row<Q, U> transpose()
475 {
476 return new Vector3.Row<Q, U>(xSi(), ySi(), zSi(), getDisplayUnit());
477 }
478
479 @Override
480 public Vector3.Col<SIQuantity, SIUnit> invertElements()
481 {
482 return new Vector3.Col<SIQuantity, SIUnit>(1.0 / xSi(), 1.0 / ySi(), 1.0 / zSi(),
483 getDisplayUnit().siUnit().invert());
484 }
485
486 @Override
487 public Vector3.Col<SIQuantity, SIUnit> multiplyElements(final Vector3.Col<?, ?> other)
488 {
489 SIUnit siUnit = SIUnit.add(getDisplayUnit().siUnit(), other.getDisplayUnit().siUnit());
490 return new Vector3.Col<SIQuantity, SIUnit>(xSi() * other.xSi(), ySi() * other.ySi(), zSi() * other.zSi(), siUnit);
491 }
492
493 @Override
494 public Vector3.Col<SIQuantity, SIUnit> divideElements(final Vector3.Col<?, ?> other)
495 {
496 SIUnit siUnit = SIUnit.subtract(getDisplayUnit().siUnit(), other.getDisplayUnit().siUnit());
497 return new Vector3.Col<SIQuantity, SIUnit>(xSi() / other.xSi(), ySi() / other.ySi(), zSi() / other.zSi(), siUnit);
498 }
499
500
501
502
503
504
505 public Matrix3x3<SIQuantity, SIUnit> multiply(final Vector3.Row<?, ?> otherVec)
506 {
507 checkMultiply(otherVec);
508 double[] resultData = MatrixMath.multiply(si(), otherVec.si(), 3, 1, 3);
509 return new Matrix3x3<SIQuantity, SIUnit>(resultData,
510 getDisplayUnit().siUnit().plus(otherVec.getDisplayUnit().siUnit()));
511 }
512
513 @Override
514 public Vector3.Col<SIQuantity, SIUnit> multiplyElements(final Quantity<?, ?> quantity)
515 {
516 SIUnit siUnit = SIUnit.add(getDisplayUnit().siUnit(), quantity.getDisplayUnit().siUnit());
517 return new Vector3.Col<SIQuantity, SIUnit>(xSi() * quantity.si(), ySi() * quantity.si(), zSi() * quantity.si(),
518 siUnit);
519 }
520
521
522
523
524
525
526
527
528
529
530 public <TQ extends Quantity<TQ, TU>, TU extends UnitInterface<TU, TQ>> Vector3.Col<TQ, TU> as(final TU targetUnit)
531 throws IllegalArgumentException
532 {
533 Throw.when(!getDisplayUnit().siUnit().equals(targetUnit.siUnit()), IllegalArgumentException.class,
534 "Quantity.as(%s) called, but units do not match: %s <> %s", targetUnit,
535 getDisplayUnit().siUnit().getDisplayAbbreviation(), targetUnit.siUnit().getDisplayAbbreviation());
536 return new Vector3.Col<TQ, TU>(xSi(), ySi(), zSi(), targetUnit.getBaseUnit()).setDisplayUnit(targetUnit);
537 }
538
539 }
540
541
542
543
544
545
546
547
548
549
550
551
552 public static class Row<Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>>
553 extends Vector3<Q, U, Row<Q, U>, Row<SIQuantity, SIUnit>, Row<?, ?>> implements VectorTransposable<Col<Q, U>>
554 {
555
556 private static final long serialVersionUID = 600L;
557
558
559
560
561
562
563
564
565 public Row(final double xSi, final double ySi, final double zSi, final U displayUnit)
566 {
567 super(xSi, ySi, zSi, displayUnit);
568 }
569
570
571
572
573
574
575
576
577
578
579
580 public static <Q extends Quantity<Q, U>, U extends UnitInterface<U, Q>> Vector3.Row<Q, U> of(final double xInUnit,
581 final double yInUnit, final double zInUnit, final U displayUnit)
582 {
583 return new Vector3.Row<>(xInUnit, yInUnit, zInUnit, displayUnit);
584 }
585
586 @Override
587 public boolean isColumnVector()
588 {
589 return false;
590 }
591
592 @Override
593 public int rows()
594 {
595 return 1;
596 }
597
598 @Override
599 public int cols()
600 {
601 return 3;
602 }
603
604 @Override
605 protected Vector3.Row<Q, U> instantiateSi(final double xSi, final double ySi, final double zSi)
606 {
607 return new Vector3.Row<>(xSi, ySi, zSi, getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
608 }
609
610 @Override
611 public Vector3.Row<SIQuantity, SIUnit> instantiateSi(final double[] siNew, final SIUnit siUnit)
612 {
613 Throw.when(siNew.length != 3, IllegalArgumentException.class, "Size of new data for Vector2 != 2, but %d",
614 siNew.length);
615 return new Vector3.Row<SIQuantity, SIUnit>(siNew[0], siNew[1], siNew[2], siUnit);
616 }
617
618 @Override
619 public double si(final int row, final int col) throws IndexOutOfBoundsException
620 {
621 checkRow(row);
622 checkCol(col);
623 return col == 0 ? xSi() : col == 1 ? ySi() : zSi();
624 }
625
626 @Override
627 public Vector3.Row<Q, U> getRowVector(final int row)
628 {
629 checkRow(row);
630 return instantiateSi(xSi(), ySi(), zSi()).setDisplayUnit(getDisplayUnit());
631 }
632
633 @Override
634 public Vector3.Row<Q, U> mgetRowVector(final int mRow)
635 {
636 mcheckRow(mRow);
637 return instantiateSi(xSi(), ySi(), zSi()).setDisplayUnit(getDisplayUnit());
638 }
639
640 @Override
641 public Vector1<Q, U> getColumnVector(final int col)
642 {
643 checkCol(col);
644 return new Vector1<Q, U>(si(0, col), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
645 }
646
647 @Override
648 public Vector1<Q, U> mgetColumnVector(final int mCol)
649 {
650 mcheckCol(mCol);
651 return new Vector1<Q, U>(msi(1, mCol), getDisplayUnit().getBaseUnit()).setDisplayUnit(getDisplayUnit());
652 }
653
654 @Override
655 public double[] getRowSi(final int row)
656 {
657 checkRow(row);
658 return new double[] {xSi(), ySi(), zSi()};
659 }
660
661 @Override
662 public double[] getColumnSi(final int col)
663 {
664 checkCol(col);
665 return new double[] {si(0, col)};
666 }
667
668 @Override
669 public Vector3.Col<Q, U> transpose()
670 {
671 return new Vector3.Col<Q, U>(xSi(), ySi(), zSi(), getDisplayUnit());
672 }
673
674 @Override
675 public Vector3.Row<SIQuantity, SIUnit> invertElements()
676 {
677 return new Vector3.Row<SIQuantity, SIUnit>(1.0 / xSi(), 1.0 / ySi(), 1.0 / zSi(),
678 getDisplayUnit().siUnit().invert());
679 }
680
681 @Override
682 public Vector3.Row<SIQuantity, SIUnit> multiplyElements(final Vector3.Row<?, ?> other)
683 {
684 SIUnit siUnit = SIUnit.add(getDisplayUnit().siUnit(), other.getDisplayUnit().siUnit());
685 return new Vector3.Row<SIQuantity, SIUnit>(xSi() * other.xSi(), ySi() * other.ySi(), zSi() * other.zSi(), siUnit);
686 }
687
688 @Override
689 public Vector3.Row<SIQuantity, SIUnit> divideElements(final Vector3.Row<?, ?> other)
690 {
691 SIUnit siUnit = SIUnit.subtract(getDisplayUnit().siUnit(), other.getDisplayUnit().siUnit());
692 return new Vector3.Row<SIQuantity, SIUnit>(xSi() / other.xSi(), ySi() / other.ySi(), zSi() / other.zSi(), siUnit);
693 }
694
695
696
697
698
699
700 public SIQuantity multiply(final Vector3.Col<?, ?> otherVec)
701 {
702 double[] resultData = MatrixMath.multiply(si(), otherVec.si(), 1, 3, 1);
703 return new SIQuantity(resultData[0], getDisplayUnit().siUnit().plus(otherVec.getDisplayUnit().siUnit()));
704 }
705
706
707
708
709
710
711 public Vector3.Col<SIQuantity, SIUnit> multiply(final Matrix3x3<?, ?> otherMat)
712 {
713 checkMultiply(otherMat);
714 double[] resultData = MatrixMath.multiply(si(), otherMat.si(), 1, 3, 3);
715 return new Vector3.Col<SIQuantity, SIUnit>(resultData[0], resultData[1], resultData[2],
716 getDisplayUnit().siUnit().plus(otherMat.getDisplayUnit().siUnit()));
717 }
718
719 @Override
720 public Vector3.Row<SIQuantity, SIUnit> multiplyElements(final Quantity<?, ?> quantity)
721 {
722 SIUnit siUnit = SIUnit.add(getDisplayUnit().siUnit(), quantity.getDisplayUnit().siUnit());
723 return new Vector3.Row<SIQuantity, SIUnit>(xSi() * quantity.si(), ySi() * quantity.si(), zSi() * quantity.si(),
724 siUnit);
725 }
726
727
728
729
730
731
732
733
734
735
736 public <TQ extends Quantity<TQ, TU>, TU extends UnitInterface<TU, TQ>> Vector3.Row<TQ, TU> as(final TU targetUnit)
737 throws IllegalArgumentException
738 {
739 Throw.when(!getDisplayUnit().siUnit().equals(targetUnit.siUnit()), IllegalArgumentException.class,
740 "Quantity.as(%s) called, but units do not match: %s <> %s", targetUnit,
741 getDisplayUnit().siUnit().getDisplayAbbreviation(), targetUnit.siUnit().getDisplayAbbreviation());
742 return new Vector3.Row<TQ, TU>(xSi(), ySi(), zSi(), targetUnit.getBaseUnit()).setDisplayUnit(targetUnit);
743 }
744
745 }
746
747 }