Skip to content

Commit

Permalink
Merge pull request #520 from saalfeldlab/fix/MeshWork
Browse files Browse the repository at this point in the history
Fix/mesh work
  • Loading branch information
cmhulbert authored Dec 15, 2023
2 parents 26a61f9 + 7859d71 commit 50ac3ae
Show file tree
Hide file tree
Showing 39 changed files with 1,046 additions and 1,033 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ canvases
*.class
*.java-version
*dockerfile
/meshes
14 changes: 12 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

<!-- kotlin -->
<kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
<kotlin.version>1.8.0</kotlin.version>
<kotlin.version>1.9.20</kotlin.version>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>

<!-- NB: Convert README.md to html during compilation. -->
Expand Down Expand Up @@ -88,6 +88,11 @@


<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>dev.dirs</groupId>
<artifactId>directories</artifactId>
Expand Down Expand Up @@ -151,6 +156,11 @@
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>io.github.oshai</groupId>
<artifactId>kotlin-logging-jvm</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.scijava</groupId>
<artifactId>scijava-common</artifactId>
Expand Down Expand Up @@ -541,7 +551,7 @@
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.6.4</version>
<version>1.7.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class ManagedMeshSettings {
public class ManagedMeshSettings<K> {

public static final String MESH_SETTINGS_KEY = "meshSettings";

Expand All @@ -35,9 +35,9 @@ public class ManagedMeshSettings {

private final MeshSettings globalSettings;

private final Map<Long, MeshSettings> individualSettings = new HashMap<>();
private final Map<K, MeshSettings> individualSettings = new HashMap<>();

private final HashMap<Long, SimpleBooleanProperty> isManagedProperties = new HashMap<>();
private final HashMap<K, SimpleBooleanProperty> isManagedProperties = new HashMap<>();

private final SimpleBooleanProperty isMeshListEnabled = new SimpleBooleanProperty(DEFAULT_IS_MESH_LIST_ENABLED);

Expand All @@ -53,7 +53,18 @@ public ManagedMeshSettings(final MeshSettings globalSettings) {
this.globalSettings = globalSettings;
}

public MeshSettings getOrAddMesh(final Long id, final boolean isManaged) {
/**
* Retrieves a MeshSettings object for the given id.
* If the mesh settings are managed, the existing managed settings will be returned.
* Otherwise, a new MeshSettings object will be created based on the global settings.
* <p>
* The resulting MeshSettings will be added to the `individualSettings` for this `id`, either `isManaged` or not.
*
* @param id The id of the mesh to retrieve the settings for.
* @param isManaged True if the individual mesh settings should be used.
* @return The mesh settings for the given id, or a new MeshSettings object if the id is not found.
*/
public MeshSettings getMeshSettings(final K id, final boolean isManaged) {

if (!isManagedProperties.containsKey(id)) {
final SimpleBooleanProperty isManagedProperty = new SimpleBooleanProperty(isManaged);
Expand All @@ -69,25 +80,44 @@ public MeshSettings getOrAddMesh(final Long id, final boolean isManaged) {
return individualSettings.get(id);
}

public MeshSettings getOrAddMesh(final Long id) {
/**
* Retrieves a MeshSettings for the given key.
* If the mesh settings are managed, then the managed settings will be returned.
* Otherwise, a new MeshSettings object will be created based on global settings.
*
* @param meshKey the key of the mesh to retrieve the settings for
* @return the mesh settings for the given mesh key, or a new MeshSettings object if the key is not found
*/
public MeshSettings getMeshSettings(final K meshKey) {

final var isManagedProperty = isManagedProperties.get(meshKey);

final boolean isManaged;
if (isManagedProperty != null)
isManaged = isManagedProperty.get();
else
isManaged = false;

if (!isManagedProperties.containsKey(id)) {
return new MeshSettings(globalSettings.getNumScaleLevels());
}
return individualSettings.get(id);
return getMeshSettings(meshKey, isManaged);
}

public MeshSettings getGlobalSettings() {

return globalSettings;
}

public boolean isPresent(final Long t) {
/**
* Checks if the given mesh key is managed.
*
* @param meshKey the mesh key
* @return true if the mesh key is managed, false otherwise
*/
public boolean isManaged(final K meshKey) {

return this.isManagedProperties.containsKey(t);
return this.isManagedProperties.containsKey(meshKey);
}

public BooleanProperty isManagedProperty(final Long t) {
public BooleanProperty isManagedProperty(final K t) {

return this.isManagedProperties.get(t);
}
Expand All @@ -108,9 +138,9 @@ public void clearSettings() {
this.individualSettings.clear();
}

public void keepOnlyMatching(final Predicate<Long> filter) {
public void keepOnlyMatching(final Predicate<K> filter) {

final Set<Long> toBeRemoved = isManagedProperties
final Set<K> toBeRemoved = isManagedProperties
.keySet()
.stream()
.filter(filter.negate())
Expand All @@ -125,16 +155,16 @@ public static Serializer jsonSerializer() {
return new Serializer();
}

public void set(final ManagedMeshSettings that) {
public void set(final ManagedMeshSettings<K> that) {

clearSettings();
globalSettings.setTo(that.globalSettings);
isMeshListEnabled.set(that.isMeshListEnabled.get());
meshesEnabled.set(that.meshesEnabled.get());
for (final Entry<Long, MeshSettings> entry : that.individualSettings.entrySet()) {
final Long id = entry.getKey();
for (final Entry<K, MeshSettings> entry : that.individualSettings.entrySet()) {
final K id = entry.getKey();
final boolean isManaged = that.isManagedProperties.get(id).get();
this.getOrAddMesh(id, isManaged);
this.getMeshSettings(id, isManaged);
if (!isManaged)
this.individualSettings.get(id).setTo(entry.getValue());
}
Expand Down Expand Up @@ -188,24 +218,26 @@ public ManagedMeshSettings deserialize(
if (!settingsMap.has(ID_KEY)) {
continue;
}
//TODO Caleb: This needs to store and deserialize based on generic key type, not just Long
// Necessary to share logic for meshes across segment and virtual sources
final Long id = context.deserialize(settingsMap.get(ID_KEY), Long.class);
final MeshSettings settings = Optional
.ofNullable(settingsMap.get(SETTINGS_KEY))
.map(el -> (MeshSettings)context.deserialize(el, MeshSettings.class))
.map(el -> (MeshSettings) context.deserialize(el, MeshSettings.class))
.orElseGet(globalSettings::copy);
final boolean isManaged = Optional
.ofNullable(settingsMap.get(IS_MANAGED_KEY))
.map(JsonElement::getAsBoolean)
.orElse(true);
LOG.debug("{} is managed? {}", id, isManaged);
if (!isManaged)
managedSettings.getOrAddMesh(id, false).setTo(settings);
managedSettings.getMeshSettings(id, false).setTo(settings);
else
managedSettings.getOrAddMesh(id, true);
managedSettings.getMeshSettings(id, true);
}
return managedSettings;
} catch (final Exception e) {
throw e instanceof JsonParseException ? (JsonParseException)e : new JsonParseException(e);
throw e instanceof JsonParseException ? (JsonParseException) e : new JsonParseException(e);
}
}

Expand All @@ -215,19 +247,22 @@ public JsonElement serialize(
final Type typeOfSrc,
final JsonSerializationContext context) {

//TODO Caleb: This also needs to serialize the generic type, instead of casting to Object
ManagedMeshSettings<Object> managedMeshSettings = (ManagedMeshSettings<Object>) src;

final JsonObject map = new JsonObject();
map.add(GLOBAL_SETTINGS_KEY, context.serialize(src.globalSettings));
map.add(GLOBAL_SETTINGS_KEY, context.serialize(managedMeshSettings.globalSettings));

if (DEFAULT_IS_MESH_LIST_ENABLED != src.isMeshListEnabledProperty().get())
map.addProperty(IS_MESH_LIST_ENABLED_KEY, src.isMeshListEnabledProperty().get());
if (DEFAULT_IS_MESH_LIST_ENABLED != managedMeshSettings.isMeshListEnabledProperty().get())
map.addProperty(IS_MESH_LIST_ENABLED_KEY, managedMeshSettings.isMeshListEnabledProperty().get());

if (DEFAULT_ARE_MESHES_ENABLED != src.getMeshesEnabledProperty().get())
map.addProperty(MESHES_ENABLED_KEY, src.getMeshesEnabledProperty().get());
if (DEFAULT_ARE_MESHES_ENABLED != managedMeshSettings.getMeshesEnabledProperty().get())
map.addProperty(MESHES_ENABLED_KEY, managedMeshSettings.getMeshesEnabledProperty().get());

final JsonArray meshSettingsList = new JsonArray();
for (final Entry<Long, MeshSettings> entry : src.individualSettings.entrySet()) {
final Long id = entry.getKey();
final Boolean isManaged = Optional.ofNullable(src.isManagedProperty(id)).map(BooleanProperty::get).orElse(true);
for (final Entry<?, MeshSettings> entry : managedMeshSettings.individualSettings.entrySet()) {
final Object id = entry.getKey();
final Boolean isManaged = Optional.ofNullable(managedMeshSettings.isManagedProperty(id)).map(BooleanProperty::get).orElse(true);
if (!isManaged) {
final JsonObject settingsMap = new JsonObject();
settingsMap.addProperty(IS_MANAGED_KEY, false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
package org.janelia.saalfeldlab.paintera.meshes;

import gnu.trove.list.array.TFloatArrayList;
import net.imglib2.FinalInterval;
import net.imglib2.Interval;
import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessible;
import net.imglib2.loops.LoopBuilder;
import net.imglib2.type.BooleanType;
import net.imglib2.util.Intervals;
import paintera.net.imglib2.view.BundleView;
import net.imglib2.view.IntervalView;
import net.imglib2.view.Views;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import paintera.net.imglib2.view.BundleView;

/**
* This class implements the marching cubes algorithm. Based on http://paulbourke.net/geometry/polygonise/
Expand Down Expand Up @@ -381,67 +377,59 @@ public float[] generateMesh() {
// This way, we need to remap the cube vertices.
// @formatter:on

final FinalInterval expandedInterval = new FinalInterval(
new long[]{
interval.min(0) - 1,
interval.min(1) - 1,
interval.min(2) - 1},
Intervals.maxAsLongArray(interval));

final TFloatArrayList vertices = new TFloatArrayList();
final float[][] interpolationPoints = new float[12][3];

final boolean[] vertexVals = new boolean[8];
final int[] idxRemap = new int[]{5, 7, 3, 1, 4, 6, 2, 0};
final long[] prevPos = new long[]{Long.MIN_VALUE, 0, 0};
final long[] curPos = new long[3];
final BundleView<B> bundledInput = new BundleView<>(Views.interval(input, interval));
final IntervalView<RandomAccess<B>> expandedBundle = Views.interval(bundledInput, expandedInterval);

LoopBuilder.setImages(expandedBundle).forEachPixel(pixelAccess -> {
pixelAccess.localize(curPos);
if (prevPos[0] != Long.MIN_VALUE && curPos[0] - prevPos[0] == 1 && curPos[1] - prevPos[1] == 0 && curPos[2] - prevPos[2] == 0) {
vertexVals[0] = vertexVals[1];
vertexVals[2] = vertexVals[3];
vertexVals[4] = vertexVals[5];
vertexVals[6] = vertexVals[7];
} else {
vertexVals[0] = pixelAccess.get().get();
pixelAccess.move(1, 1); //move to offset (0, 1, 0)
vertexVals[2] = pixelAccess.get().get();
pixelAccess.move(1, 2); //move to offset (0, 1, 1)
vertexVals[6] = pixelAccess.get().get();
pixelAccess.move(-1, 1); //move to offset (0, 0, 1)
vertexVals[4] = pixelAccess.get().get();
pixelAccess.move(-1, 2);
}
pixelAccess.move(1, 0); //move to offset (1, 0, 0)
vertexVals[1] = pixelAccess.get().get();
pixelAccess.move(1, 1); //move to offset (1, 1, 0)
vertexVals[3] = pixelAccess.get().get();
pixelAccess.move(1, 2); //move to offset (1, 1, 1)
vertexVals[7] = pixelAccess.get().get();
pixelAccess.move(-1, 1); //move to offset (1, 0, 1)
vertexVals[5] = pixelAccess.get().get();

/* setup for next loop*/
pixelAccess.setPosition(curPos);
System.arraycopy(curPos, 0, prevPos, 0, 3);

var vertexIn = 0b00000000;
for (int i = 0; i < vertexVals.length; i++) {
vertexIn |= (vertexVals[idxRemap[i]] ? 1 : 0) << i;
}

triangulation(
vertexIn,
curPos[0], //cursor0.getLongPosition(0),
curPos[1], //cursor0.getLongPosition(1),
curPos[2], //cursor0.getLongPosition(2),
vertices,
interpolationPoints
);
});
LoopBuilder.setImages(Views.interval(new BundleView<>(input), interval))
.forEachPixel(pixelAccess -> {
pixelAccess.localize(curPos);
if (prevPos[0] != Long.MIN_VALUE && curPos[0] - prevPos[0] == 1 && curPos[1] - prevPos[1] == 0 && curPos[2] - prevPos[2] == 0) {
vertexVals[0] = vertexVals[1];
vertexVals[2] = vertexVals[3];
vertexVals[4] = vertexVals[5];
vertexVals[6] = vertexVals[7];
} else {
vertexVals[0] = pixelAccess.get().get();
pixelAccess.move(1, 1); //move to offset (0, 1, 0)
vertexVals[2] = pixelAccess.get().get();
pixelAccess.move(1, 2); //move to offset (0, 1, 1)
vertexVals[6] = pixelAccess.get().get();
pixelAccess.move(-1, 1); //move to offset (0, 0, 1)
vertexVals[4] = pixelAccess.get().get();
pixelAccess.move(-1, 2);
}
pixelAccess.move(1, 0); //move to offset (1, 0, 0)
vertexVals[1] = pixelAccess.get().get();
pixelAccess.move(1, 1); //move to offset (1, 1, 0)
vertexVals[3] = pixelAccess.get().get();
pixelAccess.move(1, 2); //move to offset (1, 1, 1)
vertexVals[7] = pixelAccess.get().get();
pixelAccess.move(-1, 1); //move to offset (1, 0, 1)
vertexVals[5] = pixelAccess.get().get();

/* setup for next loop*/
pixelAccess.setPosition(curPos);
System.arraycopy(curPos, 0, prevPos, 0, 3);

var vertexIn = 0b00000000;
for (int i = 0; i < vertexVals.length; i++) {
vertexIn |= (vertexVals[idxRemap[i]] ? 1 : 0) << i;
}

triangulation(
vertexIn,
curPos[0],
curPos[1],
curPos[2],
vertices,
interpolationPoints
);
});
return vertices.toArray();
}

Expand Down
11 changes: 8 additions & 3 deletions src/main/java/org/janelia/saalfeldlab/paintera/meshes/Mesh.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ public FloatLocalizable(float[] position) {
* Of the form [ t1_v0_x, t1_v1_y, t1_v2_z, t2_v0_x, t2_v1_y, t2_v2_z, ..., tn_v0_x, tn_v1_y, tn_v2_z]
*/
public Mesh(final float[] flatTrianglesAndVertices, final Interval interval, final AffineTransform3D transform) {
this(flatTrianglesAndVertices, interval, transform, true);
}

public Mesh(final float[] flatTrianglesAndVertices, final Interval interval, final AffineTransform3D transform, final boolean overlap) {

assert flatTrianglesAndVertices.length % 9 == 0;

Expand All @@ -103,9 +107,10 @@ public Mesh(final float[] flatTrianglesAndVertices, final Interval interval, fin
final double minX = interval.min(0) - 1;
final double minZ = interval.min(2) - 1;

final double maxX = interval.max(0) + 1; // overlap 1
final double maxY = interval.max(1) + 1; // overlap 1
final double maxZ = interval.max(2) + 1; // overlap 1
final int overlapOffset = overlap ? 1 : 0;
final double maxX = interval.max(0) + overlapOffset;
final double maxY = interval.max(1) + overlapOffset;
final double maxZ = interval.max(2) + overlapOffset;

final RealInterval vertexBounds = new FinalRealInterval(
new double[]{minX, minY, minZ},
Expand Down
Loading

0 comments on commit 50ac3ae

Please sign in to comment.