Ticket #16472: 16472.5.patch

File 16472.5.patch, 28.0 KB (added by taylor.smock, 5 years ago)

Test work for attachment:16472.4.patch . THIS BREAKS IMAGE PANNING! DO NOT APPLY!

  • src/org/openstreetmap/josm/gui/util/imagery/CameraPlane.java

    diff --git a/src/org/openstreetmap/josm/gui/util/imagery/CameraPlane.java b/src/org/openstreetmap/josm/gui/util/imagery/CameraPlane.java
    index 72da2a3a31..5078cb35d6 100644
    a b package org.openstreetmap.josm.gui.util.imagery;  
    44import java.awt.Point;
    55import java.awt.geom.Point2D;
    66import java.awt.image.BufferedImage;
    7 import java.awt.image.DataBuffer;
    8 import java.awt.image.DataBufferDouble;
    9 import java.awt.image.DataBufferInt;
    107import java.util.stream.IntStream;
     8
    119import javax.annotation.Nullable;
    1210
    1311/**
    public class CameraPlane {  
    5452        this.rotation = new Vector3D(Vector3D.VectorType.RPA, distance, 0, 0);
    5553        this.vectors = new Vector3D[width][height];
    5654        IntStream.range(0, this.height).parallel().forEach(y -> IntStream.range(0, this.width).parallel()
    57             .forEach(x -> this.vectors[x][y] = this.getVector3D((double) x, y)));
     55            .forEach(x -> this.vectors[x][y] = this.getNonRotatedVector3D((double) x, y)));
    5856    }
    5957
    6058    /**
    public class CameraPlane {  
    114112    }
    115113
    116114    /**
    117      * Convert a point to a 3D vector (vectors are cached)
     115     * Convert a point to a 3D vector (vectors in image are cached)
    118116     *
    119117     * @param x The x coordinate
    120118     * @param y The y coordinate
    121119     * @return The vector
    122120     */
    123121    public Vector3D getVector3D(final int x, final int y) {
    124         Vector3D res;
    125         try {
    126             res = rotate(vectors[x][y]);
    127         } catch (Exception e) {
    128             res = Vector3D.DEFAULT_VECTOR_3D;
    129         }
    130         return res;
     122        return this.rotate(this.getNonRotatedVector3D(x, y));
    131123    }
    132124
    133125    /**
    public class CameraPlane {  
    138130     * @return The vector (the middle of the image is 0, 0)
    139131     */
    140132    public Vector3D getVector3D(final double x, final double y) {
     133        return this.rotate(this.getNonRotatedVector3D(x, y));
     134    }
     135
     136    private Vector3D getNonRotatedVector3D(final int x, final int y) {
     137        // Prefer cached vectors
     138        if (x >= 0 && y >= 0 && x < this.vectors.length && y < this.vectors[0].length) {
     139            return this.vectors[x][y];
     140        }
     141
     142        return this.getNonRotatedVector3D(x, (double) y);
     143    }
     144
     145    private Vector3D getNonRotatedVector3D(final double x, final double y) {
    141146        return new Vector3D(x - width / 2d, y - height / 2d, this.rotation.getRadialDistance()).normalize();
    142147    }
    143148
    public class CameraPlane {  
    151156    }
    152157
    153158    /**
    154      * Set the rotation from the difference of two points
     159     * Set the rotation from the difference of two points on the original plane
    155160     *
    156      * @param from The originating point
    157      * @param to The new point
     161     * @param fromPoint The originating point
     162     * @param toPoint The new point
    158163     */
    159     public void setRotationFromDelta(final Point from, final Point to) {
    160         // Bound check (bounds are essentially the image viewer component)
    161         if (from.x < 0 || from.y < 0 || to.x < 0 || to.y < 0
    162             || from.x > this.vectors.length || from.y > this.vectors[0].length
    163             || to.x > this.vectors.length || to.y > this.vectors[0].length) {
    164             return;
    165         }
    166         Vector3D f1 = this.vectors[from.x][from.y];
    167         Vector3D t1 = this.vectors[to.x][to.y];
    168         double deltaPolarAngle = f1.getPolarAngle() - t1.getPolarAngle();
    169         double deltaAzimuthalAngle = t1.getAzimuthalAngle() - f1.getAzimuthalAngle();
    170         double polarAngle = this.rotation.getPolarAngle() + deltaPolarAngle;
    171         double azimuthalAngle = this.rotation.getAzimuthalAngle() + deltaAzimuthalAngle;
     164    public void setRotationFromDelta(final Point fromPoint, final Point toPoint) {
     165        final Vector3D from = this.getVector3D(fromPoint.x, fromPoint.y);
     166        final Vector3D to = this.getVector3D(toPoint.x, toPoint.y);
     167        final double deltaPolarAngle = from.getPolarAngle() - to.getPolarAngle();
     168        final double deltaAzimuthalAngle = to.getAzimuthalAngle() - from.getAzimuthalAngle();
     169        final double polarAngle = this.rotation.getPolarAngle() + deltaPolarAngle;
     170        final double azimuthalAngle = this.rotation.getAzimuthalAngle() + deltaAzimuthalAngle;
    172171        this.setRotation(azimuthalAngle, polarAngle);
    173172    }
    174173
    public class CameraPlane {  
    178177     * @param vec vector pointing new view position.
    179178     */
    180179    public void setRotation(Vector3D vec) {
    181         setRotation(vec.getPolarAngle(), vec.getAzimuthalAngle());
     180        setRotation(vec.getAzimuthalAngle(), vec.getPolarAngle());
    182181    }
    183182
    184183    public Vector3D getRotation() {
    public class CameraPlane {  
    189188        // Note: Something, somewhere, is switching the two.
    190189        // FIXME: Figure out what is switching them and why
    191190        // Prevent us from going much outside 2pi
    192         if (polarAngle < 0) {
    193             polarAngle = polarAngle + TWO_PI;
    194         } else if (polarAngle > TWO_PI) {
    195             polarAngle = polarAngle - TWO_PI;
     191        if (azimuthalAngle < 0) {
     192            azimuthalAngle = azimuthalAngle + TWO_PI;
     193        } else if (azimuthalAngle > TWO_PI) {
     194            azimuthalAngle = azimuthalAngle - TWO_PI;
    196195        }
    197196        // Avoid flipping the camera
    198         if (azimuthalAngle > HALF_PI) {
    199             azimuthalAngle = HALF_PI;
    200         } else if (azimuthalAngle < -HALF_PI) {
    201             azimuthalAngle = -HALF_PI;
     197        if (polarAngle > HALF_PI) {
     198            polarAngle = HALF_PI;
     199        } else if (polarAngle < -HALF_PI) {
     200            polarAngle = -HALF_PI;
    202201        }
    203202        this.rotation = new Vector3D(Vector3D.VectorType.RPA, this.rotation.getRadialDistance(), polarAngle, azimuthalAngle);
    204203    }
    public class CameraPlane {  
    229228        // We only do pitch/roll (we specifically do not do roll -- this would lead to tilting the image)
    230229        // So yaw (alpha) -> azimuthalAngle, pitch (beta) -> polarAngle, roll (gamma) -> 0 (sin(gamma) -> 0, cos(gamma) -> 1)
    231230        // gamma is set here just to make it slightly easier to tilt images in the future -- we just have to set the gamma somewhere else.
    232         // Ironically enough, the alpha (yaw) and gama (roll) got reversed somewhere. TODO figure out where and fix this.
     231        // Ironically enough, the alpha (yaw) and gama (roll) got reversed somewhere. TODO figure out where and fix it.
     232        // z == roll axis
     233        // x == pitch axis
     234        // y == yaw axis
    233235        final int gamma = 0;
    234         final double sinAlpha = Math.sin(gamma);
    235         final double cosAlpha = Math.cos(gamma);
    236         final double cosGamma = this.rotation.getAzimuthalAngleCos();
    237         final double sinGamma = this.rotation.getAzimuthalAngleSin();
    238         final double cosBeta = this.rotation.getPolarAngleCos();
    239         final double sinBeta = this.rotation.getPolarAngleSin();
     236        final double sinGamma = Math.sin(gamma);
     237        final double cosGamma = Math.cos(gamma);
     238        final double cosBeta = this.rotation.getAzimuthalAngleCos();
     239        final double sinBeta = this.rotation.getAzimuthalAngleSin();
     240        final double cosAlpha = this.rotation.getPolarAngleCos();
     241        final double sinAlpha = this.rotation.getPolarAngleSin();
    240242        final double x = vec.getX();
    241243        final double y = YAW_DIRECTION * vec.getY();
    242244        final double z = vec.getZ();
    public class CameraPlane {  
    249251    }
    250252
    251253    public void mapping(BufferedImage sourceImage, BufferedImage targetImage) {
    252         DataBuffer sourceBuffer = sourceImage.getRaster().getDataBuffer();
    253         DataBuffer targetBuffer = targetImage.getRaster().getDataBuffer();
    254         // Faster mapping
    255         if (sourceBuffer.getDataType() == DataBuffer.TYPE_INT && targetBuffer.getDataType() == DataBuffer.TYPE_INT) {
    256             int[] sourceImageBuffer = ((DataBufferInt) sourceImage.getRaster().getDataBuffer()).getData();
    257             int[] targetImageBuffer = ((DataBufferInt) targetImage.getRaster().getDataBuffer()).getData();
    258             IntStream.range(0, targetImage.getHeight()).parallel()
    259                     .forEach(y -> IntStream.range(0, targetImage.getWidth()).forEach(x -> {
    260                         final Point2D.Double p = mapPoint(x, y);
    261                         int tx = (int) (p.x * (sourceImage.getWidth() - 1));
    262                         int ty = (int) (p.y * (sourceImage.getHeight() - 1));
    263                         int color = sourceImageBuffer[ty * sourceImage.getWidth() + tx];
    264                         targetImageBuffer[y * targetImage.getWidth() + x] = color;
    265                     }));
    266         } else if (sourceBuffer.getDataType() == DataBuffer.TYPE_DOUBLE && targetBuffer.getDataType() == DataBuffer.TYPE_DOUBLE) {
    267             double[] sourceImageBuffer = ((DataBufferDouble) sourceImage.getRaster().getDataBuffer()).getData();
    268             double[] targetImageBuffer = ((DataBufferDouble) targetImage.getRaster().getDataBuffer()).getData();
    269             IntStream.range(0, targetImage.getHeight()).parallel()
    270                     .forEach(y -> IntStream.range(0, targetImage.getWidth()).forEach(x -> {
    271                         final Point2D.Double p = mapPoint(x, y);
    272                         int tx = (int) (p.x * (sourceImage.getWidth() - 1));
    273                         int ty = (int) (p.y * (sourceImage.getHeight() - 1));
    274                         double color = sourceImageBuffer[ty * sourceImage.getWidth() + tx];
    275                         targetImageBuffer[y * targetImage.getWidth() + x] = color;
    276                     }));
    277         } else {
    278             IntStream.range(0, targetImage.getHeight()).parallel()
    279                 .forEach(y -> IntStream.range(0, targetImage.getWidth()).parallel().forEach(x -> {
    280                     final Point2D.Double p = mapPoint(x, y);
    281                     targetImage.setRGB(x, y, sourceImage.getRGB((int) (p.x * (sourceImage.getWidth() - 1)),
    282                         (int) (p.y * (sourceImage.getHeight() - 1))));
    283                 }));
    284         }
     254        // There can be slightly faster mapping if both data buffers are the same time (image.getRaster().getDataBuffer())
     255        IntStream.range(0, targetImage.getHeight()).parallel()
     256            .forEach(y -> IntStream.range(0, targetImage.getWidth()).parallel().forEach(x -> {
     257                final Point2D.Double p = mapPoint(x, y);
     258                targetImage.setRGB(x, y, sourceImage.getRGB((int) (p.x * (sourceImage.getWidth() - 1)),
     259                    (int) (p.y * (sourceImage.getHeight() - 1))));
     260            }));
    285261    }
    286262
    287263    /**
    288      * Map a real point to the displayed point. This method uses cached vectors.
     264     * Map a real point to the displayed point. This method may use cached vectors.
    289265     * @param x The original x coordinate
    290266     * @param y The original y coordinate
    291267     * @return The scaled (0-1) point in the image. Use {@code p.x * (image.getWidth() - 1)} or {@code p.y * image.getHeight() - 1}.
  • src/org/openstreetmap/josm/gui/util/imagery/Vector3D.java

    diff --git a/src/org/openstreetmap/josm/gui/util/imagery/Vector3D.java b/src/org/openstreetmap/josm/gui/util/imagery/Vector3D.java
    index ba98b7d884..2052e351a2 100644
    a b public final class Vector3D {  
    3030    private final double z;
    3131    /* The following are all lazily calculated, but should always be the same */
    3232    /** The radius r */
    33     private volatile double radialDistance = Double.NaN;
     33    private double radialDistance = Double.NaN;
    3434    /** The polar angle theta (inclination) */
    35     private volatile double polarAngle = Double.NaN;
     35    private double polarAngle = Double.NaN;
    3636    /** Cosine of polar angle (angle from Z axis, AKA straight up) */
    37     private volatile double polarAngleCos = Double.NaN;
     37    private double polarAngleCos = Double.NaN;
    3838    /** Sine of polar angle (angle from Z axis, AKA straight up) */
    39     private volatile double polarAngleSin = Double.NaN;
     39    private double polarAngleSin = Double.NaN;
    4040    /** The azimuthal angle phi */
    41     private volatile double azimuthalAngle = Double.NaN;
     41    private double azimuthalAngle = Double.NaN;
    4242    /** Cosine of azimuthal angle (angle from X axis) */
    43     private volatile double azimuthalAngleCos = Double.NaN;
     43    private double azimuthalAngleCos = Double.NaN;
    4444    /** Sine of azimuthal angle (angle from X axis) */
    45     private volatile double azimuthalAngleSin = Double.NaN;
     45    private double azimuthalAngleSin = Double.NaN;
    4646
    4747    /**
    4848     * Create a new Vector3D object using the XYZ coordinate system
    public final class Vector3D {  
    130130     */
    131131    public double getPolarAngle() {
    132132        if (Double.isNaN(this.polarAngle)) {
    133             // This was Math.atan(x, z) in the Mapillary plugin
    134             // This should be Math.atan(y, z)
    135             this.polarAngle = Math.atan2(this.x, this.z);
     133            if (Double.isNaN(this.radialDistance)) {
     134                // Force calculation
     135                this.getRadialDistance();
     136            }
     137            // Avoid issues where x, y, and z are 0
     138            if (this.radialDistance == 0) {
     139                this.polarAngle = 0;
     140            } else {
     141                // This was Math.acos(y / radialDistance) in the Mapillary plugin
     142                // This should be Math.acos(z / radialDistance)
     143                this.polarAngle = Math.acos(this.z / this.radialDistance);
     144            }
    136145        }
    137146        return this.polarAngle;
    138147    }
    public final class Vector3D {  
    168177     */
    169178    public double getAzimuthalAngle() {
    170179        if (Double.isNaN(this.azimuthalAngle)) {
    171             if (Double.isNaN(this.radialDistance)) {
    172                 // Force calculation
    173                 this.getRadialDistance();
    174             }
    175             // Avoid issues where x, y, and z are 0
    176             if (this.radialDistance == 0) {
    177                 this.azimuthalAngle = 0;
    178             } else {
    179                 // This was Math.acos(y / radialDistance) in the Mapillary plugin
    180                 // This should be Math.acos(z / radialDistance)
    181                 this.azimuthalAngle = Math.acos(this.y / this.radialDistance);
    182             }
     180            // This was Math.atan(x, z) in the Mapillary plugin
     181            // This should be Math.atan(y, z)
     182            this.azimuthalAngle = Math.atan2(this.y, this.z);
    183183        }
    184184        return this.azimuthalAngle;
    185185    }
    public final class Vector3D {  
    239239    public boolean equals(Object o) {
    240240        if (o instanceof Vector3D) {
    241241            Vector3D other = (Vector3D) o;
    242             return this.x == other.x && this.y == other.y && this.z == other.z;
     242            return Double.compare(this.x, other.x) == 0
     243                    && Double.compare(this.y, other.y) == 0
     244                    && Double.compare(this.z, other.z) == 0;
    243245        }
    244246        return false;
    245247    }
    246248
    247249    @Override
    248250    public String toString() {
     251        // Initialize the various ephemeral objects
     252        this.getRadialDistance();
     253        this.getAzimuthalAngle();
     254        this.getPolarAngle();
    249255        return "[x=" + this.x + ", y=" + this.y + ", z=" + this.z + ", r=" + this.radialDistance + ", inclination="
    250256            + this.polarAngle + ", azimuthal=" + this.azimuthalAngle + "]";
    251257    }
  • test/unit/org/openstreetmap/josm/gui/util/imagery/CameraPlaneTest.java

    diff --git a/test/unit/org/openstreetmap/josm/gui/util/imagery/CameraPlaneTest.java b/test/unit/org/openstreetmap/josm/gui/util/imagery/CameraPlaneTest.java
    index 03e282a2cc..2868d03d9a 100644
    a b import java.awt.geom.Point2D;  
    99import java.util.stream.Stream;
    1010
    1111import org.junit.jupiter.api.BeforeEach;
    12 import org.junit.jupiter.api.Disabled;
    1312import org.junit.jupiter.api.Test;
    1413import org.junit.jupiter.params.ParameterizedTest;
    1514import org.junit.jupiter.params.provider.Arguments;
    class CameraPlaneTest {  
    2019    private static final int CAMERA_PLANE_WIDTH = 800;
    2120    private static final int CAMERA_PLANE_HEIGHT = 600;
    2221
     22    private static final double MAX_DELTA = 1e-15;
     23
    2324    private CameraPlane cameraPlane;
    2425
    2526    @BeforeEach
    class CameraPlaneTest {  
    2829    }
    2930
    3031    @Test
    31     @Disabled("Currently broken")
     32    void testDimensions() {
     33        assertAll(() -> assertEquals(CAMERA_PLANE_HEIGHT, this.cameraPlane.getHeight()),
     34                () -> assertEquals(CAMERA_PLANE_WIDTH, this.cameraPlane.getWidth()));
     35    }
     36
     37    @Test
    3238    void testSetRotation() {
    3339        Vector3D vec = new Vector3D(0, 0, 1);
    34         cameraPlane.setRotation(vec);
    35         Vector3D out = cameraPlane.getRotation();
    36         assertAll(() -> assertEquals(280.0830152838839, out.getRadialDistance(), 0.001),
    37             () -> assertEquals(0, out.getPolarAngle(), 0.001), () -> assertEquals(0, out.getAzimuthalAngle(), 0.001));
     40        this.cameraPlane.setRotation(vec);
     41        Vector3D out = this.cameraPlane.getRotation();
     42        assertAll(() -> assertEquals(280.0830152838839, out.getRadialDistance(), 0.001, "Radial distance"),
     43            () -> assertEquals(0, out.getPolarAngle(), 0.001, "Polar angle"),
     44                () -> assertEquals(0, out.getAzimuthalAngle(), 0.001, "Azimuth angle"));
     45    }
     46
     47    static Stream<Arguments> testRotationFromDelta() {
     48        return Stream.of(Arguments.of(new Point(CAMERA_PLANE_WIDTH - 1, CAMERA_PLANE_HEIGHT / 2), 0, Math.PI),
     49                Arguments.of(new Point(0, CAMERA_PLANE_HEIGHT / 2), 0, -Math.PI),
     50                Arguments.of(new Point(CAMERA_PLANE_WIDTH / 2, CAMERA_PLANE_HEIGHT - 1), Math.PI / 2, 0),
     51                Arguments.of(new Point(CAMERA_PLANE_WIDTH / 2, 0), - Math.PI / 2, 0));
     52    }
     53
     54    @ParameterizedTest
     55    @MethodSource
     56    void testRotationFromDelta(final Point to, final double expectedPolarAngle, final double expectedAzimuthalAngle) {
     57        final double maxDelta = 1E-15;
     58        // Ensure that rotation is set to the default, and it correctly works
     59        this.testSetRotation();
     60        final Vector3D initialVector = this.cameraPlane.getRotation();
     61        final Point zero = new Point(CAMERA_PLANE_WIDTH / 2, CAMERA_PLANE_HEIGHT / 2);
     62        // This checks that we can correctly rotate to the edge of the image
     63        this.cameraPlane.setRotationFromDelta(zero, to);
     64        final Vector3D newRotation = this.cameraPlane.getRotation();
     65        assertAll(() -> assertEquals(initialVector.getRadialDistance(), newRotation.getRadialDistance(), "Radial distance should not change"),
     66                () -> assertEquals(expectedPolarAngle, newRotation.getPolarAngle(), maxDelta, "Pitch"),
     67                () -> assertEquals(expectedAzimuthalAngle, newRotation.getAzimuthalAngle(), maxDelta, "Yaw"));
     68    }
     69
     70    static Stream<Arguments> testMapPoint() {
     71        return Stream.of(Arguments.of(new Point2D.Double(0.5, 0.5), new Point2D.Double(CAMERA_PLANE_WIDTH / 2d, CAMERA_PLANE_HEIGHT / 2d), new Vector3D(0, 0, 1)),
     72                Arguments.of(new Point2D.Double(0.3472222222222222, 0.5), new Point2D.Double(0, CAMERA_PLANE_HEIGHT / 2d), new Vector3D(0, 0, 1)),
     73                Arguments.of(new Point2D.Double(0.6525905178237755, 0.5), new Point2D.Double(CAMERA_PLANE_WIDTH - 1, CAMERA_PLANE_HEIGHT / 2d), new Vector3D(0, 0, 1)),
     74                Arguments.of(new Point2D.Double(0.5, 0.7603945765026368), new Point2D.Double(CAMERA_PLANE_WIDTH / 2d, CAMERA_PLANE_HEIGHT - 1), new Vector3D(0, 0, 1)),
     75                Arguments.of(new Point2D.Double(0.5, 0.239075212564189), new Point2D.Double(CAMERA_PLANE_WIDTH / 2d, 0), new Vector3D(0, 0, 1)));
     76    }
     77
     78    @ParameterizedTest
     79    @MethodSource
     80    void testMapPoint(final Point2D.Double expectedScaled, final Point2D.Double initial, final Vector3D rotation) {
     81        this.cameraPlane.setRotation(rotation);
     82        final Point2D.Double actual = this.cameraPlane.mapPoint(initial.x, initial.y);
     83        assertAll(() -> assertEquals(expectedScaled.x, actual.x, MAX_DELTA, "X"),
     84                () -> assertEquals(expectedScaled.y, actual.y, MAX_DELTA, "Y"));
     85        final Point2D.Double intActual = this.cameraPlane.mapPoint((int) initial.x, (int) initial.y);
     86        assertAll(() -> assertEquals(expectedScaled.x, intActual.x, MAX_DELTA, "X"),
     87                () -> assertEquals(expectedScaled.y, intActual.y, MAX_DELTA, "Y"));
    3888    }
    3989
    4090    @Test
    41     @Disabled("Currently broken")
    4291    void testGetVector3D() {
    4392        Vector3D vec = new Vector3D(0, 0, 1);
    44         cameraPlane.setRotation(vec);
    45         Vector3D out = cameraPlane.getVector3D(new Point(CAMERA_PLANE_WIDTH / 2, CAMERA_PLANE_HEIGHT / 2));
    46         assertAll(() -> assertEquals(0.0, out.getX(), 1.0E-04), () -> assertEquals(0.0, out.getY(), 1.0E-04),
    47             () -> assertEquals(1.0, out.getZ(), 1.0E-04));
     93        this.cameraPlane.setRotation(vec);
     94        Vector3D out = this.cameraPlane.getVector3D(new Point(CAMERA_PLANE_WIDTH / 2, CAMERA_PLANE_HEIGHT / 2));
     95        assertAll(() -> assertEquals(0.0, out.getX(), 1.0E-04, "X"),
     96                () -> assertEquals(0.0, out.getY(), 1.0E-04, "Y"),
     97            () -> assertEquals(1.0, out.getZ(), 1.0E-04, "Z"));
    4898    }
    4999
    50100    static Stream<Arguments> testGetVector3DFloat() {
    class CameraPlaneTest {  
    63113    @ParameterizedTest
    64114    @MethodSource
    65115    void testGetVector3DFloat(final Vector3D expected, final Point toCheck) {
    66         Vector3D out = cameraPlane.getVector3D(toCheck.getX(), toCheck.getY());
    67         assertAll(() -> assertEquals(expected.getX(), out.getX(), 1.0E-04),
    68             () -> assertEquals(expected.getY(), out.getY(), 1.0E-04),
    69             () -> assertEquals(expected.getZ(), out.getZ(), 1.0E-04), () -> assertEquals(1,
    70                 Math.sqrt(Math.pow(out.getX(), 2) + Math.pow(out.getY(), 2) + Math.pow(out.getZ(), 2)), 1.0E-04));
     116        Vector3D out = this.cameraPlane.getVector3D(toCheck.getX(), toCheck.getY());
     117        assertAll(() -> assertEquals(expected.getX(), out.getX(), 1.0E-04, "X"),
     118            () -> assertEquals(expected.getY(), out.getY(), 1.0E-04, "Y"),
     119            () -> assertEquals(expected.getZ(), out.getZ(), 1.0E-04, "Z"),
     120                () -> assertEquals(1, Math.sqrt(Math.pow(out.getX(), 2) + Math.pow(out.getY(), 2) + Math.pow(out.getZ(), 2)),
     121                        1.0E-04, "Radial distance"));
    71122    }
    72123
    73124    @Test
    74     @Disabled("Currently broken")
    75125    void testMapping() {
    76126        Vector3D vec = new Vector3D(0, 0, 1);
    77         cameraPlane.setRotation(vec);
    78         Vector3D out = cameraPlane.getVector3D(new Point(300, 200));
     127        this.cameraPlane.setRotation(vec);
     128        Vector3D out = this.cameraPlane.getVector3D(new Point(300, 200));
    79129        Point2D map = UVMapping.getTextureCoordinate(out);
    80         assertAll(() -> assertEquals(0.44542099, map.getX(), 1e-8), () -> assertEquals(0.39674936, map.getY(), 1e-8));
     130        assertAll(() -> assertEquals(0.44542099, map.getX(), 1e-8, "X"),
     131                () -> assertEquals(0.39674936, map.getY(), 1e-8, "Y"));
    81132    }
    82133}
  • test/unit/org/openstreetmap/josm/gui/util/imagery/Vector3DTest.java

    diff --git a/test/unit/org/openstreetmap/josm/gui/util/imagery/Vector3DTest.java b/test/unit/org/openstreetmap/josm/gui/util/imagery/Vector3DTest.java
    index 5f360956fc..28797d1fc5 100644
    a b package org.openstreetmap.josm.gui.util.imagery;  
    33
    44import static org.junit.jupiter.api.Assertions.assertAll;
    55import static org.junit.jupiter.api.Assertions.assertEquals;
    6 import static org.junit.jupiter.api.Assertions.fail;
    76
    87import java.util.stream.Stream;
    98
    10 import org.junit.jupiter.api.Disabled;
     9import nl.jqno.equalsverifier.EqualsVerifier;
     10import org.junit.jupiter.api.Test;
    1111import org.junit.jupiter.params.ParameterizedTest;
    1212import org.junit.jupiter.params.provider.Arguments;
    1313import org.junit.jupiter.params.provider.MethodSource;
    import org.junit.jupiter.params.provider.MethodSource;  
    1818 */
    1919class Vector3DTest {
    2020
     21    private static final double MAX_DELTA = 1E-14;
     22
    2123    static Stream<Arguments> vectorInformation() {
    2224        return Stream.of(
    23             Arguments.of(0, 0, 0, 0),
    24             Arguments.of(1, 1, 1, Math.sqrt(3)),
    25             Arguments.of(-1, -1, -1, Math.sqrt(3)),
    26             Arguments.of(-2, 2, -2, Math.sqrt(12))
     25            // (x, y, z, radialDistance, polarAngle, azimuthAngle)
     26            Arguments.of(0, 0, 0, 0, 0, 0),
     27            Arguments.of(1, 1, 1, Math.sqrt(3), 0.9553166181245092, Math.PI / 4),
     28            Arguments.of(-1, -1, -1, Math.sqrt(3), 2.1862760354652844, -3 * Math.PI / 4),
     29            Arguments.of(-2, 2, -2, Math.sqrt(12), 2.1862760354652844, 3 * Math.PI / 4)
    2730        );
    2831    }
    2932
    class Vector3DTest {  
    5255    @MethodSource("vectorInformation")
    5356    void getRadialDistance(final double x, final double y, final double z, final double radialDistance) {
    5457        final Vector3D vector3D = new Vector3D(x, y, z);
    55         assertEquals(radialDistance, vector3D.getRadialDistance());
     58        assertEquals(radialDistance, vector3D.getRadialDistance(), MAX_DELTA);
    5659    }
    5760
    5861    @ParameterizedTest
    5962    @MethodSource("vectorInformation")
    60     @Disabled("Angle calculations may be corrected")
    61     void getPolarAngle() {
    62         fail("Not yet implemented");
     63    void getPolarAngle(final double x, final double y, final double z, final double ignoredRadialDistance, final double polarAngle) {
     64        final Vector3D vector3D = new Vector3D(x, y, z);
     65        assertEquals(polarAngle, vector3D.getPolarAngle(), MAX_DELTA);
    6366    }
    6467
    6568    @ParameterizedTest
    6669    @MethodSource("vectorInformation")
    67     @Disabled("Angle calculations may be corrected")
    68     void getAzimuthalAngle() {
    69         fail("Not yet implemented");
     70    void getAzimuthalAngle(final double x, final double y, final double z,
     71            final double ignoredRadialDistance, final double ignoredPolarAngle, final double azimuthAngle) {
     72        final Vector3D vector3D = new Vector3D(x, y, z);
     73        assertEquals(azimuthAngle, vector3D.getAzimuthalAngle(), MAX_DELTA);
    7074    }
    7175
    7276    @ParameterizedTest
    class Vector3DTest {  
    8185
    8286    @ParameterizedTest
    8387    @MethodSource("vectorInformation")
    84     @Disabled("Angle calculations may be corrected")
    85     void testToString() {
    86         fail("Not yet implemented");
     88    void testConstructorArgs(final double x, final double y, final double z) {
     89        final Vector3D vector3D = new Vector3D(x, y, z);
     90        final Vector3D vector3DRPA = new Vector3D(Vector3D.VectorType.RPA, vector3D.getRadialDistance(),
     91                vector3D.getPolarAngle(), vector3D.getAzimuthalAngle());
     92        final Vector3D vector3DRAP = new Vector3D(Vector3D.VectorType.RAP, vector3D.getRadialDistance(),
     93                vector3D.getAzimuthalAngle(), vector3D.getPolarAngle());
     94        assertAll(() -> assertEquals(vector3D.getX(), vector3DRPA.getX(), MAX_DELTA, "RPA X"),
     95                () -> assertEquals(vector3D.getY(), vector3DRPA.getY(), MAX_DELTA, "RPA Y"),
     96                () -> assertEquals(vector3D.getZ(), vector3DRPA.getZ(), MAX_DELTA, "RPA Z"),
     97                () -> assertEquals(vector3D.getX(), vector3DRAP.getX(), MAX_DELTA, "RAP X"),
     98                () -> assertEquals(vector3D.getY(), vector3DRAP.getY(), MAX_DELTA, "RAP Y"),
     99                () -> assertEquals(vector3D.getZ(), vector3DRAP.getZ(), MAX_DELTA, "RAP Z"));
     100    }
     101
     102    @ParameterizedTest
     103    @MethodSource("vectorInformation")
     104    void testToString(final double x, final double y, final double z,
     105            final double radialDistance, final double polarAngle, final double azimuthAngle) {
     106        final Vector3D vector3D = new Vector3D(x, y, z);
     107        assertEquals("[x=" + x + ", y=" + y + ", z=" + z + ", r=" + radialDistance + ", inclination="
     108                + polarAngle + ", azimuthal=" + azimuthAngle + "]", vector3D.toString());
     109    }
     110
     111    @Test
     112    void testEqualsVerifier() {
     113        EqualsVerifier.forClass(Vector3D.class)
     114                // These fields are all dependendent upon x, y, and z, and should always evaluate to the same numbers
     115                .withIgnoredFields("radialDistance",
     116                        "polarAngle", "polarAngleCos", "polarAngleSin",
     117                        "azimuthalAngle", "azimuthalAngleCos", "azimuthalAngleSin")
     118                .verify();
    87119    }
    88120}