/* * Project Info: http://jcae.sourceforge.net * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * (C) Copyright 2008, by EADS France */ package org.jcae.vtk; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import org.jcae.opencascade.Utilities; import org.jcae.opencascade.jni.BRepBndLib; import org.jcae.opencascade.jni.BRepMesh_IncrementalMesh; import org.jcae.opencascade.jni.BRepTools; import org.jcae.opencascade.jni.BRep_Tool; import org.jcae.opencascade.jni.Bnd_Box; import org.jcae.opencascade.jni.GCPnts_UniformDeflection; import org.jcae.opencascade.jni.GeomAdaptor_Curve; import org.jcae.opencascade.jni.GeomLProp_SLProps; import org.jcae.opencascade.jni.Geom_Curve; import org.jcae.opencascade.jni.Geom_Surface; import org.jcae.opencascade.jni.Poly_Triangulation; import org.jcae.opencascade.jni.ShapeAnalysis_FreeBounds; import org.jcae.opencascade.jni.TopAbs_Orientation; import org.jcae.opencascade.jni.TopAbs_ShapeEnum; import org.jcae.opencascade.jni.TopExp_Explorer; import org.jcae.opencascade.jni.TopLoc_Location; import org.jcae.opencascade.jni.TopoDS_Compound; import org.jcae.opencascade.jni.TopoDS_Edge; import org.jcae.opencascade.jni.TopoDS_Face; import org.jcae.opencascade.jni.TopoDS_Shape; import org.jcae.opencascade.jni.TopoDS_Vertex; /** * This class compute different type of meshes (vertices, edges, faces...) from * a TopoDS_Shape. * @author Julian Ibarz */ //TODO try to remove dependency over java3d (javax.vecmath.*) public class OCCMeshExtractor { public static class VertexData extends LeafNode.DataProvider { private final TopoDS_Vertex vertex; public VertexData(final TopoDS_Vertex vertex) { this.vertex = vertex; } @Override public void load() { float[] newNodes = new float[3]; double[] pnt = BRep_Tool.pnt(vertex); newNodes[0] = (float) pnt[0]; newNodes[1] = (float) pnt[1]; newNodes[2] = (float) pnt[2]; setNodes(newNodes); vertices = new int[2]; vertices[0] = 1; vertices[1] = 0; nbrOfVertices = 1; } } public static class EdgeData extends LeafNode.DataProvider { private final TopoDS_Edge edge; public EdgeData(final TopoDS_Edge edge) { this.edge = edge; } @Override public void load() { double[] range = BRep_Tool.range(edge); Geom_Curve gc = BRep_Tool.curve(edge, range); Bnd_Box box = new Bnd_Box(); BRepBndLib.add(edge, box); double[] bbox = box.get(); double boundingBoxDeflection = 0.0005 * Math.max(Math.max(bbox[3] - bbox[0], bbox[4] - bbox[1]), bbox[5] - bbox[2]); float[] newNodes = null; if (gc != null) { GeomAdaptor_Curve adaptator = new GeomAdaptor_Curve(gc); GCPnts_UniformDeflection deflector = new GCPnts_UniformDeflection(); deflector.initialize(adaptator, boundingBoxDeflection, range[0], range[1]); int npts = deflector.nbPoints(); // Allocate one additional point at each end = parametric value 0, 1 newNodes = new float[(npts + 2) * 3]; int j = 0; double[] values = adaptator.value(range[0]); newNodes[j++] = (float) values[0]; newNodes[j++] = (float) values[1]; newNodes[j++] = (float) values[2]; // All intermediary points for (int i = 0; i < npts; ++i) { values = adaptator.value(deflector.parameter(i + 1)); newNodes[j++] = (float) values[0]; newNodes[j++] = (float) values[1]; newNodes[j++] = (float) values[2]; } // Add last point values = adaptator.value(range[1]); newNodes[j++] = (float) values[0]; newNodes[j++] = (float) values[1]; newNodes[j++] = (float) values[2]; } else if (!BRep_Tool.degenerated(edge)) { // So, there is no curve, and the edge is not degenerated? // => draw lines between the vertices and ignore curvature // best approximation we can do ArrayList<double[]> aa = new ArrayList<double[]>(); // store points here for (TopExp_Explorer explorer2 = new TopExp_Explorer(edge, TopAbs_ShapeEnum.VERTEX); explorer2.more(); explorer2.next()) { TopoDS_Shape sv = explorer2.current(); if (!(sv instanceof TopoDS_Vertex)) continue; // should not happen! TopoDS_Vertex v = (TopoDS_Vertex) sv; aa.add(BRep_Tool.pnt(v)); } newNodes = new float[aa.size() * 3]; for (int i = 0, j = 0; i < aa.size(); i++) { double[] f = aa.get(i); newNodes[j++] = (float) f[0]; newNodes[j++] = (float) f[1]; newNodes[j++] = (float) f[2]; } } if(newNodes != null) { setNodes(newNodes); } if (newNodes == null || newNodes.length == 0) { unLoad(); return; } // Construct the indices nbrOfLines = newNodes.length / 3 - 1; lines = new int[nbrOfLines * 3]; int offset = 0; for (int i = 0; i < nbrOfLines;) { lines[offset++] = 2; lines[offset++] = i++; lines[offset++] = i; } } } public static class FaceData extends LeafNode.DataProvider { private final TopoDS_Face face; private final boolean faceReversed; public FaceData(final TopoDS_Face face, boolean faceReversed) { this.face = face; this.faceReversed = faceReversed; } private boolean checkNormals(double[] normals) { for (int i = 0; i < normals.length / 3; i++) if (normals[3 * i] == 0 & normals[3 * i + 1] == 0 & normals[3 * i + 2] == 0) return false; return true; } private void transformMesh(TopLoc_Location loc, double[] src, float[] dst) { double[] matrix = new double[16]; loc.transformation().getValues(matrix); Matrix4d m4d = new Matrix4d(matrix); Point3d p3d = new Point3d(); for (int i = 0; i < src.length; i += 3) { p3d.x = src[i + 0]; p3d.y = src[i + 1]; p3d.z = src[i + 2]; m4d.transform(p3d); dst[i + 0] = (float) p3d.x; dst[i + 1] = (float) p3d.y; dst[i + 2] = (float) p3d.z; } } /** * @param itriangles */ static private void reverseMesh(int[] itriangles) { for (int i = 0; i < itriangles.length; i += 3) { int tmp = itriangles[i]; itriangles[i] = itriangles[i + 1]; itriangles[i + 1] = tmp; } } @Override public void load() { TopLoc_Location loc = new TopLoc_Location(); Poly_Triangulation pt = BRep_Tool.triangulation(face, loc); float[] newNodes = null; if (pt == null) { System.err.println("Triangulation failed for face " + face + ". Trying other mesh parameters."); newNodes = new float[0]; polys = new int[0]; normals = new float[0]; return; } double[] dnodes = pt.nodes(); final int[] itriangles = pt.triangles(); if ((face.orientation() == TopAbs_Orientation.REVERSED && !this.faceReversed) || (face.orientation() != TopAbs_Orientation.REVERSED && this.faceReversed) ) reverseMesh(itriangles); // Compute the indices nbrOfPolys = itriangles.length / 3; polys = new int[4 * nbrOfPolys]; int offset = 0; for (int i = 0; i < itriangles.length;) { polys[offset++] = 3; polys[offset++] = itriangles[i++]; polys[offset++] = itriangles[i++]; polys[offset++] = itriangles[i++]; } newNodes = new float[dnodes.length]; if (loc.isIdentity()) { for (int j = 0; j < dnodes.length; j++) newNodes[j] = (float) dnodes[j]; } else transformMesh(loc, dnodes, newNodes); setNodes(newNodes); // Compute the normals Geom_Surface surf = BRep_Tool.surface(face); if (surf != null) { GeomLProp_SLProps geomProp = new GeomLProp_SLProps(1, 0); geomProp.setSurface(surf); if (pt.hasUVNodes()) { double[] n = geomProp.normalArray(pt.uvNodes()); //check the normals if (!checkNormals(n)) { // TODO : To be checked normals = new float[n.length]; for (int i = 0; i < n.length; i++) normals[i] = (float) 0.; System.err.println("Normal computation failed " + face); } else { //convert into floats normals = new float[n.length]; // Inverse normal if the face is inversed double reverse = 1.; if ((face.orientation() == TopAbs_Orientation.REVERSED && !this.faceReversed) || (face.orientation() != TopAbs_Orientation.REVERSED && this.faceReversed)) reverse = -1.; for (int i = 0; i < n.length; i++) normals[i] = (float) (reverse * n[i]); } } else System.err.println("No UV Nodes to the point triangulation !"); } else System.err.println("Can not compute the Geom_Surface of " + face); } } private final TopoDS_Shape shape; private boolean meshCreated; /** * Create a CAOMeshExtractor from a TopoDS_Shape object * * @param shape */ public OCCMeshExtractor(TopoDS_Shape shape) { this.shape = shape; } /** * Create a CAOMeshExtractor from a BREP, STEP or IGES file. * @param fileName */ public OCCMeshExtractor(String fileName) { this(Utilities.readFile(fileName)); } public Collection<TopoDS_Vertex> getVertices() { TopExp_Explorer explorer = new TopExp_Explorer(); HashSet<TopoDS_Vertex> vertices = new HashSet<TopoDS_Vertex>(); for (explorer.init(shape, TopAbs_ShapeEnum.VERTEX); explorer.more(); explorer.next()) vertices.add((TopoDS_Vertex) explorer.current()); return vertices; } /** * Create the mesh by calling BRepMesh_IncrementalMesh * Override to call it with custom parameters */ protected void createMesh() { //Force to recreate the mesh with our parameters BRepTools.clean(shape); new BRepMesh_IncrementalMesh(shape, 7E-3, true); } public Collection<TopoDS_Face> getFaces() { if(!meshCreated) { createMesh(); meshCreated = true; } TopExp_Explorer explorer = new TopExp_Explorer(); HashSet<TopoDS_Face> faces = new LinkedHashSet<TopoDS_Face>(); for (explorer.init(shape, TopAbs_ShapeEnum.FACE); explorer.more(); explorer.next()) faces.add((TopoDS_Face) explorer.current()); return faces; } /** * Get only free edges * @return */ public Collection<TopoDS_Edge> getFreeEdges() { ShapeAnalysis_FreeBounds safb = new ShapeAnalysis_FreeBounds(shape); TopoDS_Compound closedWires = safb.getClosedWires(); if(closedWires == null) return Collections.EMPTY_SET; TopExp_Explorer explorer = new TopExp_Explorer(); HashSet<TopoDS_Edge> freeEdges = new HashSet<TopoDS_Edge>(); for(explorer.init(closedWires, TopAbs_ShapeEnum.EDGE); explorer.more() ; explorer.next()) freeEdges.add((TopoDS_Edge)explorer.current()); return freeEdges; } /** * Get all the edges (free edges or not) * @return */ public Collection<TopoDS_Edge> getEdges() { TopExp_Explorer explorer = new TopExp_Explorer(); HashSet<TopoDS_Edge> edges = new HashSet<TopoDS_Edge>(); for (explorer.init(shape, TopAbs_ShapeEnum.EDGE); explorer.more(); explorer.next()) edges.add((TopoDS_Edge) explorer.current()); return edges; } }