Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(geomath): introduce class for point cloud #401

Merged
merged 9 commits into from
Aug 14, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.eclipse.mosaic.fed.sumo.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.verify;
Expand Down Expand Up @@ -155,13 +156,13 @@ public void addAndGenerateLaneAssignment() throws InternalFederateException, IOE
);

long fileSize0 = Files.size(temporaryFolder.getRoot().toPath().resolve(base.resolve("AV-lane0.png")));
assertEquals(15d, fileSize0 / 1000d, 7d);
assertTrue(fileSize0 > 0);

long fileSize1 = Files.size(temporaryFolder.getRoot().toPath().resolve(base.resolve("ALL-lane1.png")));
assertEquals(15d, fileSize1 / 1000d, 7d);
assertTrue(fileSize1 > 0);

long fileSize2 = Files.size(temporaryFolder.getRoot().toPath().resolve(base.resolve("EMPTY-lane2.png")));
assertEquals(70d, fileSize2 / 1000d, 20d);
assertTrue(fileSize2 > 0);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
/*
* Copyright (c) 2024 Fraunhofer FOKUS and others. All rights reserved.
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contact: [email protected]
*/

package org.eclipse.mosaic.lib.spatial;
schwepmo marked this conversation as resolved.
Show resolved Hide resolved

import org.eclipse.mosaic.lib.math.Matrix3d;
import org.eclipse.mosaic.lib.math.Vector3d;

