diff --git a/Processing/nbproject/project.xml b/Processing/nbproject/project.xml index 55f4895f..d2b92ee4 100644 --- a/Processing/nbproject/project.xml +++ b/Processing/nbproject/project.xml @@ -115,6 +115,7 @@ cz.fidentis.gui cz.fidentis.processing.comparison.surfaceComparison + cz.fidentis.processing.decimation cz.fidentis.processing.exportProcessing cz.fidentis.processing.featurePoints cz.fidentis.processing.fileUtils diff --git a/Processing/src/cz/fidentis/processing/decimation/Edge.java b/Processing/src/cz/fidentis/processing/decimation/Edge.java new file mode 100644 index 00000000..ae14b79b --- /dev/null +++ b/Processing/src/cz/fidentis/processing/decimation/Edge.java @@ -0,0 +1,53 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package cz.fidentis.processing.decimation; + +/** + * Simple wrapper that identifies a valid pair for edge collapse. + * @author Marek Zuzi + */ +public class Edge { + private int from; + private int to; + + public Edge(int from, int to) { + this.from = from; + this.to = to; + } + + public int getFrom() { + return from; + } + + public int getTo() { + return to; + } + + @Override + public int hashCode() { + int hash = 5; + int addition = from + to; + hash = 73 * hash + addition; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Edge other = (Edge) obj; + return (this.from == other.from && this.to == other.to) || (this.from == other.to && this.to == other.from); + } + + +} diff --git a/Processing/src/cz/fidentis/processing/decimation/EdgeComparator.java b/Processing/src/cz/fidentis/processing/decimation/EdgeComparator.java new file mode 100644 index 00000000..26a843fa --- /dev/null +++ b/Processing/src/cz/fidentis/processing/decimation/EdgeComparator.java @@ -0,0 +1,40 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package cz.fidentis.processing.decimation; + +import java.util.Comparator; +import java.util.Map; + +/** + * + * @author Marek Zuzi + */ +public class EdgeComparator implements Comparator { + private static final double EPS = 0.00001d; + private Map weights; + + public EdgeComparator(Map weights) { + this.weights = weights; + } + + + + @Override + public int compare(Edge o1, Edge o2) { + double w1 = weights.get(o1); + double w2 = weights.get(o2); + + if(Math.abs(w1 - w2) < EPS) { + return 0; + } + + if(w1 < w1) { + return 1; + } else { + return -1; + } + } +} diff --git a/Processing/src/cz/fidentis/processing/decimation/QuadricEdgeCollapse.java b/Processing/src/cz/fidentis/processing/decimation/QuadricEdgeCollapse.java new file mode 100644 index 00000000..a6d577ce --- /dev/null +++ b/Processing/src/cz/fidentis/processing/decimation/QuadricEdgeCollapse.java @@ -0,0 +1,470 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package cz.fidentis.processing.decimation; + +import Jama.Matrix; +import cz.fidentis.model.Faces; +import cz.fidentis.model.Model; +import cz.fidentis.model.corner_table.Corner; +import cz.fidentis.model.corner_table.CornerTable; +import cz.fidentis.utils.MathUtils; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.PriorityQueue; +import javax.vecmath.Vector3f; +import javax.vecmath.Vector4d; + +/** + * + * @author Marek Zuzi + */ +public class QuadricEdgeCollapse { + private final Model model; + private float threshold = -1; + + private final HashMap quadrics = new HashMap<>(); + + private final HashSet pairs = new HashSet<>(); + private final HashMap pairTargets = new HashMap<>(); + private final HashMap pairErrors = new HashMap<>(); + + private PriorityQueue queue; + private final HashSet removedFaces = new HashSet<>(); + private final HashSet removedVerts = new HashSet<>(); + + private final HashMap> vertsToFaces = new HashMap<>(); + private final HashMap indexReorientations = new HashMap<>(); + + public QuadricEdgeCollapse(Model model) { + this.model = model; + } + + public void decimate(int targetVertexCount) { + while(decimateWhilePossible(targetVertexCount)) { + + } + } + + public boolean decimateWhilePossible(int targetVertexCount) { + // initialize variables and data structures needed to keep track of + // computation + initializeVariables(); + + // 1. determine quadrics for each vertex + initializeVertexQuadrics(); + // 2. consider all valid pairs + initializePairs(); + // 3. compute target V of pair and its error + initializeTargets(); + // 4. construct heap keyed by pair errors + initializeQueue(); + + // 5. iterate until desired number of vertices is removed + for(int i = model.getVerts().size() - targetVertexCount; i > 0; i--) { + Edge edgeToContract = getEdgeToContract(); + if(edgeToContract == null) { + break; + } + contractEdge(edgeToContract); + } + + // 6. clean up model to remove detached verices and faces + cleanupModel(); + + return queue.isEmpty(); + } + + public void setThreshold(float t) { + this.threshold = t; + } + + public Model getModel() { + return model; + } + + /** + * Initializes basic variables and structures needed during computation. + * Especially, clears sets of removed vertices and faces, index reorientation + * map which keeps track of collapsed pairs and builds a data structure to + * quickly find all faces incident with a given vertex, which is needed later. + */ + private void initializeVariables() { + removedFaces.clear(); + removedVerts.clear(); + indexReorientations.clear(); + + // initialize faces so that we could find faces to vertices quickly + vertsToFaces.clear(); + for(int i=0;i()); + } + } + for(int triangleIdx=0;triangleIdx 0.0f) { + for(int i=0;i(pairs.size(), c); + + for(Edge e : pairs) { + // get weight of this pair + double weight = computeWeight(e); + pairErrors.put(e, weight); + + // add it to priority queue keyed by weight + queue.add(e); + } + } + + /** + * Removes all vertices and faces that were detached during collapses. Also + * recompute normals. + */ + private void cleanupModel() { + ArrayList newVerts = new ArrayList<>(model.getVerts().size() - removedVerts.size()); + HashMap newMapping = new HashMap<>(model.getVerts().size() - removedVerts.size()); + + // copy only vertices that were not removed + for(int i=0;i> newFaces = new ArrayList<>(model.getFaces().getNumFaces() - removedFaces.size()); + for(int i=0;i newFace = new ArrayList<>(triangle.length); + for(int j=0;j