diff --git a/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java b/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java index a4ec60f28e493f4d41aa070506c088fa4418c2bb..192d585741e74fdc759076fe676272069eb0c70d 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java @@ -42,7 +42,6 @@ public class DistanceAction extends ControlPanelAction { private String heatmapDisplayed = DistancePanel.HEATMAP_HAUSDORFF_DISTANCE; private boolean weightedFPsShow = true; - private Map<MeshFacet, List<Double>> weightedHausdorffDistance = null; private FeaturePointType hoveredFeaturePoint = null; private final DistancePanel controlPanel; @@ -142,6 +141,7 @@ public class DistanceAction extends ControlPanelAction { getSecondaryDrawableFace().setRenderHeatmap(isHeatmapDisplayed()); } else { weightedFeaturePoints.hide(); + getSecondaryDrawableFace().clearHeatMapSaturation(); } }); topControlPanel.setSelectedComponent(controlPanel); // Focus Hausdorff distance panel @@ -220,27 +220,27 @@ public class DistanceAction extends ControlPanelAction { if (visitor == null) { calculateHausdorffDistance(featurePointTypes); - weightedHausdorffDistance = getWeightedDistance(); - // Update GUI elements that display the calculated Hausdorff distance metrics setFeaturePointWeigths(); setHausdorffDistanceStatistics(); } - final Map<MeshFacet, List<Double>> heatmap; switch (heatmapDisplayed) { case DistancePanel.HEATMAP_HAUSDORFF_DISTANCE: - heatmap = visitor.getDistances(); + getSecondaryDrawableFace().clearHeatMapSaturation(); break; case DistancePanel.HEATMAP_WEIGHTED_HAUSDORFF_DISTANCE: - heatmap = weightedHausdorffDistance; + getSecondaryDrawableFace().setHeatMapSaturation( + visitor.getMergedPriorities() + .get(getSecondaryDrawableFace().getHumanFace()) + ); break; case DistancePanel.HEATMAP_HIDE: return; default: throw new UnsupportedOperationException(heatmapDisplayed); } - getSecondaryDrawableFace().setHeatMap(heatmap); + getSecondaryDrawableFace().setHeatMap(visitor.getDistances()); } /** @@ -273,31 +273,6 @@ public class DistanceAction extends ControlPanelAction { getSecondaryDrawableFace().getHumanFace().accept(visitor); } - /** - * Calculates weighted Hausdorff distance of the face. - * - * @return weighted Hausdorff distance - */ - private Map<MeshFacet, List<Double>> getWeightedDistance() { - final Map<MeshFacet, List<Double>> weightedDistances = new HashMap<>(visitor.getDistances()); - final Map<MeshFacet, List<Double>> mergedPriorities = visitor.getMergedPriorities() - .get(getSecondaryDrawableFace().getHumanFace()); - - // Merge the map of distances with the map of priorities - for (final Map.Entry<MeshFacet, List<Double>> facetPriorities: mergedPriorities.entrySet()) { - weightedDistances.merge( - facetPriorities.getKey(), - facetPriorities.getValue(), - (distancesList, prioritiesList) -> - IntStream.range(0, distancesList.size()) - .mapToDouble(i -> distancesList.get(i) * prioritiesList.get(i)) - .boxed() - .collect(Collectors.toList())); - } - - return weightedDistances; - } - /** * Updates the GUI elements of {@link DistancePanel} that display * the weights of feature points used to calculate the weighted Hausdorff distance. @@ -331,7 +306,7 @@ public class DistanceAction extends ControlPanelAction { .flatMap(List::stream) .mapToDouble(Double::doubleValue) .summaryStatistics(), - weightedHausdorffDistance + getWeightedDistance() .values() .stream() .flatMap(List::stream) @@ -340,6 +315,31 @@ public class DistanceAction extends ControlPanelAction { ); } + /** + * Calculates weighted Hausdorff distance of the face. + * + * @return weighted Hausdorff distance + */ + private Map<MeshFacet, List<Double>> getWeightedDistance() { + final Map<MeshFacet, List<Double>> weightedDistances = new HashMap<>(visitor.getDistances()); + final Map<MeshFacet, List<Double>> mergedPriorities = visitor.getMergedPriorities() + .get(getSecondaryDrawableFace().getHumanFace()); + + // Merge the map of distances with the map of priorities + for (final Map.Entry<MeshFacet, List<Double>> facetPriorities: mergedPriorities.entrySet()) { + weightedDistances.merge( + facetPriorities.getKey(), + facetPriorities.getValue(), + (distancesList, prioritiesList) -> + IntStream.range(0, distancesList.size()) + .mapToDouble(i -> distancesList.get(i) * prioritiesList.get(i)) + .boxed() + .collect(Collectors.toList())); + } + + return weightedDistances; + } + /** * Returns {@code true} if the heatmap is displayed and {@code false} otherwise. * diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java index 84bc48f766b69b2444ca4fbc73f00815ee5744e0..3d1c97da453e6910f02ccc6cdec3068ee09b0810 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java @@ -13,6 +13,7 @@ import java.util.Map; * Drawable human face. * * @author Radek Oslejsek + * @author Daniel Schramm */ public class DrawableFace extends DrawableMesh { @@ -26,6 +27,10 @@ public class DrawableFace extends DrawableMesh { * Values at mesh vertices that are to be transferred to colors. */ private Map<MeshFacet, List<Double>> heatmap = new HashMap<>(); + /** + * Values at mesh vertices that determine the saturation level of heatmap colors. + */ + private Map<MeshFacet, List<Double>> heatmapSaturation = new HashMap<>(); /** * Constructor. @@ -49,6 +54,13 @@ public class DrawableFace extends DrawableMesh { this.heatmap = heatmap; } + /** + * Removes the heatmap from the face. + */ + public void clearHeatMap() { + heatmap = new HashMap<>(); + } + /** * Returns heatmap of the face * (values at mesh vertices that are to be transferred to colors). @@ -59,6 +71,32 @@ public class DrawableFace extends DrawableMesh { return Collections.unmodifiableMap(heatmap); } + /** + * Sets new map of heatmap colors saturation. + * + * @param heatmapSaturation New map of heatmap colors saturation + */ + public void setHeatMapSaturation(Map<MeshFacet, List<Double>> heatmapSaturation) { + this.heatmapSaturation = heatmapSaturation; + } + + /** + * Removes the map of heatmap colors saturation from the face. + */ + public void clearHeatMapSaturation() { + heatmapSaturation = new HashMap<>(); + } + + /** + * Returns map of heatmap colors saturation + * (values at mesh vertices that determine the saturation level of heatmap colors). + * + * @return Map of heatmap colors saturation + */ + public Map<MeshFacet, List<Double>> getHeatMapSaturation() { + return Collections.unmodifiableMap(heatmapSaturation); + } + /** * Returns the human face. * @@ -89,7 +127,7 @@ public class DrawableFace extends DrawableMesh { @Override protected void renderObject(GL2 gl) { if (isHeatmapRendered()) { - new HeatmapRenderer().drawMeshModel(gl, getModel(), heatmap); + new HeatmapRenderer().drawMeshModel(gl, getModel(), heatmap, heatmapSaturation); } else { super.renderObject(gl); } diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/HeatmapRenderer.java b/GUI/src/main/java/cz/fidentis/analyst/scene/HeatmapRenderer.java index 1a2802259edaaf062fc29620ce04b75ed64c22ae..015692b2bf614d9ec54f6a6d660f0032f7cae946 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/scene/HeatmapRenderer.java +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/HeatmapRenderer.java @@ -13,6 +13,7 @@ import javax.vecmath.Vector3d; * Heatmap rendering. * * @author Daniel Sokol + * @author Daniel Schramm */ public class HeatmapRenderer { @@ -32,22 +33,36 @@ public class HeatmapRenderer { * @param gl OpenGL context * @param model Mesh model to be rendered * @param distances Distances in mesh model vertices + * @param saturation Saturation of colors at mesh model vertices */ - public void drawMeshModel(GL2 gl, MeshModel model, Map<MeshFacet, List<Double>> distances) { - Double minDistance = Double.POSITIVE_INFINITY; - Double maxDistance = Double.NEGATIVE_INFINITY; + public void drawMeshModel(GL2 gl, MeshModel model, + Map<MeshFacet, List<Double>> distances, Map<MeshFacet, List<Double>> saturation) { + double minDistance = Double.POSITIVE_INFINITY; + double maxDistance = Double.NEGATIVE_INFINITY; for (MeshFacet f: model.getFacets()) { List<Double> distanceList = distances.get(f); if (distanceList != null) { - minDistance = Math.min(minDistance, distanceList.parallelStream().mapToDouble(Double::doubleValue).min().getAsDouble()); - maxDistance = Math.max(maxDistance, distanceList.parallelStream().mapToDouble(Double::doubleValue).max().getAsDouble()); + minDistance = Math.min( + minDistance, + distanceList.parallelStream() + .mapToDouble(Double::doubleValue) + .min() + .getAsDouble() + ); + maxDistance = Math.max( + maxDistance, + distanceList.parallelStream() + .mapToDouble(Double::doubleValue) + .max() + .getAsDouble() + ); } } for (MeshFacet f: model.getFacets()) { if (distances.containsKey(f)) { - renderMeshFacet(gl, f, distances.get(f), minDistance, maxDistance); + renderMeshFacet(gl, f, distances.get(f), saturation.get(f), minDistance, maxDistance); } } } @@ -57,23 +72,31 @@ public class HeatmapRenderer { * @param gl OpenGL context * @param facet Mesh facet * @param distancesList Distances in the mesh facet vertices + * @param saturationList Saturation of colors at mesh facet vertices. May be {@code null}. * @param minDistance Minimal distance threshold (smaller distances are cut-off) * @param maxDistance Maxim distance threshold (bigger distances are cut-off) */ - public void renderMeshFacet(GL2 gl, MeshFacet facet, List<Double> distancesList, Double minDistance, Double maxDistance) { + public void renderMeshFacet(GL2 gl, MeshFacet facet, List<Double> distancesList, + List<Double> saturationList, double minDistance, double maxDistance) { gl.glBegin(GL2.GL_TRIANGLES); //vertices are rendered as triangles // get the normal and tex coords indicies for face i for (int v = 0; v < facet.getCornerTable().getSize(); v++) { + int vertexIndex = facet.getCornerTable().getRow(v).getVertexIndex(); + // render the normals - Vector3d norm = facet.getVertices().get(facet.getCornerTable().getRow(v).getVertexIndex()).getNormal(); + Vector3d norm = facet.getVertices().get(vertexIndex).getNormal(); if (norm != null) { gl.glNormal3d(norm.x, norm.y, norm.z); } + // render the vertices - Point3d vert = facet.getVertices().get(facet.getCornerTable().getRow(v).getVertexIndex()).getPosition(); + Point3d vert = facet.getVertices().get(vertexIndex).getPosition(); + //get color of vertex - Color c = getColor(distancesList.get(facet.getCornerTable().getRow(v).getVertexIndex()), minDistance, maxDistance); + double currentDistance = distancesList.get(vertexIndex); + double currentSaturation = saturationList == null ? 1d : saturationList.get(vertexIndex); + Color c = getColor(currentDistance, currentSaturation, minDistance, maxDistance); gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE, c.getComponents(null), 0); gl.glVertex3d(vert.x, vert.y, vert.z); @@ -82,7 +105,7 @@ public class HeatmapRenderer { gl.glPopAttrib(); } - private Color getColor(Double currentDistance, Double minDistance, Double maxDistance) { + private Color getColor(double currentDistance, double currentSaturation, double minDistance, double maxDistance) { double currentParameter = ((currentDistance - minDistance) / (maxDistance - minDistance)); float[] hsb1 = Color.RGBtoHSB(minColor.getRed(), minColor.getGreen(), minColor.getBlue(), null); @@ -122,7 +145,7 @@ public class HeatmapRenderer { hue = hue - 1; } - float saturation = (float) ((1 - currentParameter) * s1 + currentParameter * s2); + float saturation = (float) (((1 - currentParameter) * s1 + currentParameter * s2) * currentSaturation); float brightness = (float) ((1 - currentParameter) * b1 + currentParameter * b2); return Color.getHSBColor(hue, saturation, brightness);