import java.io.Serializable;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
* This class represents a point cloud based on the {@link Vector3d} coordinate system.
* <ul> <li>It consists of a reference point which represents the origin/center of this point cloud
* in absolute world coordinates.</li>
* <li>A rotation matrix used to translate relative end points of the point cloud to absolute coordinates.</li>
* <li>A list of points (x,y,z, hit type, distance to origin) forming this point cloud.</li>
* <li>Use {@link Relative} to create a point cloud by a given list of points in relative coordinates.
* When {@link #getAbsoluteEndPoints} is called on such object, a coordinate transformation is done (and cached).</li>
* <li>Use {@link Absolute} to create a point cloud by a given list of points in absolute coordinates.
* When {@link #getRelativeEndPoints()} is called on such object, a coordinate transformation is done (and cached).</li>
* </ul>
*/
public abstract class PointCloud implements Serializable {

private static final long serialVersionUID = 1L;

private final Vector3d reference;
private final RotationMatrix rotation;
private final long timestamp;
private final double minRange;
private final double maxRange;

protected final List<Point> points;

protected PointCloud(RotationMatrix rotation, Vector3d reference, List<Point> points,
long timestamp, double minRange, double maxRange) {
this.rotation = rotation;
this.reference = reference;
this.points = points;
this.timestamp = timestamp;
this.minRange = minRange;
this.maxRange = maxRange;
}

/**
* Relative coordinates mean the use of a cartesian coordinate system with its origin (0,0,0) at
* the {@link PointCloud}'s reference, returned by {@link #getReference()}. In addition, the
* coordinate system is rotated. See {@link #getRotation()} for details.
*
* @return end points of all rays of this {@link PointCloud} in relative coordinates
* @see #getReference() origin of ray / translation of coordinates
* @see #getRotation() rotation of coordinates
*/
public abstract List<Point> getRelativeEndPoints();


/**
* @return end points of all rays of this {@link PointCloud} that hit something in relative coordinates
*/
public abstract List<Point> getRelativeEndPointsWithHit();

/**
* @return end points of all rays of this {@link PointCloud} in absolute coordinates
*/
public abstract List<Point> getAbsoluteEndPoints();

/**
* @return end points of all rays of this {@link PointCloud} that hit something in absolute coordinates
*/
public abstract List<Point> getAbsoluteEndPointsWithHit();

/**
* A {@link PointCloud} is represented by a constant origin and their end points
* returned by {@link #getRelativeEndPoints()} and {@link #getAbsoluteEndPoints()}
* Relative coordinates are translated and rotated absolute coordinates.
*
* @return origin of the rays forming this point cloud in absolute coordinates
* @see #getRotation()
*/
public Vector3d getReference() {
return reference;
}

/**
* Ray end points in relative coordinates of this point cloud are translated and rotated end points in
* absolute coordinates.
* <p>
* Let o be the ray origin in absolute coordinates, let R be the rotation matrix. Thus, the transformation
* of relative coordinates r into absolute coordinates a is
* <code>a = R * r + o</code>
*
* @return rotation matrix R
* @see #getReference() returns <code>o</code>
*/
public RotationMatrix getRotation() {
return rotation;
}

/**
* @return simulation time of {@link PointCloud}
*/
public long getTimestamp() {
return timestamp;
}

/**
* @return sensor's minimum detectable range in distance units of Phabmacs
kschrab marked this conversation as resolved.
Show resolved Hide resolved
*/
public double getMinRange() {
return minRange;
}

/**
* @return sensor's maximum detectable range in distance units of Phabmacs
*/
public double getMaxRange() {
return maxRange;
}

/**
* A {@link Point} of the point cloud consists of its coordinates, an identifier
* of the type of object the point has hit, and the distance to the point cloud origin.
*/
public static class Point extends Vector3d {

private static final long serialVersionUID = 1L;

private final byte hitType;
schwepmo marked this conversation as resolved.
Show resolved Hide resolved
private final float distance;

/**
* @param endPoint the coordinates of the point cloud
* @param distance the distance to the origin of the point cloud
* @param hitType the type of hit object represented by this point. 0 = no hit
*/
public Point(Vector3d endPoint, float distance, byte hitType) {
x = endPoint.x;
y = endPoint.y;
z = endPoint.z;
this.distance = distance;
this.hitType = hitType;
}

/**
* @return true if the ray generating this {@link Point} has hit an object
schwepmo marked this conversation as resolved.
Show resolved Hide resolved
*/
public boolean hasHit() {
return hitType != 0;
}

/**
* @return the type of the object the ray generating this point has hit. (0 = no hit)
*/
public byte getHitType() {
return hitType;
}

/**
* @return the distance to the origin of point cloud this point belongs to
*/
public float getDistance() {
return distance;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Point other = (Point) o;
return super.equals(other) &&
this.hitType == other.hitType &&
Float.compare(this.distance, other.distance) == 0;
}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + Byte.hashCode(hitType);
result = 31 * result + Float.hashCode(distance);
return result;
}
}

public static class Absolute extends PointCloud {

private static final long serialVersionUID = 1L;

private transient List<Point> absoluteEndPointsWithHit = null;
private transient List<Point> relativeEndPoints = null;
private transient List<Point> relativeEndPointsWithHit = null;
kschrab marked this conversation as resolved.
Show resolved Hide resolved

public Absolute(RotationMatrix rotation, Vector3d reference, List<Point> absoluteEndPoints,
long timestamp, double minRange, double maxRange) {
super(rotation, reference, absoluteEndPoints, timestamp, minRange, maxRange);
}

@Override
public List<Point> getAbsoluteEndPoints() {
return points;
}

@Override
public List<Point> getAbsoluteEndPointsWithHit() {
if (absoluteEndPointsWithHit == null) {
absoluteEndPointsWithHit = getAbsoluteEndPoints().stream().filter(Point::hasHit).collect(Collectors.toList());
}
return absoluteEndPointsWithHit;
}

@Override
public List<Point> getRelativeEndPoints() {
if (relativeEndPoints == null) {
relativeEndPoints = absoluteToRelative(points, p -> true);
}
return relativeEndPoints;
}

@Override
public List<Point> getRelativeEndPointsWithHit() {
if (relativeEndPointsWithHit == null) {
relativeEndPointsWithHit = absoluteToRelative(points, Point::hasHit);
}
return relativeEndPointsWithHit;
}

/**
* Transforms list of {@link Point}s in absolute coordinates to relative coordinates. *
*
* @param absoluteEndPoints end points in absolute coordinates
* @return end points in relative coordinates
*/
private List<Point> absoluteToRelative(List<Point> absoluteEndPoints, Predicate<Point> filter) {
schwepmo marked this conversation as resolved.
Show resolved Hide resolved
Matrix3d inv = getRotation().transpose(new Matrix3d());
return absoluteEndPoints
.stream()
.filter(filter)
.map(point -> (Point) inv.multiply(point.subtract(getReference(), new Point(point, point.getDistance(), point.getHitType()))))
.collect(Collectors.toList());
}
}

public static class Relative extends PointCloud {

private static final long serialVersionUID = 1L;

private transient List<Point> relativeEndPointsWithHit = null;
private transient List<Point> absoluteEndPoints = null;
private transient List<Point> absoluteEndPointsWithHit = null;

public Relative(RotationMatrix rotation, Vector3d reference, List<Point> relativeEndPoints,
long timestamp, double minRange, double maxRange) {
super(rotation, reference, relativeEndPoints, timestamp, minRange, maxRange);
}

@Override
public List<Point> getAbsoluteEndPoints() {
if (absoluteEndPoints == null) {
absoluteEndPoints = relativeToAbsolute(points, p -> true);
}
return absoluteEndPoints;
}

@Override
public List<Point> getAbsoluteEndPointsWithHit() {
if (absoluteEndPointsWithHit == null) {
absoluteEndPointsWithHit = relativeToAbsolute(points, Point::hasHit);
}
return absoluteEndPointsWithHit;
}

@Override
public List<Point> getRelativeEndPoints() {
return points;
}

@Override
public List<Point> getRelativeEndPointsWithHit() {
if (relativeEndPointsWithHit == null) {
relativeEndPointsWithHit = getRelativeEndPoints().stream().filter(Point::hasHit).collect(Collectors.toList());
}
return relativeEndPointsWithHit;
}

/**
* Transforms list of {@link Point}s in relative coordinates to absolute coordinates.
*
* @param relativeEndpoints end points in relative coordinates
* @return end points in absolute coordinates
*/
private List<Point> relativeToAbsolute(List<Point> relativeEndpoints, Predicate<Point> filter) {
return relativeEndpoints
.stream()
.filter(filter)
.map(point -> (Point) getRotation().multiply(new Point(point, point.getDistance(), point.getHitType())).add(getReference()))
.collect(Collectors.toList());
}
}
}
Loading
Loading