JavaCAD
SymbolicSubdivisionBuilder.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.shape3d.symbolic;
33 
34 import eu.mihosoft.vrl.v3d.ext.openjfx.shape3d.SubdivisionMesh.BoundaryMode;
35 import eu.mihosoft.vrl.v3d.ext.openjfx.shape3d.SubdivisionMesh.MapBorderMode;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Set;
42 import javafx.geometry.Point2D;
43 
44 
45 // TODO: Auto-generated Javadoc
50 
53 
55  private Map<Edge, EdgeInfo> edgeInfos;
56 
58  private FaceInfo[] faceInfos;
59 
61  private PointInfo[] pointInfos;
62 
65 
67  private float[] texCoords;
68 
70  private int[] reindex;
71 
73  private int newTexCoordIndex;
74 
77 
80 
89  this.oldMesh = oldMesh;
90  this.boundaryMode = boundaryMode;
91  this.mapBorderMode = mapBorderMode;
92  }
93 
100  collectInfo();
101 
102  texCoords = new float[(oldMesh.getNumEdgesInFaces() * 3 + oldMesh.faces.length) * 2];
103  int[][] faces = new int[oldMesh.getNumEdgesInFaces()][8];
104  int[] faceSmoothingGroups = new int[oldMesh.getNumEdgesInFaces()];
105  newTexCoordIndex = 0;
106  reindex = new int[oldMesh.points.numPoints]; // indexes incremented by 1, 0 reserved for empty
107 
108  // face points first
109  int newFacesInd = 0;
110  for (int f = 0; f < oldMesh.faces.length; f++) {
111  FaceInfo faceInfo = faceInfos[f];
112  int[] oldFaces = oldMesh.faces[f];
113  for (int p = 0; p < oldFaces.length; p += 2) {
114  faces[newFacesInd][4] = getPointNewIndex(faceInfo);
115  faces[newFacesInd][5] = getTexCoordNewIndex(faceInfo);
116  faceSmoothingGroups[newFacesInd] = oldMesh.faceSmoothingGroups[f];
117  newFacesInd++;
118  }
119  }
120  // then, add edge points
121  newFacesInd = 0;
122  for (int f = 0; f < oldMesh.faces.length; f++) {
123  FaceInfo faceInfo = faceInfos[f];
124  int[] oldFaces = oldMesh.faces[f];
125  for (int p = 0; p < oldFaces.length; p += 2) {
126  faces[newFacesInd][2] = getPointNewIndex(faceInfo, (p / 2 + 1) % faceInfo.edges.length);
127  faces[newFacesInd][3] = getTexCoordNewIndex(faceInfo, (p / 2 + 1) % faceInfo.edges.length);
128  faces[newFacesInd][6] = getPointNewIndex(faceInfo, p / 2);
129  faces[newFacesInd][7] = getTexCoordNewIndex(faceInfo, p / 2);
130  newFacesInd++;
131  }
132  }
133  // finally, add control points
134  newFacesInd = 0;
135  for (int f = 0; f < oldMesh.faces.length; f++) {
136  FaceInfo faceInfo = faceInfos[f];
137  int[] oldFaces = oldMesh.faces[f];
138  for (int p = 0; p < oldFaces.length; p += 2) {
139  faces[newFacesInd][0] = getPointNewIndex(oldFaces[p]);
140  faces[newFacesInd][1] = getTexCoordNewIndex(faceInfo, oldFaces[p], oldFaces[p+1]);
141  newFacesInd++;
142  }
143  }
144 
145  SymbolicPolygonMesh newMesh = new SymbolicPolygonMesh(points, texCoords, faces, faceSmoothingGroups);
146  return newMesh;
147  }
148 
159  return subdivision.subdivide();
160  }
161 
168  private void addEdge(Edge edge, FaceInfo faceInfo) {
169  EdgeInfo edgeInfo = edgeInfos.get(edge);
170  if (edgeInfo == null) {
171  edgeInfo = new EdgeInfo();
172  edgeInfo.edge = edge;
173  edgeInfos.put(edge, edgeInfo);
174  }
175  edgeInfo.faces.add(faceInfo);
176  }
177 
185  private void addPoint(int point, FaceInfo faceInfo, Edge edge) {
186  PointInfo pointInfo = pointInfos[point];
187  if (pointInfo == null) {
188  pointInfo = new PointInfo();
189  pointInfos[point] = pointInfo;
190  }
191  pointInfo.edges.add(edge);
192  pointInfo.faces.add(faceInfo);
193  }
194 
201  private void addPoint(int point, Edge edge) {
202  PointInfo pointInfo = pointInfos[point];
203  if (pointInfo == null) {
204  pointInfo = new PointInfo();
205  pointInfos[point] = pointInfo;
206  }
207  pointInfo.edges.add(edge);
208  }
209 
213  private void collectInfo() {
214  edgeInfos = new HashMap<>(oldMesh.faces.length * 2);
215  faceInfos = new FaceInfo[oldMesh.faces.length];
217 
218  for (int f = 0; f < oldMesh.faces.length; f++) {
219  int[] face = oldMesh.faces[f];
220  int n = face.length / 2;
221  FaceInfo faceInfo = new FaceInfo(n);
222  faceInfos[f] = faceInfo;
223  if (n < 3) {
224  continue;
225  }
226  int from = face[(n-1) * 2];
227  int texFrom = face[(n-1) * 2 + 1];
228  double fu, fv;
229  double tu, tv;
230  double u = 0, v = 0;
231  fu = oldMesh.texCoords[texFrom * 2];
232  fv = oldMesh.texCoords[texFrom * 2 + 1];
233  for (int i = 0; i < n; i++) {
234  int to = face[i * 2];
235  int texTo = face[i * 2 + 1];
236  tu = oldMesh.texCoords[texTo * 2];
237  tv = oldMesh.texCoords[texTo * 2 + 1];
238  Point2D midTexCoord = new Point2D((fu + tu) / 2, (fv + tv) / 2);
239  Edge edge = new Edge(from, to);
240  faceInfo.edges[i] = edge;
241  faceInfo.edgeTexCoords[i] = midTexCoord;
242  addEdge(edge, faceInfo);
243  addPoint(to, faceInfo, edge);
244  addPoint(from, edge);
245  fu = tu; fv = tv;
246  u += tu / n; v += tv / n;
247  from = to;
248  texFrom = texTo;
249  }
250  faceInfo.texCoord = new Point2D(u, v);
251  }
252 
254 
255  for (int f = 0; f < oldMesh.faces.length; f++) {
256  int[] face = oldMesh.faces[f];
257  int n = face.length / 2;
258  int[] faceVertices = new int[n];
259  for (int i = 0; i < n; i++) {
260  faceVertices[i] = face[i * 2];
261  }
262  faceInfos[f].facePoint = points.addFacePoint(faceVertices);
263  }
264 
265  for(EdgeInfo edgeInfo : edgeInfos.values()) {
266  int[] edgeFacePoints = new int[edgeInfo.faces.size()];
267  for (int f = 0; f < edgeInfo.faces.size(); f++) {
268  edgeFacePoints[f] = edgeInfo.faces.get(f).facePoint;
269  }
270  edgeInfo.edgePoint = points.addEdgePoint(edgeFacePoints, edgeInfo.edge.from, edgeInfo.edge.to, edgeInfo.isBoundary());
271  }
272  }
273 
280  private int calcControlPoint(int srcPointIndex) {
281  PointInfo pointInfo = pointInfos[srcPointIndex];
282  int origPoint = srcPointIndex;
283 
284  int[] facePoints = new int[pointInfo.faces.size()];
285  for (int f = 0; f < facePoints.length; f++) {
286  facePoints[f] = pointInfo.faces.get(f).facePoint;
287  }
288  int[] edgePoints = new int[pointInfo.edges.size()];
289  boolean[] isEdgeBoundary = new boolean[pointInfo.edges.size()];
290  int[] fromEdgePoints = new int[pointInfo.edges.size()];
291  int[] toEdgePoints = new int[pointInfo.edges.size()];
292  int i = 0;
293  for (Edge edge : pointInfo.edges) {
294  EdgeInfo edgeInfo = edgeInfos.get(edge);
295  edgePoints[i] = edgeInfo.edgePoint;
296  isEdgeBoundary[i] = edgeInfo.isBoundary();
297  fromEdgePoints[i] = edgeInfo.edge.from;
298  toEdgePoints[i] = edgeInfo.edge.to;
299  i++;
300  }
301  int destPointIndex = points.addControlPoint(facePoints, edgePoints, fromEdgePoints, toEdgePoints, isEdgeBoundary, origPoint, pointInfo.isBoundary(), pointInfo.hasInternalEdge());
302  return destPointIndex;
303  }
304 
313  private void calcControlTexCoord(FaceInfo faceInfo, int srcPointIndex, int srcTexCoordIndex, int destTexCoordIndex){
314  PointInfo pointInfo = pointInfos[srcPointIndex];
315  boolean pointBelongsToCrease = oldMesh.points instanceof OriginalPointArray;
316  if ((mapBorderMode == MapBorderMode.SMOOTH_ALL && (pointInfo.isBoundary() || pointBelongsToCrease)) ||
318  double u = oldMesh.texCoords[srcTexCoordIndex * 2] / 2;
319  double v = oldMesh.texCoords[srcTexCoordIndex * 2 + 1] / 2;
320  for (int i = 0; i < faceInfo.edges.length; i++) {
321  if ((faceInfo.edges[i].to == srcPointIndex) || (faceInfo.edges[i].from == srcPointIndex)) {
322  u += faceInfo.edgeTexCoords[i].getX() / 4;
323  v += faceInfo.edgeTexCoords[i].getY() / 4;
324  }
325  }
326  texCoords[destTexCoordIndex * 2] = (float) u;
327  texCoords[destTexCoordIndex * 2 + 1] = (float) v;
328  } else {
329  texCoords[destTexCoordIndex * 2] = oldMesh.texCoords[srcTexCoordIndex * 2];
330  texCoords[destTexCoordIndex * 2 + 1] = oldMesh.texCoords[srcTexCoordIndex * 2 + 1];
331  }
332  }
333 
340  private int getPointNewIndex(int srcPointIndex) {
341  int destPointIndex = reindex[srcPointIndex] - 1;
342  if (destPointIndex == -1) {
343  destPointIndex = calcControlPoint(srcPointIndex);
344  reindex[srcPointIndex] = destPointIndex + 1;
345  }
346  return destPointIndex;
347  }
348 
356  private int getPointNewIndex(FaceInfo faceInfo, int edgeInd) {
357  Edge edge = faceInfo.edges[edgeInd];
358  EdgeInfo edgeInfo = edgeInfos.get(edge);
359  return edgeInfo.edgePoint;
360  }
361 
368  private int getPointNewIndex(FaceInfo faceInfo) {
369  return faceInfo.facePoint;
370  }
371 
380  private int getTexCoordNewIndex(FaceInfo faceInfo, int srcPointIndex, int srcTexCoordIndex) {
381  int destTexCoordIndex = newTexCoordIndex;
383  calcControlTexCoord(faceInfo, srcPointIndex, srcTexCoordIndex, destTexCoordIndex);
384  return destTexCoordIndex;
385  }
386 
394  private int getTexCoordNewIndex(FaceInfo faceInfo, int edgeInd) {
395  int destTexCoordIndex = newTexCoordIndex;
397  texCoords[destTexCoordIndex * 2] = (float) faceInfo.edgeTexCoords[edgeInd].getX();
398  texCoords[destTexCoordIndex * 2 + 1] = (float) faceInfo.edgeTexCoords[edgeInd].getY();
399  return destTexCoordIndex;
400  }
401 
408  private int getTexCoordNewIndex(FaceInfo faceInfo) {
409  int destTexCoordIndex = faceInfo.newTexCoordIndex - 1;
410  if (destTexCoordIndex == -1) {
411  destTexCoordIndex = newTexCoordIndex;
412  faceInfo.newTexCoordIndex = destTexCoordIndex + 1;
414  texCoords[destTexCoordIndex * 2] = (float) faceInfo.texCoord.getX();
415  texCoords[destTexCoordIndex * 2 + 1] = (float) faceInfo.texCoord.getY();
416  }
417  return destTexCoordIndex;
418  }
419 
423  private static class Edge {
424 
426  int from, to;
427 
434  public Edge(int from, int to) {
435  this.from = Math.min(from, to);
436  this.to = Math.max(from, to);
437  }
438 
439  /* (non-Javadoc)
440  * @see java.lang.Object#hashCode()
441  */
442  @Override
443  public int hashCode() {
444  int hash = 7;
445  hash = 41 * hash + this.from;
446  hash = 41 * hash + this.to;
447  return hash;
448  }
449 
450  /* (non-Javadoc)
451  * @see java.lang.Object#equals(java.lang.Object)
452  */
453  @Override
454  public boolean equals(Object obj) {
455  if (obj == null) {
456  return false;
457  }
458  if (getClass() != obj.getClass()) {
459  return false;
460  }
461  final Edge other = (Edge) obj;
462  if (this.from != other.from) {
463  return false;
464  }
465  if (this.to != other.to) {
466  return false;
467  }
468  return true;
469  }
470  }
471 
475  private static class EdgeInfo {
476 
478  Edge edge;
479 
481  int edgePoint;
482 
484  List<FaceInfo> faces = new ArrayList<>(2);
485 
491  public boolean isBoundary() {
492  return faces.size() == 1;
493  }
494  }
495 
499  private class PointInfo {
500 
502  List<FaceInfo> faces = new ArrayList<>(4);
503 
505  Set<Edge> edges = new HashSet<>(4);
506 
512  public boolean isBoundary() {
513  for (Edge edge : edges) {
514  EdgeInfo edgeInfo = edgeInfos.get(edge);
515  if (edgeInfo.isBoundary())
516  return true;
517  }
518  return false;
519  }
520 
526  public boolean hasInternalEdge() {
527  for (Edge edge : edges) {
528  EdgeInfo edgeInfo = edgeInfos.get(edge);
529  if (!edgeInfo.isBoundary())
530  return true;
531  }
532  return false;
533  }
534  }
535 
539  private static class FaceInfo {
540 
542  int facePoint;
543 
545  Point2D texCoord;
546 
548  int newTexCoordIndex;
549 
551  Edge[] edges;
552 
554  Point2D[] edgeTexCoords;
555 
561  public FaceInfo(int n) {
562  edges = new Edge[n];
563  edgeTexCoords = new Point2D[n];
564  }
565  }
566 }
int addEdgePoint(int[] facePoints, int fromPoint, int toPoint, boolean isBoundary)
int addControlPoint(int[] facePoints, int[] edgePoints, int[] fromEdgePoints, int[] toEdgePoints, boolean[] isEdgeBoundary, int origPoint, boolean isBoundary, boolean hasInternalEdge)
SymbolicSubdivisionBuilder(SymbolicPolygonMesh oldMesh, BoundaryMode boundaryMode, MapBorderMode mapBorderMode)
int getTexCoordNewIndex(FaceInfo faceInfo, int srcPointIndex, int srcTexCoordIndex)
void calcControlTexCoord(FaceInfo faceInfo, int srcPointIndex, int srcTexCoordIndex, int destTexCoordIndex)
static SymbolicPolygonMesh subdivide(SymbolicPolygonMesh oldMesh, BoundaryMode boundaryMode, MapBorderMode mapBorderMode)