Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008 package edu.tum.cs.vis.model;
00009
00010 import java.io.BufferedWriter;
00011 import java.io.File;
00012 import java.io.FileWriter;
00013 import java.io.IOException;
00014 import java.text.DecimalFormat;
00015 import java.text.NumberFormat;
00016 import java.util.ArrayList;
00017 import java.util.Arrays;
00018 import java.util.Collections;
00019 import java.util.HashSet;
00020 import java.util.LinkedList;
00021 import java.util.List;
00022 import java.util.Set;
00023 import java.util.concurrent.Callable;
00024
00025 import javax.vecmath.Point3f;
00026 import javax.vecmath.Tuple3f;
00027 import javax.vecmath.Vector3f;
00028
00029 import org.apache.log4j.Logger;
00030
00031 import processing.core.PGraphics;
00032 import edu.tum.cs.ias.knowrob.utils.ThreadPool;
00033 import edu.tum.cs.vis.model.util.BSphere;
00034 import edu.tum.cs.vis.model.util.DrawSettings;
00035 import edu.tum.cs.vis.model.util.Group;
00036 import edu.tum.cs.vis.model.util.Line;
00037 import edu.tum.cs.vis.model.util.Triangle;
00038 import edu.tum.cs.vis.model.util.Vertex;
00039 import edu.tum.cs.vis.model.util.algorithm.Miniball;
00040
00050 public class Model {
00051
00055 private static Logger logger = Logger.getLogger(Model.class);
00056
00060 private String textureBasePath = null;
00061
00065 private Group group;
00066
00070 private final List<Vertex> vertices = new ArrayList<Vertex>();
00071
00075 private final List<Triangle> triangles = new ArrayList<Triangle>();
00076
00080 private final List<Line> lines = new ArrayList<Line>();
00081
00087 private BSphere boundingSphere = null;
00088
00093 private float scale = 1;
00094
00098 private boolean normalsInitialized = false;
00099
00107 public void draw(PGraphics g, DrawSettings drawSettings) {
00108 if (group != null)
00109 group.draw(g, drawSettings);
00110 }
00111
00119 public BSphere getBoundingSphere() {
00120 if (boundingSphere == null)
00121 calculateBoundingsphere();
00122 return boundingSphere;
00123 }
00124
00130 public Group getGroup() {
00131 return group;
00132 }
00133
00139 public List<Line> getLines() {
00140 return lines;
00141 }
00142
00149 public float getScale() {
00150 return scale;
00151 }
00152
00158 public String getTextureBasePath() {
00159 return textureBasePath;
00160 }
00161
00167 public List<Triangle> getTriangles() {
00168 return triangles;
00169 }
00170
00178 public float getUnscaled(float scaled) {
00179 return scaled * 1f / scale;
00180 }
00181
00190 public Tuple3f[] getUnscaled(Point3f[] corner) {
00191 Tuple3f[] ret = new Point3f[corner.length];
00192 for (int i = 0; i < corner.length; i++) {
00193 ret[i] = getUnscaled(corner[i]);
00194 }
00195 return ret;
00196 }
00197
00205 public Tuple3f getUnscaled(Tuple3f t) {
00206 Tuple3f tr = new Point3f(t);
00207 tr.scale(1f / scale);
00208 return tr;
00209 }
00210
00218 public Vector3f getUnscaled(Vector3f t) {
00219 Vector3f tr = new Vector3f(t);
00220 tr.scale(1f / scale);
00221 return tr;
00222 }
00223
00229 public List<Vertex> getVertices() {
00230 return vertices;
00231 }
00232
00236 public void mirrorX() {
00237 for (Vertex v : vertices) {
00238 v.x *= (-1);
00239 }
00240 group.resetMinMaxValues();
00241
00242 }
00243
00248 public void normalize() {
00249 reloadVertexList();
00250 float x = group.getMaxX() - group.getMinX();
00251 float y = group.getMaxY() - group.getMinY();
00252 float z = group.getMaxZ() - group.getMinZ();
00253
00254 float max = Math.max(x, Math.max(y, z));
00255
00256 scale(1f / max);
00257
00258 scale = 1f / max;
00259 }
00260
00268 public void scale(float factor) {
00269
00270 for (Vertex v : vertices) {
00271 v.scale(factor);
00272 }
00273 for (Triangle t : triangles)
00274 t.updateCentroid();
00275 for (Line l : lines)
00276 l.updateCentroid();
00277 group.resetMinMaxValues();
00278 }
00279
00286 public void setGroup(Group group) {
00287 this.group = group;
00288 }
00289
00296 public void setTextureBasePath(String textureBasePath) {
00297 this.textureBasePath = textureBasePath;
00298 }
00299
00306 public int removeDoubleSidedTriangles() {
00307 final Set<Triangle> toRemove = new HashSet<Triangle>();
00308
00309 final int interval = 1000;
00310 List<Callable<Void>> threads = new LinkedList<Callable<Void>>();
00311 for (int start = 0; start < triangles.size(); start += interval) {
00312 final int st = start;
00313 threads.add(new Callable<Void>() {
00314
00315 @Override
00316 public Void call() throws Exception {
00317 int end = Math.min(st + interval, triangles.size());
00318 for (int i = st; i < end; i++) {
00319 Triangle t1 = triangles.get(i);
00320 if (toRemove.contains(t1))
00321 continue;
00322 for (int j = i + 1; j < triangles.size(); j++) {
00323 Triangle t2 = triangles.get(j);
00324
00325 if (toRemove.contains(t2))
00326 continue;
00327 int eqCnt = 0;
00328 for (int k = 0; k < 3; k++) {
00329 Point3f p1 = t1.getPosition()[k];
00330 for (Point3f p2 : t2.getPosition()) {
00331 if (p1.x == p2.x && p1.y == p2.y && p1.z == p2.z) {
00332 eqCnt++;
00333 break;
00334 }
00335 }
00336 if (eqCnt != k + 1) {
00337
00338 break;
00339 }
00340 }
00341 if (eqCnt == 3) {
00342
00343
00344 synchronized (toRemove) {
00345 toRemove.add(t2);
00346 }
00347 }
00348 }
00349 }
00350 return null;
00351 }
00352
00353 });
00354 };
00355
00356 ThreadPool.executeInPool(threads);
00357 if (toRemove.size() > 0) {
00358 this.group.removeTriangle(toRemove);
00359 reloadVertexList();
00360 }
00361 return toRemove.size();
00362 }
00363
00368 private void reloadVertexList() {
00369 synchronized (triangles) {
00370 triangles.clear();
00371 this.group.getAllTriangles(triangles);
00372 }
00373 Set<Vertex> vertices = new HashSet<Vertex>(triangles.size() * 2);
00374 for (Triangle t : triangles) {
00375 vertices.addAll(Arrays.asList(t.getPosition()));
00376 }
00377 for (Line l : lines) {
00378 vertices.addAll(Arrays.asList(l.getPosition()));
00379 }
00380 this.vertices.clear();
00381 this.vertices.addAll(vertices);
00382 }
00383
00389 public void updateVertexSharing() {
00390
00391 final Set<Triangle> checkedTriangles = Collections.synchronizedSet(new HashSet<Triangle>());
00392
00393 Set<Vertex> toRemove = new HashSet<Vertex>();
00394
00395 for (Triangle t : triangles)
00396 updateVertexSharingForTriangle(t, checkedTriangles, toRemove);
00397 synchronized (vertices) {
00398 vertices.removeAll(toRemove);
00399 }
00400 reloadVertexList();
00401 }
00402
00418 private void updateVertexSharingForNeighbors(final Triangle t, final Triangle n,
00419 final Set<Triangle> checkedTriangles, Set<Vertex> toRemove) {
00420 if (t == n)
00421 return;
00422 synchronized (n) {
00423 if (checkedTriangles.contains(n))
00424 return;
00425
00426 double angle = t.getDihedralAngle(n);
00427
00428 boolean share = (angle < 15 / 180.0 * Math.PI);
00429
00430 for (Vertex vt : t.getPosition()) {
00431 for (int i = 0; i < n.getPosition().length; i++) {
00432 Vertex vn = n.getPosition()[i];
00433
00434
00435
00436
00437 if (share && vn != vt && vt.sameCoordinates(vn)) {
00438
00439 n.getPosition()[i] = vt;
00440 int cnt = 0;
00441 for (Triangle tmpTri : t.getNeighbors()) {
00442 for (Vertex tmpVer : tmpTri.getPosition())
00443 if (tmpVer == vn) {
00444 cnt++;
00445 break;
00446 }
00447 }
00448 if (cnt == 0) {
00449 for (Triangle tmpTri : n.getNeighbors()) {
00450 if (tmpTri == t)
00451 continue;
00452 for (Vertex tmpVer : tmpTri.getPosition())
00453 if (tmpVer == vn) {
00454 cnt++;
00455 break;
00456 }
00457 }
00458 }
00459 if (cnt == 0) {
00460 toRemove.add(vn);
00461 }
00462 } else if (!share && vn == vt) {
00463
00464
00465
00466
00467
00468
00469
00470 }
00471 }
00472 }
00473 }
00474 }
00475
00488 private void updateVertexSharingForTriangle(final Triangle t,
00489 final Set<Triangle> checkedTriangles, final Set<Vertex> toRemove) {
00490 synchronized (t) {
00491 if (checkedTriangles.contains(t))
00492 return;
00493 synchronized (t.getNeighbors()) {
00494 for (Triangle n : t.getNeighbors()) {
00495 updateVertexSharingForNeighbors(t, n, checkedTriangles, toRemove);
00496
00497 for (Triangle nn : n.getNeighbors()) {
00498 updateVertexSharingForNeighbors(t, nn, checkedTriangles, toRemove);
00499 }
00500 }
00501
00502 }
00503 checkedTriangles.add(t);
00504 }
00505 }
00506
00514 public void updateVertexNormals() {
00515
00516
00517 List<Callable<Void>> threads = new LinkedList<Callable<Void>>();
00518
00519 final int interval = 500;
00520
00521
00522 for (Vertex v : vertices) {
00523 v.getNormalVector().x = v.getNormalVector().y = v.getNormalVector().z = 0;
00524 }
00525
00526 for (int start = 0; start < triangles.size(); start += interval) {
00527 final int st = start;
00528
00529
00530
00531
00532 int end = Math.min(st + interval, triangles.size());
00533 for (int i = st; i < end; i++) {
00534 calculateVertexNormalsForTriangle(triangles.get(i));
00535 }
00536
00537
00538
00539
00540 };
00541
00542 ThreadPool.executeInPool(threads);
00543
00544
00545
00546
00547
00548 Vertex extrema[] = new Vertex[6];
00549
00550 for (Vertex v : vertices) {
00551 v.getNormalVector().normalize();
00552 float coord[] = new float[3];
00553 v.get(coord);
00554
00555 for (int i = 0; i < 3; i++) {
00556 float extCoord[] = new float[3];
00557 if (extrema[i] != null)
00558 extrema[i].get(extCoord);
00559 if (extrema[i] == null || extCoord[i] < coord[i])
00560 extrema[i] = v;
00561 }
00562
00563 for (int i = 0; i < 3; i++) {
00564 float extCoord[] = new float[3];
00565 if (extrema[i + 3] != null)
00566 extrema[i + 3].get(extCoord);
00567 if (extrema[i + 3] == null || extCoord[i] > coord[i])
00568 extrema[i + 3] = v;
00569 }
00570 }
00571
00572 int vote = 0;
00573
00574
00575
00576 for (int i = 0; i < 6; i++) {
00577 float coord[] = { 0, 0, 0 };
00578 coord[i % 3] = i < 3 ? 1 : -1;
00579 double dot = extrema[i].getNormalVector().dot((new Vector3f(coord)));
00580 double angle = Math.acos(dot);
00581
00582 if (angle < Math.PI / 2)
00583 vote--;
00584 else
00585 vote++;
00586 }
00587
00588 if (vote > 0) {
00589
00590 logger.debug("Inverting normal vertices, because vote is: " + vote);
00591 for (Vertex v : vertices) {
00592 v.getNormalVector().scale(-1f);
00593 }
00594 }
00595
00596 normalsInitialized = true;
00597
00598 }
00599
00606 static void calculateVertexNormalsForTriangle(final Triangle t) {
00607 Vertex p0 = t.getPosition()[0];
00608 Vertex p1 = t.getPosition()[1];
00609 Vertex p2 = t.getPosition()[2];
00610
00611
00612 Vector3f a = new Vector3f(p0);
00613 a.sub(p1);
00614 Vector3f b = new Vector3f(p1);
00615 b.sub(p2);
00616 Vector3f c = new Vector3f(p2);
00617 c.sub(p0);
00618
00619 float l2a = a.lengthSquared(), l2b = b.lengthSquared(), l2c = c.lengthSquared();
00620 if (l2a == 0 || l2b == 0 || l2c == 0)
00621 return;
00622
00623 Vector3f facenormal = new Vector3f();
00624 facenormal.cross(a, b);
00625
00626 Vector3f normalP0 = (Vector3f) facenormal.clone();
00627 normalP0.scale(1.0f / (l2a * l2c));
00628 synchronized (p0.getNormalVector()) {
00629 p0.getNormalVector().add(normalP0);
00630 }
00631
00632 Vector3f normalP1 = (Vector3f) facenormal.clone();
00633 normalP1.scale(1.0f / (l2b * l2a));
00634 synchronized (p1.getNormalVector()) {
00635 p1.getNormalVector().add(normalP1);
00636 }
00637
00638 Vector3f normalP2 = (Vector3f) facenormal.clone();
00639 normalP2.scale(1.0f / (l2c * l2b));
00640 synchronized (p2.getNormalVector()) {
00641 p2.getNormalVector().add(normalP2);
00642 }
00643 }
00644
00661 public File exportVerticesAsTxt() {
00662 try {
00663 File tmp = File.createTempFile("knowrob_", ".txt",
00664 new File(System.getProperty("java.io.tmpdir")));
00665 exportVerticesAsTxt(tmp, true);
00666 return tmp;
00667 } catch (IOException e) {
00668 logger.error("Couldn't create temp file name: " + e.getMessage());
00669 return null;
00670 }
00671
00672 }
00673
00692 public boolean exportVerticesAsTxt(File path, boolean overwrite) {
00693
00694 try {
00695 if (!overwrite && path.exists()) {
00696 logger.error("Couldn't export model to file. Already exists: "
00697 + path.getAbsolutePath());
00698 return false;
00699 }
00700 FileWriter fstream = new FileWriter(path);
00701 BufferedWriter out = new BufferedWriter(fstream);
00702 out.write(vertices.size() + "\n");
00703 NumberFormat df = new DecimalFormat("0.####################");
00704 for (int i = 0; i < vertices.size(); i++) {
00705 Vertex v = vertices.get(i);
00706 out.write(df.format(v.x) + "\t" + df.format(v.y) + "\t" + df.format(v.z));
00707 if (normalsInitialized) {
00708 out.write("\t" + df.format(v.getNormalVector().x) + "\t"
00709 + df.format(v.getNormalVector().y) + "\t"
00710 + df.format(v.getNormalVector().z));
00711 }
00712 if (i < vertices.size() - 1)
00713 out.write("\n");
00714 }
00715 logger.info("Model exported to file " + path.getAbsolutePath());
00716 out.close();
00717 return true;
00718 } catch (IOException e) {
00719 logger.error("Couldn't export model to file " + path.getAbsolutePath() + ". "
00720 + e.getMessage());
00721 }
00722 return false;
00723 }
00724
00730 public double getSizeX() {
00731 return group.getMaxX() - group.getMinX();
00732 }
00733
00739 public double getSizeY() {
00740 return group.getMaxY() - group.getMinY();
00741 }
00742
00748 public double getSizeZ() {
00749 return group.getMaxZ() - group.getMinZ();
00750 }
00751
00758 private void calculateBoundingsphere() {
00759 if (vertices.size() == 0)
00760 return;
00761
00762 Miniball mb = new Miniball(3);
00763 for (Vertex v : vertices) {
00764 double arr[] = new double[3];
00765 arr[0] = v.x;
00766 arr[1] = v.y;
00767 arr[2] = v.z;
00768 mb.check_in(arr);
00769 }
00770 mb.build();
00771 this.boundingSphere = new BSphere((float) Math.sqrt(mb.squared_radius()), new Vector3f(
00772 (float) mb.center()[0], (float) mb.center()[1], (float) mb.center()[2]));
00773
00774 }
00775
00783 public float feature_size() {
00784 if (triangles.size() == 0)
00785 return 0.0f;
00786
00787 int nf = triangles.size();
00788 int nsamp = Math.min(nf / 2, 333);
00789
00790 Float[] samples = new Float[nsamp * 3];
00791
00792 for (int i = 0; i < nsamp; i++) {
00793
00794 int ind = (int) (Math.random() * nf);
00795 final Vertex p0 = triangles.get(ind).getPosition()[0];
00796 final Vertex p1 = triangles.get(ind).getPosition()[1];
00797 final Vertex p2 = triangles.get(ind).getPosition()[2];
00798 samples[i * 3] = p0.distanceSquared(p1);
00799 samples[i * 3 + 1] = p1.distanceSquared(p2);
00800 samples[i * 3 + 2] = p2.distanceSquared(p0);
00801 }
00802 Arrays.sort(samples);
00803 return getUnscaled((float) Math.sqrt(samples[samples.length / 2]));
00804 }
00805 }