Model.java
Go to the documentation of this file.
00001 /*******************************************************************************
00002  * Copyright (c) 2012 Stefan Profanter. All rights reserved. This program and the accompanying
00003  * materials are made available under the terms of the GNU Public License v3.0 which accompanies
00004  * this distribution, and is available at http://www.gnu.org/licenses/gpl.html
00005  * 
00006  * Contributors: Stefan Profanter - initial API and implementation, Year: 2012 Andrei Stoica -
00007  * complemented implementation during Google Summer of Code 2014.
00008  ******************************************************************************/
00009 package edu.tum.cs.vis.model;
00010 
00011 import java.io.BufferedWriter;
00012 import java.io.File;
00013 import java.io.FileWriter;
00014 import java.io.IOException;
00015 import java.text.DecimalFormat;
00016 import java.text.NumberFormat;
00017 import java.util.ArrayList;
00018 import java.util.Arrays;
00019 import java.util.Collections;
00020 import java.util.HashMap;
00021 import java.util.HashSet;
00022 import java.util.LinkedList;
00023 import java.util.List;
00024 import java.util.Set;
00025 import java.util.concurrent.Callable;
00026 
00027 import javax.vecmath.Point3f;
00028 import javax.vecmath.Tuple3f;
00029 import javax.vecmath.Vector3f;
00030 
00031 import org.apache.log4j.Logger;
00032 
00033 import processing.core.PGraphics;
00034 import edu.tum.cs.ias.knowrob.utils.ThreadPool;
00035 import edu.tum.cs.vis.model.util.BSphere;
00036 import edu.tum.cs.vis.model.util.DrawSettings;
00037 import edu.tum.cs.vis.model.util.Group;
00038 import edu.tum.cs.vis.model.util.Line;
00039 import edu.tum.cs.vis.model.util.Region;
00040 import edu.tum.cs.vis.model.util.Triangle;
00041 import edu.tum.cs.vis.model.util.Vertex;
00042 import edu.tum.cs.vis.model.util.algorithm.Miniball;
00043 
00053 public class Model {
00054 
00058         private static final Logger             LOGGER                          = Logger.getLogger(Model.class);
00059 
00063         private String                                  textureBasePath         = null;
00064 
00068         private Group                                   group;
00069 
00073         private final List<Vertex>              vertices                        = new ArrayList<Vertex>();
00074 
00078         private final List<Triangle>    triangles                       = new ArrayList<Triangle>();
00079 
00083         private final List<Line>                lines                           = new ArrayList<Line>();
00084 
00088         private final List<Region>              regions                         = new ArrayList<Region>();
00089 
00095         private BSphere                                 boundingSphere          = null;
00096 
00101         private float                                   scale                           = 1f;
00102 
00106         private boolean                                 normalsInitialized      = false;
00107 
00111         private float                                   lowMeanCurvature        = Float.MAX_VALUE;
00112 
00116         private float                                   highMeanCurvature       = Float.MIN_VALUE;
00117 
00121         private float                                   lowGaussCurvature       = Float.MAX_VALUE;
00122 
00126         private float                                   highGaussCurvature      = Float.MIN_VALUE;
00127 
00131         private float                                   avgMeanCurvature        = 0f;
00132 
00136         private float                                   varMeanCurvature        = 0f;
00137 
00141         private float                                   avgGaussCurvature       = 0f;
00142 
00146         private float                                   varGaussCurvature       = 0f;
00147 
00158         public void draw(PGraphics g, DrawSettings drawSettings) {
00159                 if (group != null)
00160                         group.draw(g, drawSettings);
00161         }
00162 
00170         public BSphere getBoundingSphere() {
00171                 if (boundingSphere == null)
00172                         calculateBoundingsphere();
00173                 return boundingSphere;
00174         }
00175 
00181         public Group getGroup() {
00182                 return group;
00183         }
00184 
00190         public List<Line> getLines() {
00191                 return lines;
00192         }
00193 
00199         public List<Region> getRegions() {
00200                 return regions;
00201         }
00202 
00203         public HashMap<Integer, Region> getRegionsMap() {
00204                 HashMap<Integer, Region> regionsMap = new HashMap<Integer, Region>();
00205                 for (Region r : regions) {
00206                         regionsMap.put(r.getRegionId(), r);
00207                 }
00208                 return regionsMap;
00209         }
00210 
00217         public float getScale() {
00218                 return scale;
00219         }
00220 
00226         public String getTextureBasePath() {
00227                 return textureBasePath;
00228         }
00229 
00235         public List<Triangle> getTriangles() {
00236                 return triangles;
00237         }
00238 
00246         public float getUnscaled(float scaled) {
00247                 return scaled * 1f / scale;
00248         }
00249 
00256         public float getLowMeanCurvature() {
00257                 return lowMeanCurvature;
00258         }
00259 
00266         public float getHighMeanCurvature() {
00267                 return highMeanCurvature;
00268         }
00269 
00276         public float getLowGaussCurvature() {
00277                 return lowGaussCurvature;
00278         }
00279 
00286         public float getHighGaussCurvature() {
00287                 return highGaussCurvature;
00288         }
00289 
00295         public float getAvgMeanCurvature() {
00296                 return avgMeanCurvature;
00297         }
00298 
00304         public float getAvgGaussCurvature() {
00305                 return avgGaussCurvature;
00306         }
00307 
00313         public float getVarMeanCurvature() {
00314                 return varMeanCurvature;
00315         }
00316 
00322         public float getVarGaussCurvature() {
00323                 return varGaussCurvature;
00324         }
00325 
00334         public Tuple3f[] getUnscaled(Point3f[] corner) {
00335                 Tuple3f[] ret = new Point3f[corner.length];
00336                 for (int i = 0; i < corner.length; i++) {
00337                         ret[i] = getUnscaled(corner[i]);
00338                 }
00339                 return ret;
00340         }
00341 
00349         public Tuple3f getUnscaled(Tuple3f t) {
00350                 Tuple3f tr = new Point3f(t);
00351                 tr.scale(1f / scale);
00352                 return tr;
00353         }
00354 
00362         public Vector3f getUnscaled(Vector3f t) {
00363                 Vector3f tr = new Vector3f(t);
00364                 tr.scale(1f / scale);
00365                 return tr;
00366         }
00367 
00373         public List<Vertex> getVertices() {
00374                 return vertices;
00375         }
00376 
00380         public void mirrorX() {
00381                 for (Vertex v : vertices) {
00382                         v.x *= (-1);
00383                 }
00384                 group.resetMinMaxValues();
00385         }
00386 
00391         public void normalize() {
00392                 reloadVertexList();
00393                 float x = group.getMaxX() - group.getMinX();
00394                 float y = group.getMaxY() - group.getMinY();
00395                 float z = group.getMaxZ() - group.getMinZ();
00396 
00397                 float max = Math.max(x, Math.max(y, z));
00398 
00399                 scale(1f / max);
00400 
00401                 scale = 1f / max;
00402                 LOGGER.debug("Model normalized to fit a unity length cube. Scaling applied: " + scale);
00403         }
00404 
00412         public void scale(float factor) {
00413 
00414                 for (Vertex v : vertices) {
00415                         v.scale(factor);
00416                 }
00417                 for (Triangle t : triangles) {
00418                         t.updateEdges();
00419                         t.updateCentroid();
00420                 }
00421                 for (Line l : lines)
00422                         l.updateCentroid();
00423                 group.resetMinMaxValues();
00424         }
00425 
00432         public void setGroup(Group group) {
00433                 this.group = group;
00434         }
00435 
00442         public void setRegions(List<Region> regions) {
00443                 if (this.regions != null) {
00444                         this.regions.clear();
00445                         this.regions.addAll(regions);
00446                 }
00447         }
00448 
00455         public void setTextureBasePath(String textureBasePath) {
00456                 this.textureBasePath = textureBasePath;
00457         }
00458 
00464         public void setLowMeanCurvature(final float lowMeanCurvature) {
00465                 this.lowMeanCurvature = lowMeanCurvature;
00466         }
00467 
00473         public void setHighMeanCurvature(final float highMeanCurvature) {
00474                 this.highMeanCurvature = highMeanCurvature;
00475         }
00476 
00482         public void setLowGaussCurvature(final float lowGaussCurvature) {
00483                 this.lowGaussCurvature = lowGaussCurvature;
00484         }
00485 
00491         public void setHighGaussCurvature(final float highGaussCurvature) {
00492                 this.highGaussCurvature = highGaussCurvature;
00493         }
00494 
00500         public void setAvgMeanCurvature(final float avgMeanCurvature) {
00501                 this.avgMeanCurvature = avgMeanCurvature;
00502         }
00503 
00509         public void setAvgGaussCurvature(final float avgGaussCurvature) {
00510                 this.avgGaussCurvature = avgGaussCurvature;
00511         }
00512 
00518         public void setVarMeanCurvature(final float varMeanCurvature) {
00519                 this.varMeanCurvature = varMeanCurvature;
00520         }
00521 
00527         public void setVarGaussCurvature(final float varGaussCurvature) {
00528                 this.varGaussCurvature = varGaussCurvature;
00529         }
00530 
00537         public int removeDoubleSidedTriangles() {
00538                 final Set<Triangle> toRemove = new HashSet<Triangle>();
00539 
00540                 final int interval = 1000;
00541                 List<Callable<Void>> threads = new LinkedList<Callable<Void>>();
00542                 for (int start = 0; start < triangles.size(); start += interval) {
00543                         final int st = start;
00544                         threads.add(new Callable<Void>() {
00545 
00546                                 @Override
00547                                 public Void call() throws Exception {
00548                                         int end = Math.min(st + interval, triangles.size());
00549                                         for (int i = st; i < end; i++) {
00550                                                 Triangle t1 = triangles.get(i);
00551                                                 if (toRemove.contains(t1))
00552                                                         continue;
00553                                                 for (int j = i + 1; j < triangles.size(); j++) {
00554                                                         Triangle t2 = triangles.get(j);
00555 
00556                                                         if (toRemove.contains(t2))
00557                                                                 continue;
00558                                                         int eqCnt = 0;
00559                                                         for (int k = 0; k < 3; k++) {
00560                                                                 Point3f p1 = t1.getPosition()[k];
00561                                                                 for (Point3f p2 : t2.getPosition()) {
00562                                                                         if (p1.x == p2.x && p1.y == p2.y && p1.z == p2.z) {
00563                                                                                 eqCnt++;
00564                                                                                 break;
00565                                                                         }
00566                                                                 }
00567                                                                 if (eqCnt != k + 1) {
00568                                                                         // break if not enough vertices are equal
00569                                                                         break;
00570                                                                 }
00571                                                         }
00572                                                         if (eqCnt == 3) {
00573                                                                 // triangles are the same, so remove j
00574 
00575                                                                 synchronized (toRemove) {
00576                                                                         toRemove.add(t2);
00577                                                                 }
00578                                                         }
00579                                                 }
00580                                         }
00581                                         return null;
00582                                 }
00583 
00584                         });
00585                 };
00586 
00587                 ThreadPool.executeInPool(threads);
00588                 if (toRemove.size() > 0) {
00589                         this.group.removeTriangle(toRemove);
00590                         reloadVertexList();
00591                 }
00592                 return toRemove.size();
00593         }
00594 
00599         public void reloadVertexList() {
00600                 synchronized (triangles) {
00601                         triangles.clear();
00602                         this.group.getAllTriangles(triangles);
00603                 }
00604                 Set<Vertex> vertices = new HashSet<Vertex>(triangles.size() * 2);
00605                 for (Triangle t : triangles) {
00606                         vertices.addAll(Arrays.asList(t.getPosition()));
00607                 }
00608                 for (Line l : lines) {
00609                         vertices.addAll(Arrays.asList(l.getPosition()));
00610                 }
00611                 this.vertices.clear();
00612                 this.vertices.addAll(vertices);
00613         }
00614 
00620         public void updateVertexSharing() {
00621 
00622                 final Set<Triangle> checkedTriangles = Collections.synchronizedSet(new HashSet<Triangle>());
00623 
00624                 Set<Vertex> toRemove = new HashSet<Vertex>();
00625 
00626                 for (Triangle t : triangles)
00627                         updateVertexSharingForTriangle(t, checkedTriangles, toRemove);
00628                 synchronized (vertices) {
00629                         vertices.removeAll(toRemove);
00630                 }
00631                 reloadVertexList();
00632         }
00633 
00649         private void updateVertexSharingForNeighbors(final Triangle t, final Triangle n,
00650                         final Set<Triangle> checkedTriangles, Set<Vertex> toRemove) {
00651                 if (t == n)
00652                         return;
00653                 synchronized (n) {
00654                         if (checkedTriangles.contains(n))
00655                                 return;
00656 
00657                         double angle = t.getDihedralAngle(n);
00658                         // Share vertices if angle is < 15 degree
00659                         boolean share = (angle < 15 / 180.0 * Math.PI);
00660 
00661                         for (Vertex vt : t.getPosition()) {
00662                                 for (int i = 0; i < n.getPosition().length; i++) {
00663                                         Vertex vn = n.getPosition()[i];
00664                                         /*
00665                                         if ((vt.sameCoordinates(vn) && vt.getPointarea() == vn.getPointarea() && vt
00666                                                         .getNormalVector().equals(vn.getNormalVector()))
00667                                                         ||(share && vn != vt && vt.sameCoordinates(vn))) {*/
00668                                         if (share && vn != vt && vt.sameCoordinates(vn)) {
00669                                                 // merge vertices
00670                                                 n.getPosition()[i] = vt;
00671                                                 int cnt = 0;
00672                                                 for (Triangle tmpTri : t.getNeighbors()) {
00673                                                         for (Vertex tmpVer : tmpTri.getPosition())
00674                                                                 if (tmpVer == vn) {
00675                                                                         cnt++;
00676                                                                         break;
00677                                                                 }
00678                                                 }
00679                                                 if (cnt == 0) {
00680                                                         for (Triangle tmpTri : n.getNeighbors()) {
00681                                                                 if (tmpTri == t)
00682                                                                         continue;
00683                                                                 for (Vertex tmpVer : tmpTri.getPosition())
00684                                                                         if (tmpVer == vn) {
00685                                                                                 cnt++;
00686                                                                                 break;
00687                                                                         }
00688                                                         }
00689                                                 }
00690                                                 if (cnt == 0) {
00691                                                         toRemove.add(vn);
00692                                                 }
00693                                         } else if (!share && vn == vt) {
00694                                                 // split vertices
00695                                                 // System.out.println("split");
00696                                                 /*Vertex clone = (Vertex) vt.clone();
00697                                                 synchronized (vertices) {
00698                                                         vertices.add(clone);
00699                                                 }
00700                                                 n.getPosition()[i] = clone;*/
00701                                         }
00702                                 }
00703                         }
00704                 }
00705         }
00706 
00719         private void updateVertexSharingForTriangle(final Triangle t,
00720                         final Set<Triangle> checkedTriangles, final Set<Vertex> toRemove) {
00721                 synchronized (t) {
00722                         if (checkedTriangles.contains(t))
00723                                 return;
00724                         synchronized (t.getNeighbors()) {
00725                                 for (Triangle n : t.getNeighbors()) {
00726                                         updateVertexSharingForNeighbors(t, n, checkedTriangles, toRemove);
00727                                         // also check neighbors of neighbor
00728                                         for (Triangle nn : n.getNeighbors()) {
00729                                                 updateVertexSharingForNeighbors(t, nn, checkedTriangles, toRemove);
00730                                         }
00731                                 }
00732 
00733                         }
00734                         checkedTriangles.add(t);
00735                 }
00736         }
00737 
00748         public void updateVertexNormals() {
00749                 // Compute from faces
00750 
00751                 // Reset normal vectors, because vertex normals from collada aren't correct
00752                 for (Vertex v : vertices) {
00753                         v.getNormalVector().x = v.getNormalVector().y = v.getNormalVector().z = 0;
00754                 }
00755 
00756                 for (int i = 0; i < triangles.size(); ++i) {
00757                         calculateVertexNormalsForTriangle(triangles.get(i));
00758                 }
00759 
00760                 // Normalize all vectors.
00761                 // Additionally search the vectors which have max x, max y and max z coordinates (needed for
00762                 // vertex winding check / inverted normals check)
00763 
00764                 Vertex extrema[] = new Vertex[6];
00765 
00766                 for (Vertex v : vertices) {
00767                         v.getNormalVector().normalize();
00768                         float coord[] = new float[3];
00769                         v.get(coord);
00770                         // Set all max values
00771                         for (int i = 0; i < 3; i++) {
00772                                 float extCoord[] = new float[3];
00773                                 if (extrema[i] != null)
00774                                         extrema[i].get(extCoord);
00775                                 if (extrema[i] == null || extCoord[i] < coord[i])
00776                                         extrema[i] = v;
00777                         }
00778                         // Set all min values
00779                         for (int i = 0; i < 3; i++) {
00780                                 float extCoord[] = new float[3];
00781                                 if (extrema[i + 3] != null)
00782                                         extrema[i + 3].get(extCoord);
00783                                 if (extrema[i + 3] == null || extCoord[i] > coord[i])
00784                                         extrema[i + 3] = v;
00785                         }
00786                 }
00787 
00788                 int vote = 0;
00789                 // Now the vertex normal of maxX must point in approximately the same direction as the
00790                 // axis. If the angle between the vertex normal and the direction is smaller than 90° vote
00791                 // for inversion.
00792                 for (int i = 0; i < 6; i++) {
00793                         float coord[] = { 0, 0, 0 };
00794                         coord[i % 3] = i < 3 ? 1 : -1;
00795                         double dot = extrema[i].getNormalVector().dot((new Vector3f(coord)));
00796                         double angle = Math.acos(dot);
00797                         // System.out.println("Angle: " + (angle * 180 / Math.PI));
00798                         if (angle < Math.PI / 2)
00799                                 vote--;
00800                         else
00801                                 vote++;
00802                 }
00803                 // System.out.println("VOTE: " + vote);
00804                 if (vote > 0) {
00805                         // They voted for inverting
00806                         LOGGER.debug("Inverting normal vertices, because vote is: " + vote);
00807                         for (Vertex v : vertices) {
00808                                 v.getNormalVector().scale(-1f);
00809                         }
00810                 }
00811                 for (Triangle t : triangles) {
00812                         t.calculateNormalVector();
00813                 }
00814 
00815                 normalsInitialized = true;
00816 
00817         }
00818 
00825         private static void calculateVertexNormalsForTriangle(final Triangle t) {
00826                 Vertex p0 = t.getPosition()[0];
00827                 Vertex p1 = t.getPosition()[1];
00828                 Vertex p2 = t.getPosition()[2];
00829 
00830                 // get vectors from p0 to p1 and so on
00831                 Vector3f a = new Vector3f(p0);
00832                 a.sub(p1);
00833                 Vector3f b = new Vector3f(p1);
00834                 b.sub(p2);
00835                 Vector3f c = new Vector3f(p2);
00836                 c.sub(p0);
00837                 // length of these vectors
00838                 float l2a = a.lengthSquared(), l2b = b.lengthSquared(), l2c = c.lengthSquared();
00839                 if (l2a == 0.0 || l2b == 0.0 || l2c == 0.0) {
00840                         LOGGER.debug("skipping triangle: " + t + "\n(" + p0 + p1 + p2 + ")");
00841                         return;
00842                 }
00843 
00844                 Vector3f facenormal = new Vector3f();
00845                 facenormal.cross(a, b); // unscaled normal
00846 
00847                 Vector3f normalP0 = (Vector3f) facenormal.clone();
00848                 normalP0.scale(1.0f / (l2a * l2c));
00849                 // normalP0.scale(1.0f / areaOfTriangle);
00850                 synchronized (p0.getNormalVector()) {
00851                         p0.getNormalVector().add(normalP0);
00852                 }
00853 
00854                 Vector3f normalP1 = (Vector3f) facenormal.clone();
00855                 normalP1.scale(1.0f / (l2b * l2a));
00856                 // normalP1.scale(1.0f / areaOfTriangle);
00857                 synchronized (p1.getNormalVector()) {
00858                         p1.getNormalVector().add(normalP1);
00859                 }
00860 
00861                 Vector3f normalP2 = (Vector3f) facenormal.clone();
00862                 normalP2.scale(1.0f / (l2c * l2b));
00863                 // normalP2.scale(1.0f / areaOfTriangle);
00864                 synchronized (p2.getNormalVector()) {
00865                         p2.getNormalVector().add(normalP2);
00866                 }
00867         }
00868 
00885         public File exportVerticesAsTxt() {
00886                 try {
00887                         File tmp = File.createTempFile("knowrob_", ".txt",
00888                                         new File(System.getProperty("java.io.tmpdir")));
00889                         exportVerticesAsTxt(tmp, true);
00890                         return tmp;
00891                 } catch (IOException e) {
00892                         LOGGER.error("Couldn't create temp file name: " + e.getMessage());
00893                         return null;
00894                 }
00895 
00896         }
00897 
00916         public boolean exportVerticesAsTxt(File path, boolean overwrite) {
00917 
00918                 try {
00919                         if (!overwrite && path.exists()) {
00920                                 LOGGER.error("Couldn't export model to file. Already exists: "
00921                                                 + path.getAbsolutePath());
00922                                 return false;
00923                         }
00924                         FileWriter fstream = new FileWriter(path);
00925                         BufferedWriter out = new BufferedWriter(fstream);
00926                         out.write(vertices.size() + "\n");
00927                         NumberFormat df = new DecimalFormat("0.####################");
00928                         for (int i = 0; i < vertices.size(); i++) {
00929                                 Vertex v = vertices.get(i);
00930                                 out.write(df.format(v.x) + "\t" + df.format(v.y) + "\t" + df.format(v.z));
00931                                 if (normalsInitialized) {
00932                                         out.write("\t" + df.format(v.getNormalVector().x) + "\t"
00933                                                         + df.format(v.getNormalVector().y) + "\t"
00934                                                         + df.format(v.getNormalVector().z));
00935                                 }
00936                                 if (i < vertices.size() - 1)
00937                                         out.write("\n");
00938                         }
00939                         LOGGER.info("Model exported to file " + path.getAbsolutePath());
00940                         out.close();
00941                         return true;
00942                 } catch (IOException e) {
00943                         LOGGER.error("Couldn't export model to file " + path.getAbsolutePath() + ". "
00944                                         + e.getMessage());
00945                 }
00946                 return false;
00947         }
00948 
00954         public double getSizeX() {
00955                 return group.getMaxX() - group.getMinX();
00956         }
00957 
00963         public double getSizeY() {
00964                 return group.getMaxY() - group.getMinY();
00965         }
00966 
00972         public double getSizeZ() {
00973                 return group.getMaxZ() - group.getMinZ();
00974         }
00975 
00982         private void calculateBoundingsphere() {
00983                 if (vertices.size() == 0)
00984                         return;
00985 
00986                 Miniball mb = new Miniball(3);
00987                 for (Vertex v : vertices) {
00988                         double arr[] = new double[3];
00989                         arr[0] = v.x;
00990                         arr[1] = v.y;
00991                         arr[2] = v.z;
00992                         mb.check_in(arr);
00993                 }
00994                 mb.build();
00995                 this.boundingSphere = new BSphere((float) Math.sqrt(mb.squared_radius()), new Vector3f(
00996                                 (float) mb.center()[0], (float) mb.center()[1], (float) mb.center()[2]));
00997 
00998         }
00999 
01007         public float feature_size() {
01008                 if (triangles.size() == 0)
01009                         return 0.0f;
01010 
01011                 int nf = triangles.size();
01012                 int nsamp = Math.min(nf / 2, 333);
01013 
01014                 Float[] samples = new Float[nsamp * 3];
01015 
01016                 for (int i = 0; i < nsamp; i++) {
01017 
01018                         int ind = (int) (Math.random() * nf);
01019                         final Vertex p0 = triangles.get(ind).getPosition()[0];
01020                         final Vertex p1 = triangles.get(ind).getPosition()[1];
01021                         final Vertex p2 = triangles.get(ind).getPosition()[2];
01022                         samples[i * 3] = p0.distanceSquared(p1);
01023                         samples[i * 3 + 1] = p1.distanceSquared(p2);
01024                         samples[i * 3 + 2] = p2.distanceSquared(p0);
01025                 }
01026                 Arrays.sort(samples);
01027                 return getUnscaled((float) Math.sqrt(samples[samples.length / 2]));
01028         }
01029 }


knowrob_cad_parser
Author(s): Stefan Profanter
autogenerated on Mon Oct 6 2014 01:29:56