Commit 63c7e98b authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Merge branch '46-refactoring-refactor-and-merge-hausdorff-distance' into 'master'

Resolve "Refactoring: Refactor and merge Hausdorff distance"

Closes #46

See merge request grp-fidentis/analyst2!46
parents a9e66469 33dd5279
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
package cz.fidentis.analyst.mesh;
package cz.fidentis.analyst;

import cz.fidentis.analyst.mesh.core.MeshModel;
import cz.fidentis.analyst.mesh.io.MeshObjLoader;
+0 −39
Original line number Diff line number Diff line
package cz.fidentis.analyst.comparison;

import cz.fidentis.analyst.mesh.core.MeshPoint;

/**
 * 
 * @author Matej Lukes
 */
public class ClosestVertices {
    
    private MeshPoint firstVertex;
    private MeshPoint secondVertex;
    private double distance;

    /**
     * Constructor.
     * 
     * @param firstVertex Firt vertex of the pair
     * @param secondVertex Second vertex of the pair
     * @param distance Distance
     */
    public ClosestVertices(MeshPoint firstVertex, MeshPoint secondVertex, double distance) {
        this.firstVertex = firstVertex;
        this.secondVertex = secondVertex;
        this.distance = distance;
    }

    public MeshPoint getFirstVertex() {
        return firstVertex;
    }

    public MeshPoint getSecondVertex() {
        return secondVertex;
    }

    public double getDistance() {
        return distance;
    }
}
+0 −95
Original line number Diff line number Diff line
package cz.fidentis.analyst.comparison;

import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.mesh.io.MeshObjLoader;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * @author Matej Lukes
 */
public class Comparison {
    private HausdorffDistance hausdorffDistance;

    private MeshFacet mainFacet;
    private MeshFacet comparedFacet;

