Skip to content
Snippets Groups Projects
Commit 813aeb3e authored by Matej Kovár's avatar Matej Kovár
Browse files

Merge branch 'master' into 103-list-of-faces-in-project

parents 17bf3917 e6d35d70
No related branches found
No related tags found
No related merge requests found
......@@ -21,8 +21,9 @@ import java.util.stream.Collector;
* @param <T> Data type of the stream elements
*/
public class WeightedAverageCollector<T> implements Collector<T, IntemediateResults, Double> {
private final ToDoubleFunction<? super T> valueFunction, weightFunction;
private final ToDoubleFunction<? super T> valueFunction;
private final ToDoubleFunction<? super T> weightFunction;
/**
* Constructor.
......@@ -48,7 +49,7 @@ public class WeightedAverageCollector<T> implements Collector<T, IntemediateResu
public static <T> Collector<T, ?, Double> toWeightedAverage(
ToDoubleFunction<? super T> valueFunction,
ToDoubleFunction<? super T> weightFunction) {
return new WeightedAverageCollector(valueFunction, weightFunction);
return new WeightedAverageCollector<>(valueFunction, weightFunction);
}
/**
......@@ -69,9 +70,9 @@ public class WeightedAverageCollector<T> implements Collector<T, IntemediateResu
@Override
public BiConsumer<IntemediateResults, T> accumulator() {
return (iResult, streamElement) -> {
iResult.weightedValSum += valueFunction.applyAsDouble(streamElement)
* weightFunction.applyAsDouble(streamElement);
iResult.weightSum += weightFunction.applyAsDouble(streamElement);
iResult.addWeightedValSum(valueFunction.applyAsDouble(streamElement)
* weightFunction.applyAsDouble(streamElement));
iResult.addWeightSum(weightFunction.applyAsDouble(streamElement));
};
}
......@@ -86,8 +87,8 @@ public class WeightedAverageCollector<T> implements Collector<T, IntemediateResu
@Override
public BinaryOperator<IntemediateResults> combiner() {
return (iResult1, iResult2) -> {
iResult1.weightedValSum += iResult2.weightedValSum;
iResult1.weightSum += iResult2.weightSum;
iResult1.addWeightedValSum(iResult2.getWeightedValSum());
iResult1.addWeightSum(iResult2.getWeightSum());
return iResult1;
};
......@@ -108,7 +109,7 @@ public class WeightedAverageCollector<T> implements Collector<T, IntemediateResu
*/
@Override
public Function<IntemediateResults, Double> finisher() {
return iResult -> iResult.weightedValSum / iResult.weightSum;
return iResult -> iResult.getWeightedValSum() / iResult.getWeightSum();
}
/**
......@@ -130,6 +131,22 @@ public class WeightedAverageCollector<T> implements Collector<T, IntemediateResu
* @author Daniel Schramm
*/
class IntemediateResults {
double weightedValSum = 0;
double weightSum = 0;
private double weightedValSum = 0;
private double weightSum = 0;
public double getWeightedValSum() {
return weightedValSum;
}
public double getWeightSum() {
return weightSum;
}
public void addWeightedValSum(double weightedValSum) {
this.weightedValSum += weightedValSum;
}
public void addWeightSum(double weightSum) {
this.weightSum += weightSum;
}
}
package cz.fidentis.analyst.core;
/**
* Class holding the upper and lower bounds of an interval.
*
* @author Daniel Schramm
*/
public class IntValueRange {
private final int minimum, maximum;
/**
* Constructor.
*
* @param minimum The lower bound of the interval
* @param maximum The upper bound of the interval
* @throws IllegalArgumentException if the {@code minimum} is greater than the {@code maximum}
*/
public IntValueRange(int minimum, int maximum) {
if (minimum > maximum) {
throw new IllegalArgumentException("Minimum value must be less or equal to maximum value");
}
this.minimum = minimum;
this.maximum = maximum;
}
/**
* Returns the lower bound of the interval.
*
* @return Lower bound of the interval
*/
public int getMinimum() {
return minimum;
}
/**
* Returns the upper bound of the interval.
*
* @return Upper bound of the interval
*/
public int getMaximum() {
return maximum;
}
}
......@@ -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.
*
......
......@@ -138,9 +138,24 @@ public class DistancePanel extends ControlPanel {
});
featurePointCheckBoxes.add(checkBox);
final JTextField sliderInput = fpBuilder.addSliderOptionLine(null, null, 100, null);
final JTextField sliderInput = fpBuilder.addSliderButtonedWithVal(100, 1, null);
sliderInput.setText(ControlPanelBuilder.doubleToStringLocale(DrawableFeaturePoints.DEFAULT_SIZE));
sliderInput.postActionEvent(); // Set correct position of slider
sliderInput.addActionListener((ActionEvent ae) -> {
if (!checkBox.isSelected()) {
return; // Recompute only if the feature point is selected
}
if (ControlPanelBuilder.TEXT_FIELD_BUTTON_PRESSED_MINUS.equals(ae.getActionCommand())
|| ControlPanelBuilder.TEXT_FIELD_BUTTON_PRESSED_PLUS.equals(ae.getActionCommand())) {
// Plus/minus button was pressed -> recompute
action.actionPerformed(new ActionEvent(
ae.getSource(),
ActionEvent.ACTION_PERFORMED,
ACTION_COMMAND_DISTANCE_RECOMPUTE
));
}
});
sliderInput.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_RESIZE, i));
// Modify listener of the ENTER key press
final Object enterKeyAction = sliderInput.getInputMap()
......
......@@ -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);
}
......
......@@ -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);
......
......@@ -6,7 +6,7 @@ This project is being developed at Masaryk University, Brno, as collaborative pr
## Requirements
* Oracle JDK 11 (JOGL 2.3 does not work with JDK >11 properly, at leat at Linux; should be working again in ongoing JOGL 2.4)
* [Oracle JDK 11](https://www.oracle.com/java/technologies/downloads/#java11) (JOGL 2.3 does not work with JDK >11 properly, at leat at Linux; should be working again in ongoing JOGL 2.4)
* [Maven](https://www.mkyong.com/maven/how-to-install-maven-in-windows/) for compilation
* IDE for development, e.g. NetBeans or IntelliJ IDEA
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment