JavaCAD
SmoothingGroups.java
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates.
3  * All rights reserved. Use is subject to license terms.
4  *
5  * This file is available and licensed under the following license:
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * - Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * - Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the distribution.
16  * - Neither the name of Oracle Corporation nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package eu.mihosoft.vrl.v3d.ext.openjfx.importers;
33 
34 import eu.mihosoft.vrl.v3d.Vector3d;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.BitSet;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.LinkedList;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Queue;
44 import javafx.scene.shape.TriangleMesh;
45 import javax.vecmath.Vector3f;
46 
47 // TODO: Auto-generated Javadoc
51 public class SmoothingGroups {
52 
54  private BitSet visited, notVisited;
55 
57  private Queue<Integer> q;
58 
60  private int[][] faces;
61 
63  private int[][] faceNormals;
64 
66  private float[] normals;
67 
69  private Edge[][] faceEdges;
70 
78  public SmoothingGroups(int faces[][], int[][] faceNormals, float[] normals) {
79  this.faces = faces;
80  this.faceNormals = faceNormals;
81  this.normals = normals;
82  visited = new BitSet(faces.length);
83  notVisited = new BitSet(faces.length);
84  notVisited.set(0, faces.length, true);
85  q = new LinkedList<Integer>();
86  }
87 
94  // edge -> [faces]
95  private List<Integer> getNextConnectedComponent(Map<Edge, List<Integer>> adjacentFaces) {
96  int index = notVisited.previousSetBit(faces.length - 1);
97  q.add(index);
98  visited.set(index);
99  notVisited.set(index, false);
100  List<Integer> res = new ArrayList<Integer>();
101  while (!q.isEmpty()) {
102  Integer faceIndex = q.remove();
103  res.add(faceIndex);
104  for (Edge edge : faceEdges[faceIndex]) {
105  List<Integer> adjFaces = adjacentFaces.get(edge);
106  if (adjFaces == null) {
107  continue;
108  }
109  Integer adjFaceIndex = adjFaces.get(adjFaces.get(0).equals(faceIndex) ? 1 : 0);
110  if (!visited.get(adjFaceIndex)) {
111  q.add(adjFaceIndex);
112  visited.set(adjFaceIndex);
113  notVisited.set(adjFaceIndex, false);
114  }
115  }
116  }
117  return res;
118  }
119 
125  private boolean hasNextConnectedComponent() {
126  return !notVisited.isEmpty();
127  }
128 
132  private void computeFaceEdges() {
133  faceEdges = new Edge[faces.length][];
134  for (int f = 0; f < faces.length; f++) {
135  int[] face = faces[f];
136  int[] faceNormal = faceNormals[f];
137  int n = face.length/2;
138  faceEdges[f] = new Edge[n];
139  int from = face[(n-1) * 2];
140  int fromNormal = faceNormal[n-1];
141  for (int i = 0; i < n; i++) {
142  int to = face[i * 2];
143  int toNormal = faceNormal[i];
144  Edge edge = new Edge(from, to, fromNormal, toNormal);
145  faceEdges[f][i] = edge;
146  from = to;
147  fromNormal = toNormal;
148  }
149  }
150  }
151 
157  private Map<Edge, List<Integer>> getAdjacentFaces() {
158  Map<Edge, List<Integer>> adjacentFaces = new HashMap<Edge, List<Integer>>();
159  for (int f = 0; f < faceEdges.length; f++) {
160  for (Edge edge : faceEdges[f]) {
161  if (!adjacentFaces.containsKey(edge)) {
162  adjacentFaces.put(edge, new ArrayList<Integer>());
163  }
164  adjacentFaces.get(edge).add(f);
165  }
166  }
167  for (Iterator<Map.Entry<Edge, List<Integer>>> it = adjacentFaces.entrySet().iterator(); it.hasNext(); ) {
168  Map.Entry<Edge, List<Integer>> e = it.next();
169  if (e.getValue().size() != 2) {
170  // just skip them
171  it.remove();
172  }
173  }
174  return adjacentFaces;
175  }
176 
183  Vector3f getNormal(int index) {
184  return new Vector3f(normals[index * 3], normals[index * 3 + 1], normals[index * 3 + 2]);
185  }
186 
188  private static final float normalAngle = 0.9994f; // cos(2)
189 
197  private static boolean isNormalsEqual(Vector3f n1, Vector3f n2) {
198  if (n1.x == 1.0e20f || n1.y == 1.0e20f || n1.z == 1.0e20f
199  || n2.x == 1.0e20f || n2.y == 1.0e20f || n2.z == 1.0e20f) {
200  //System.out.println("unlocked normal found, skipping");
201  return false;
202  }
203  Vector3f myN1 = new Vector3f(n1);
204  myN1.normalize();
205  Vector3f myN2 = new Vector3f(n2);
206  myN2.normalize();
207  return myN1.dot(myN2) >= normalAngle;
208  }
209 
216  private Map<Edge, List<Integer>> getSmoothEdges(Map<Edge, List<Integer>> adjacentFaces) {
217  Map<Edge, List<Integer>> smoothEdges = new HashMap<Edge, List<Integer>>();
218 
219  for (int face = 0; face < faceEdges.length; face++) {
220  for (Edge edge : faceEdges[face]) {
221  List<Integer> adjFaces = adjacentFaces.get(edge);
222  if (adjFaces == null || adjFaces.size() != 2) {
223  // could happen when we skip edges!
224  continue;
225  }
226  int adjFace = adjFaces.get(adjFaces.get(0) == face ? 1 : 0);
227  Edge[] adjFaceEdges = faceEdges[adjFace];
228  int adjEdgeInd = Arrays.asList(adjFaceEdges).indexOf(edge);
229  if (adjEdgeInd == -1) {
230  System.out.println("Can't find edge " + edge + " in face " + adjFace);
231  System.out.println(Arrays.asList(adjFaceEdges));
232  continue;
233  }
234  Edge adjEdge = adjFaceEdges[adjEdgeInd];
235 
236  if (edge.isSmooth(adjEdge)) {
237  if (!smoothEdges.containsKey(edge)) {
238  smoothEdges.put(edge, adjFaces);
239  }
240  }
241  }
242  }
243  return smoothEdges;
244  }
245 
252  private List<List<Integer>> calcConnComponents(Map<Edge, List<Integer>> smoothEdges) {
253  //System.out.println("smoothEdges = " + smoothEdges);
254  List<List<Integer>> groups = new ArrayList<List<Integer>>();
255  while (hasNextConnectedComponent()) {
256  List<Integer> smoothGroup = getNextConnectedComponent(smoothEdges);
257  groups.add(smoothGroup);
258  }
259  return groups;
260  }
261 
268  private int[] generateSmGroups(List<List<Integer>> groups) {
269  int[] smGroups = new int[faceNormals.length];
270  int curGroup = 0;
271  for (int i = 0; i < groups.size(); i++) {
272  List<Integer> list = groups.get(i);
273  if (list.size() == 1) {
274  smGroups[list.get(0)] = 0;
275  } else {
276  for (int j = 0; j < list.size(); j++) {
277  Integer faceIndex = list.get(j);
278  smGroups[faceIndex] = 1 << curGroup;
279  }
280  if (curGroup++ == 31) {
281  curGroup = 0;
282  }
283  }
284  }
285  return smGroups;
286  }
287 
293  private int[] calcSmoothGroups() {
295 
296  // edge -> [faces]
297  Map<Edge, List<Integer>> adjacentFaces = getAdjacentFaces();
298 
299  // smooth edge -> [faces]
300  Map<Edge, List<Integer>> smoothEdges = getSmoothEdges(adjacentFaces);
301 
302  //System.out.println("smoothEdges = " + smoothEdges);
303  List<List<Integer>> groups = calcConnComponents(smoothEdges);
304 
305  return generateSmGroups(groups);
306  }
307 
311  private class Edge {
312 
314  int from, to;
315 
317  int fromNormal, toNormal;
318 
327  public Edge(int from, int to, int fromNormal, int toNormal) {
328  this.from = Math.min(from, to);
329  this.to = Math.max(from, to);
330  this.fromNormal = Math.min(fromNormal, toNormal);
331  this.toNormal = Math.max(fromNormal, toNormal);
332  }
333 
340  public boolean isSmooth(Edge edge) {
341  boolean smooth = (isNormalsEqual(getNormal(fromNormal), getNormal(edge.fromNormal)) && isNormalsEqual(getNormal(toNormal), getNormal(edge.toNormal))) ||
342  (isNormalsEqual(getNormal(fromNormal), getNormal(edge.toNormal)) && isNormalsEqual(getNormal(toNormal), getNormal(edge.fromNormal)));
343  return smooth;
344  }
345 
346  /* (non-Javadoc)
347  * @see java.lang.Object#hashCode()
348  */
349  @Override
350  public int hashCode() {
351  int hash = 7;
352  hash = 41 * hash + this.from;
353  hash = 41 * hash + this.to;
354  return hash;
355  }
356 
357  /* (non-Javadoc)
358  * @see java.lang.Object#equals(java.lang.Object)
359  */
360  @Override
361  public boolean equals(Object obj) {
362  if (obj == null) {
363  return false;
364  }
365  if (getClass() != obj.getClass()) {
366  return false;
367  }
368  final Edge other = (Edge) obj;
369  if (this.from != other.from) {
370  return false;
371  }
372  if (this.to != other.to) {
373  return false;
374  }
375  return true;
376  }
377  }
378 
387  public static int[] calcSmoothGroups(int[][] faces, int[][] faceNormals, float[] normals) {
389  return smoothGroups.calcSmoothGroups();
390  }
391 
401  public static int[] calcSmoothGroups(TriangleMesh mesh, int[] flatFaces, int[] flatFaceNormals, float[] normals) {
402  int faceElementSize = mesh.getFaceElementSize();
403  int[][] faces = new int[flatFaces.length/faceElementSize][faceElementSize];
404  for (int f = 0; f < faces.length; f++) {
405  for (int e = 0; e < faceElementSize; e++) {
406  faces[f][e] = flatFaces[f * faceElementSize + e];
407  }
408  }
409  int pointElementSize = mesh.getPointElementSize();
410  int[][] faceNormals = new int[flatFaceNormals.length/pointElementSize][pointElementSize];
411  for (int f = 0; f < faceNormals.length; f++) {
412  for (int e = 0; e < pointElementSize; e++) {
413  faceNormals[f][e] = flatFaceNormals[f * pointElementSize + e];
414  }
415  }
417  return smoothGroups.calcSmoothGroups();
418  }
419 }
Edge(int from, int to, int fromNormal, int toNormal)
List< List< Integer > > calcConnComponents(Map< Edge, List< Integer >> smoothEdges)
static boolean isNormalsEqual(Vector3f n1, Vector3f n2)
static int[] calcSmoothGroups(TriangleMesh mesh, int[] flatFaces, int[] flatFaceNormals, float[] normals)
SmoothingGroups(int faces[][], int[][] faceNormals, float[] normals)
Map< Edge, List< Integer > > getSmoothEdges(Map< Edge, List< Integer >> adjacentFaces)
List< Integer > getNextConnectedComponent(Map< Edge, List< Integer >> adjacentFaces)
static int[] calcSmoothGroups(int[][] faces, int[][] faceNormals, float[] normals)