    /**
     * Asynchronously loads main meshModel
     *
     * @param path path to meshModel file
     * @return CompletableFuture
     */
    public CompletableFuture loadMainModel(String path) {

        return CompletableFuture.runAsync(() -> {
            try {
                mainFacet = MeshObjLoader.read(new File(path)).getFacets().get(1);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * Asynchronously loads compared meshModel
     *
     * @param path path to meshModel file
     * @return CompletableFuture
     */
    public CompletableFuture loadComparedModel(String path) {
        return CompletableFuture.runAsync(() -> {
            try {
                comparedFacet = MeshObjLoader.read(new File(path)).getFacets().get(1);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * Asynchronously registers compared meshFacet to main meshFacet
     *
     * @param method registration method
     * @return CompletableFuture
     */
    public CompletableFuture register(RegistrationMethod method) {
        return CompletableFuture.runAsync(() -> comparedFacet = Registration
                .register(mainFacet, comparedFacet, method));
    }

    /**
     * Asynchronously compares MeshFacets from vertices to vertices
     *
     * @return list containing vertex from first facet, closest vertex to it from second facet, distance
     */
    public CompletableFuture<List<ClosestVertices>> compareHausdorffDistanceToVertices() {
        hausdorffDistance = new HausdorffDistance(mainFacet, comparedFacet);
        return CompletableFuture.supplyAsync(() -> hausdorffDistance.calculateHausdorffDistanceToVertices());
    }

    /**
     * Asynchronously compares MeshFacets from vertices to any point on mesh
     *
     * @return list containing vertex from first facet, closest point to it from second facet, distance
     */
    public CompletableFuture<List<ClosestVertices>> compareHausdorffDistanceToMesh() {
        hausdorffDistance = new HausdorffDistance(mainFacet, comparedFacet);
        return CompletableFuture.supplyAsync(() -> hausdorffDistance.calculateHausdorffDistanceToMesh());
    }

    /**
     * returns progress percentage
     *
     * @return progress percentage
     */
    public double getComparisonProgress() {
        if (hausdorffDistance == null) {
            return -1;
        }
        return hausdorffDistance.getProgressPercentage();
    }
}
+0 −359
Original line number Diff line number Diff line
package cz.fidentis.analyst.comparison;

import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.mesh.core.MeshPoint;
import cz.fidentis.analyst.mesh.core.MeshPointImpl;
import cz.fidentis.analyst.mesh.core.MeshTriangle;
import cz.fidentis.analyst.mesh.visitors.TriangleListVisitor;

import javax.vecmath.Vector3d;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author Matej Lukes
 */
public class HausdorffDistance {
    
    private MeshFacet mainFacet;
    private MeshFacet comparedFacet;

    private AtomicInteger progress = new AtomicInteger();
    private int numberOfVertices;

    /**
     * @param mainFacet     main MeshFacet
     * @param comparedFacet compared MeshFacet
     */
    public HausdorffDistance(MeshFacet mainFacet, MeshFacet comparedFacet) {
        this.mainFacet = mainFacet;
        this.comparedFacet = comparedFacet;
        this.numberOfVertices = mainFacet.getNumberOfVertices();
    }

    /**
     * returns progress percentage
     *
     * @return progress
     */
    public double getProgressPercentage() {
        return ((double) progress.get() / numberOfVertices) * 100;
    }

    /**
     * Finds the nearest vertex on the second facet.
     *
     * @param vertex vertex from
     * @return vertex, nearest vertex from second facet, distance
     */
    private ClosestVertices getNearestVertex(MeshPoint vertex) {
        Optional<Pair<MeshPoint, Double>> closestVertexAndDistance = comparedFacet.getVertices().parallelStream()
                .map((meshPoint) -> new Pair<>(meshPoint, getDistanceBetweenPoints(vertex, meshPoint.getPosition())))
                .max((Comparator.comparingDouble(Pair::getValue)));
        return closestVertexAndDistance.map(vector3dDoublePair -> new ClosestVertices(vertex,
                vector3dDoublePair.getKey(),
                vector3dDoublePair.getValue())).orElse(null);
    }

    /**
     * returns distance between two points
     *
     * @param point1 first point
     * @param point2 second point
     * @return distance
     */
    private double getDistanceBetweenPoints(MeshPoint point1, Vector3d point2) {
        Vector3d helperVector = new Vector3d();
        helperVector.sub(point1.getPosition(), point2);
        return Math.signum(helperVector.dot(point1.getNormal())) * helperVector.length();
    }

    /**
     * calculates Hausdorff Distance to the nearest vertex from second facet for each vertex in first facet
     * this implementation uses executor
     *
     * @return list containing vertex from first facet, closest vertex to it from second facet, distance
     */
    public List<ClosestVertices> calculateHausdorffDistanceToVertices() {
        progress.set(0);
        int numberOfVertices = mainFacet.getNumberOfVertices();
        List<Future<ClosestVertices>> closestVerticesFutures = new ArrayList<>(numberOfVertices);
        ExecutorService executor = Executors.newCachedThreadPool();

        for (final MeshPoint vertex : mainFacet.getVertices()) {
            closestVerticesFutures.add(executor.submit(() -> {
                ClosestVertices result = getNearestVertex(vertex);
                progress.addAndGet(1);
                return result;
            }));
        }

        List<ClosestVertices> closestVertices = new ArrayList<>(numberOfVertices);
        for (Future<ClosestVertices> future :
                closestVerticesFutures) {
            executor.submit(() -> {
                try {
                    ClosestVertices result = future.get();
                    synchronized (closestVertices) {
                        closestVertices.add(result);
                    }
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown();
        return closestVertices;
    }

    /**
     * calculates Hausdorff Distance to the nearest vertex from second facet for each vertex in first facet
     * this implementation uses parallel streams
     *
     * @return list containing vertex from first facet, closest vertex to it from second facet, distance
     */
    public List<ClosestVertices> calculateHausdorffDistanceToVertices2() {
        progress.set(0);
        return mainFacet.getVertices().parallelStream()
                .map((vertex) -> {
                    ClosestVertices result = getNearestVertex(vertex);
                    progress.addAndGet(1);
                    return result;
                })
                .collect(Collectors.toList());
    }

    /**
     * calculates Hausdorff Distance to the nearest point on second facet for each vertex
     * this implementation uses executor
     *
     * @return list containing vertex from first facet, closest point to it from second facet, distance
     */
    public List<ClosestVertices> calculateHausdorffDistanceToMesh() {
        progress.set(0);
        int numberOfVertices = mainFacet.getNumberOfVertices();
        List<Future<ClosestVertices>> closestPointsFutures = new ArrayList<>(numberOfVertices);
        ExecutorService executor = Executors.newCachedThreadPool();

        for (final MeshPoint vertex : mainFacet.getVertices()) {
            closestPointsFutures.add(executor.submit(() -> {
                ClosestVertices result = calculateNearestPointOnMesh(vertex,
                        comparedFacet.getCornerTable()
                                .getTriangleIndexesByVertexIndex(comparedFacet.getVertices()
                                        .indexOf(getNearestVertex(vertex)
                                                .getSecondVertex())));
                progress.addAndGet(1);
                return result;
            }));
        }

        List<ClosestVertices> closestVertices = new ArrayList<>(numberOfVertices);
        for (Future<ClosestVertices> future : closestPointsFutures) {
            executor.submit(() -> {
                try {
                    ClosestVertices result = future.get();
                    synchronized (closestVertices) {
                        closestVertices.add(result);
                    }
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown();
        return closestVertices;
    }

    /**
     * calculates Hausdorff Distance to the nearest point on second facet for each vertex
     * this implementation uses parallel streams
     *
     * @return list containing vertex from first facet, closest point to it from second facet, distance
     */
    public List<ClosestVertices> calculateHausdorffDistanceToMesh2() {
        progress.set(0);
        return mainFacet.getVertices().parallelStream()
                .map((meshPoint) -> {
                    ClosestVertices result = calculateNearestPointOnMesh(meshPoint,
                            comparedFacet.getCornerTable()
                                    .getTriangleIndexesByVertexIndex(comparedFacet.getVertices()
                                            .indexOf(getNearestVertex(meshPoint)
                                                    .getSecondVertex())));
                    progress.addAndGet(1);
                    return result;
                }).collect(Collectors.toList());
    }

    /**
     * calculates Hausdorff Distance to the nearest point on second facet for vertex
     *
     * @param vertex                     vertex from first facet
     * @param indicesOfTrianglesOfVertex indices of triangles that contain the nearest vertex on second mesh
     * @return vertex from first facet, closest point to it from second facet, distance
     */
    private ClosestVertices calculateNearestPointOnMesh(MeshPoint vertex, List<Integer> indicesOfTrianglesOfVertex) {
        Vector3d vertexPosition = vertex.getPosition();
        List<Pair<Vector3d, Double>> projections = new ArrayList<>(indicesOfTrianglesOfVertex.size());
        Vector3d helperVector = new Vector3d();

        TriangleListVisitor visitor = new TriangleListVisitor();
        comparedFacet.accept(visitor);
        List<MeshTriangle> trList = visitor.getTriangles();
        for (int index : indicesOfTrianglesOfVertex) {
            List<Vector3d> triangle = new ArrayList<>();
            triangle.add(trList.get(index).vertex1.getPosition());
            triangle.add(trList.get(index).vertex2.getPosition());
            triangle.add(trList.get(index).vertex3.getPosition());
            //List<Vector3d> triangle = comparedFacet.asTriangles()..getVerticesOfTriangle(index).stream()
            //        .map(MeshPoint::getPosition)
            //        .collect(Collectors.toList());
            Vector3d projection = getProjectionToTrianglePlane(vertexPosition, triangle);
            if (isPointInTriangle(projection, triangle)) {
                helperVector.sub(vertexPosition, projection);
                projections.add(new Pair<>(projection, helperVector.length()));
            } else {
                projection = getProjectionToClosestEdge(projection, triangle);
                helperVector.sub(vertexPosition, projection);
                projections.add(new Pair<>(projection, helperVector.length()));
            }
        }

        Pair<Vector3d, Double> closestPosition = projections.stream()
                .min(Comparator.comparingDouble(Pair::getValue)).orElseGet(() -> new Pair<>(null, Double.MAX_VALUE));
        return new ClosestVertices(vertex,
                new MeshPointImpl(closestPosition.getKey(), null, null),
                closestPosition.getValue());
    }

    /**
     * returns perpendicular projection from vertex to plane of triangle
     *
     * @param vertex   vertex from which the projection is created
     * @param triangle triangle that defines the plane
     * @return projection to plane of triangle
     */
    private Vector3d getProjectionToTrianglePlane(Vector3d vertex, List<Vector3d> triangle) {
        Vector3d ab = new Vector3d();
        ab.sub(triangle.get(0), triangle.get(1));
        Vector3d ac = new Vector3d();
        ac.sub(triangle.get(0), triangle.get(2));
        Vector3d normal = new Vector3d();
        normal.cross(ab, ac);
        normal.normalize();

        Vector3d helperVector = new Vector3d(vertex);
        helperVector.sub(triangle.get(0));
        double distance = helperVector.dot(normal);
        helperVector.scaleAdd(-distance, normal, helperVector);
        return helperVector;
    }

    /**
     * checks if a point in plane of triangle lies within the triangle
     *
     * @param point    checked point
     * @param triangle triangle
     * @return true if point is in triangle, false otherwise
     */
    private boolean isPointInTriangle(Vector3d point, List<Vector3d> triangle) {
        List<Vector3d> pointToVertices = triangle.stream()
                .map((vertex) -> {
                    Vector3d v = new Vector3d(vertex);
                    v.sub(point);
                    return v;
                }).collect(Collectors.toList());

        double angleSum = 0;
        for (int i = 0; i < 3; i++) {
            angleSum += pointToVertices.get(i).angle(pointToVertices.get((i + 1) % 3));
        }
        angleSum -= Math.PI;
        return -0.01 < angleSum && angleSum < 0.01;
    }

    /**
     * returns projection to to the nearest edge of triangle
     *
     * @param point    point in plane of triangle
     * @param triangle triangle
     * @return perpendicular projection to the nearest edge
     */
    private Vector3d getProjectionToClosestEdge(Vector3d point, List<Vector3d> triangle) {
        Vector3d[] projections = new Vector3d[3];
        for (int i = 0; i < 3; i++) {
            projections[i] = getProjectionToEdge(point, triangle.get(i), triangle.get((i + 1) % 3));
        }

        double minDistance = Double.MAX_VALUE;
        Vector3d closestProjection = null;
        Vector3d helperVector = new Vector3d();
        for (Vector3d projection :
                projections) {
            helperVector.sub(point, projection);
            double distance = helperVector.length();
            if (distance < minDistance) {
                minDistance = distance;
                closestProjection = projection;
            }
        }
        return closestProjection;
    }

    /**
     * returns projection to edge
     *
     * @param point       point in plane of triangle
     * @param edgeVertex1 first vertex of edge
     * @param edgeVertex2 second vertex of edge
     * @return projection to edge
     */
    private Vector3d getProjectionToEdge(Vector3d point, Vector3d edgeVertex1, Vector3d edgeVertex2) {
        Vector3d ab = new Vector3d();
        ab.sub(edgeVertex1, edgeVertex2);
        Vector3d ap = new Vector3d();
        ap.sub(edgeVertex1, point);
        double t = ab.dot(ap) / ab.lengthSquared();
        return new Vector3d(edgeVertex1.x + t * ab.x, edgeVertex1.y + t * ab.y, edgeVertex1.z + t * ab.z);
    }
    
    
    /**
     * Helper class for pairs.
     * 
     * @param <K> key 
     * @param <V> value
     * @author Radek Oslejsek
     */
    private final class Pair<K,V> {
        private final K key;
        private final V value;
       
        /**
         * Constructor.
         * @param key key
         * @param value value
         */
        private Pair(K key, V value) {
            this.key = key;
            this.value = value;
        }
        
        public K getKey() {
            return key;
        }
        
        public V getValue() {
            return value;
        }
    }
}
+0 −27
Original line number Diff line number Diff line
package cz.fidentis.analyst.comparison;

import cz.fidentis.analyst.mesh.core.MeshFacet;

/**
 * 
 * @author Matej Lukes
 */
public class Registration {

    /**
     * Heler method - TO DO
     * 
     * @param facet main facet
     * @param registeredFacet refistered facet
     * @param method registration method
     * @return TO DO
     */
    public static MeshFacet register(MeshFacet facet, MeshFacet registeredFacet, RegistrationMethod method) {
        switch (method) {
            case NO_REGISTRATION:
                return registeredFacet;
            default:
                return null;
        }
    }
}
Loading