Commit 7b57c496 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Merge branch '104-better-color-mapping-for-the-whd-heatmap' into 'master'

Resolve "Better color mapping for the WHD heatmap"

Closes #104

See merge request grp-fidentis/analyst2!112
parents 692a363c 682bcfd1
Loading
Loading
Loading
Loading
+33 −33
Original line number Diff line number Diff line
@@ -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.
     * 
+39 −1
Original line number Diff line number Diff line
@@ -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);
        }
+35 −12
Original line number Diff line number Diff line
@@ -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);