/* * Copyright (C) 2010-2014 Mathias Unberath * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */ package edu.stanford.rsl.conrad.geometry.shapes.mesh; import java.util.ArrayList; import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND; import edu.stanford.rsl.conrad.geometry.splines.UniformCubicBSpline; import edu.stanford.rsl.conrad.numerics.SimpleMatrix; import edu.stanford.rsl.conrad.numerics.SimpleVector; import edu.stanford.rsl.conrad.utils.VisualizationUtil; import edu.stanford.rsl.tutorial.motion.estimation.EstimateCubic2DSpline; /** * This class represents a time varying mesh. The time variation is described as splines so that each time-point can be * sampled in a smooth fashion. * @author Mathias Unberath * */ public class Mesh4D { /** * The ArrayList containing the mesh's vertices corresponding to the phases. */ private ArrayList<SimpleMatrix> meshes; /** * The ArrayList containing the time-resolving splines for each vertex. */ private ArrayList<UniformCubicBSpline> splines; /** * The number of meshes which is assumed equal to the phases, i.e. sampled time-steps. */ private int numMeshes; /** * Dimension of the vertices; */ private int dimension; /** * The number of vertices in the mesh. */ private int numPoints; /** * The number of connections in the connectivity information, e.g. triangles. */ private int numConnections; /** * The matrix containing the connectivity information. */ private SimpleMatrix triangles; //========================================================================================== // METHODS //========================================================================================== /** * Constructs the object and initializes the class members. */ public Mesh4D(){ this.meshes = new ArrayList<SimpleMatrix>(); this.splines = new ArrayList<UniformCubicBSpline>(); this.numMeshes = 0; } /** * Adds a mesh's vertices to the ArrayList and increments the counter. * When the first mesh is added, all necessary class members are set. * @param m The mesh to add. */ public void addMesh(Mesh m){ if(this.numMeshes == 0){ this.triangles = m.getConnectivity(); this.numConnections = m.numConnections; this.dimension = m.dimension; this.numPoints = m.numPoints; }else{ assert(this.numConnections == m.numConnections) : new IllegalArgumentException("Input mesh does not fit the meshes already present."); assert(this.dimension == m.dimension) : new IllegalArgumentException("Input mesh does not fit the meshes already present."); assert(this.numPoints == m.numPoints) : new IllegalArgumentException("Input mesh does not fit the meshes already present."); } meshes.add(m.getPoints()); numMeshes++; } /** * Calculates the spline curves describing the vertex motion and stores them in the class member. */ public void calculateSplines(){ for(int i = 0; i < numPoints; i++){ ArrayList<PointND> points = new ArrayList<PointND>(); for(int j = 0; j < numMeshes; j++){ points.add(new PointND(meshes.get(j).getRow(i).copyAsDoubleArray())); //System.out.println(meshes.get(j).getRow(i).getElement(0) + " "+meshes.get(j).getRow(i).getElement(1)+" "+meshes.get(j).getRow(i).getElement(2)); } points.add(points.get(0)); EstimateCubic2DSpline fs = new EstimateCubic2DSpline(points); UniformCubicBSpline bs = fs.estimateUniformCubic(points.size()-3-1); //eval(bs,points); this.splines.add(bs); } } /** * Method to evaluate the quality of a spline fit. * @param bs The spline to be evaluated * @param points The points that decide the quality. */ @SuppressWarnings("unused") private void eval(UniformCubicBSpline bs, ArrayList<PointND> points){ double err = 0; for(int i = 0; i < points.size(); i++){ double kj = (float)i/(points.size()-1); SimpleVector res = new SimpleVector(bs.evaluate(kj).getAbstractVector()); res.subtract(points.get(i).getAbstractVector()); err += res.normL2(); System.out.println(res.getElement(0) + " " + res.getElement(1) + " " + res.getElement(2) + " " +err); } VisualizationUtil.createSplinePlot(bs).show(); } /** * Samples the time-variant splines list at a certain time t = [0,1] and returns the corresponding mesh. * @param time The time to be sampled. * @return The mesh corresponding to the time. */ public Mesh evaluateSplines(double time){ Mesh m = new Mesh(); m.setConnectivity(this.triangles); SimpleMatrix pts = new SimpleMatrix(numPoints, dimension); for(int i = 0; i < numPoints; i++){ pts.setRowValue(i, new SimpleVector(splines.get(i).evalFast(time))); } m.setPoints(pts); return m; } /** * Samples the time variant meshes at a certain time t=[0,1] and returns the corresponding mesh. * Sampling is done using linear interpolation. * @param time * @return */ public Mesh evaluateLinearInterpolation(double time){ Mesh m = new Mesh(); m.setConnectivity(this.triangles); int low = (int)Math.floor(time * numMeshes); int high; if(low == numMeshes-1){ high = 0; }else{ high = low + 1; } double key = time*numMeshes - low; SimpleMatrix pts = new SimpleMatrix(numPoints, dimension); for(int i = 0; i < numPoints; i++){ pts.setRowValue(i, interp(meshes.get(low).getRow(i), meshes.get(high).getRow(i), key)); } m.setPoints(pts); return m; } /** * Linearly interpolates between two simplevectors. * @param low Floor of interp. * @param high Ceil of interp. * @param key Value of interp. * @return The interpolated SimpleVector. */ private SimpleVector interp(SimpleVector low, SimpleVector high, double key){ SimpleVector res = new SimpleVector(low.getLen()); for(int i = 0; i < low.getLen(); i++){ double val = (1-key)*low.getElement(i) + key*high.getElement(i); res.setElementValue(i, val); } return res; } /** * Getter for the connectivity. * @return The connectivity. */ public SimpleMatrix getFaces(){ return this.triangles; } }