Created
June 14, 2023 10:14
-
-
Save ctrueden/1386c19877933b12350484b76bdb9744 to your computer and use it in GitHub Desktop.
Hacky way to check whether points of a point cloud lie within a mesh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.imglib2.mesh.alg; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.function.BiConsumer; | |
import net.imglib2.Localizable; | |
import net.imglib2.mesh.io.ply.PLYMeshIO; | |
import net.imglib2.mesh.obj.Mesh; | |
import net.imglib2.position.FunctionRandomAccessible; | |
import net.imglib2.type.logic.BitType; | |
import org.junit.Test; | |
public class MeshCursorTest | |
{ | |
public class Vector3f { | |
public float x, y, z; | |
public Vector3f(float x, float y, float z) { | |
this.x = x; this.y = y; this.z = z; | |
} | |
/** Wraps a localizable into a vector. */ | |
public Vector3f(Localizable loc) { | |
this(loc.getFloatPosition(0), loc.getFloatPosition(1), loc.getFloatPosition(2)); | |
} | |
public Vector3f round() { | |
return new Vector3f(Math.round(x), Math.round(y), Math.round(x)); | |
} | |
@Override | |
public boolean equals(Object other) { | |
if (!(other instanceof Vector3f)) return false; | |
Vector3f that = (Vector3f) other; | |
boolean same = this.x == that.x && this.y == that.y && this.z == that.z; | |
System.out.println(String.format("Checking %f %f %f =?= %f %f %f", this.x, this.y, this.z, that.x, that.y, that.z)); | |
return same; | |
} | |
} | |
@Test | |
public void testPointCloud() throws IOException { | |
// Generate a random point cloud. | |
// For testing, we use [0, 0, 0] - [1000, 1000, 1000] | |
// But this could be anything. In theory, this code won't explode if W x H x D > 2^31. | |
List<Vector3f> points = new ArrayList<>(); | |
points.add(new Vector3f(333, 444, 555)); | |
points.add(new Vector3f(333, 444, 555)); | |
points.add(new Vector3f(333, 444, 555)); | |
for (int i=0; i<1000000; i++) { | |
float x = (float) (1000 * Math.random()); | |
float y = (float) (1000 * Math.random()); | |
float z = (float) (1000 * Math.random()); | |
points.add(new Vector3f(x, y, z)); | |
} | |
// Smush our point cloud into a set of integer coordinates. | |
// This is the set of coordinates that have a point in the point cloud nearby. | |
// Where "nearby" means rounded to nearest integer. | |
// | |
// Before doing this, you could scale the cloud somehow to avoid precision issues... | |
// But we don't do it here. | |
// | |
// A confusing thing is that we reuse Vector3f for both the *real-space* point cloud, | |
// *and* the integer-space coordinates. Just because we are lazy. You could have | |
// a separate object for the integer triple if you wanted, for clarity. | |
// | |
// From Tobias: an alternative to this data structure would be to use the | |
// NearestNeighborSearchOnKDTree to power your function below; rather than | |
// set containment, you do an NN search. | |
HashSet<Vector3f> coords = new HashSet<>(); | |
for (int i=0; i<points.size(); i++) { | |
Vector3f point = points.get(i); | |
Vector3f coord = point.round(); | |
coords.add(coord); | |
} | |
// Now, we want an ImgLib2 image that is *mask* so we use BitType. | |
// We want this image to be backed by the set of coordinates, | |
// so that we don't have to actually populate a big array of anything. | |
// So we make an image backed by a function, which queries the coord set. | |
// The location is where we are (Localizable). The type is the output container. | |
BiConsumer <Localizable, BitType> function = (location, type) -> { | |
boolean b = coords.contains(new Vector3f(location)); | |
type.set(b); | |
}; | |
FunctionRandomAccessible<BitType> mask = new FunctionRandomAccessible<>(3, function, BitType::new); | |
// Read a mesh from disk. | |
Mesh mesh = PLYMeshIO.open("/home/curtis/data/3d/burkardt/ply/airplane.ply"); | |
// Sanity check the vertex coordinates. | |
// for (int v=0; v<mesh.vertices().size(); v++) { | |
// double x = mesh.vertices().x(v); | |
// double y = mesh.vertices().y(v); | |
// double z = mesh.vertices().z(v); | |
// System.out.println(x + "," + y + "," + z); | |
// } | |
// Try using MeshCursor. | |
double[] cal = {1, 1, 1}; | |
// Here is another way to make an ImgLib2 image, with *no* data. | |
// RandomAccessible<Void> nothing = ConstantUtils.constantRandomAccessible( null, 3 ); | |
// MeshCursor<Void> cursor = new MeshCursor<>(nothing.randomAccess(), mesh, cal); | |
MeshCursor<BitType> cursor = new MeshCursor<>(mask.randomAccess(), mesh, cal); | |
while (cursor.hasNext()) { | |
BitType t = cursor.next(); | |
long x = cursor.getLongPosition(0); | |
long y = cursor.getLongPosition(1); | |
long z = cursor.getLongPosition(2); | |
if (t.get()) { | |
// Congratulations, this integer coordinate has at least one real-space point nearby. | |
System.out.println(String.format("POINT: %d, %d, %d", x, y, z)); | |
} | |
else { | |
// This coordinate does not have a nearby point from the point cloud. | |
System.out.println(String.format("NOPE: %d, %d, %d", x, y, z)); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment