Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
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
00569 break;
00570 }
00571 }
00572 if (eqCnt == 3) {
00573
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
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
00666
00667
00668 if (share && vn != vt && vt.sameCoordinates(vn)) {
00669
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
00695
00696
00697
00698
00699
00700
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
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
00750
00751
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
00761
00762
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
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
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
00790
00791
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
00798 if (angle < Math.PI / 2)
00799 vote--;
00800 else
00801 vote++;
00802 }
00803
00804 if (vote > 0) {
00805
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
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
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);
00846
00847 Vector3f normalP0 = (Vector3f) facenormal.clone();
00848 normalP0.scale(1.0f / (l2a * l2c));
00849
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
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
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 }