/*
* 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 2007, by EADS France
*/
package org.jcae.viewer3d.cad.occ;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import javax.vecmath.Matrix4d;
import javax.vecmath.Point3d;
import org.jcae.opencascade.jni.*;
import org.jcae.viewer3d.cad.CADDomainAdaptor;
import org.jcae.viewer3d.cad.DefaultFaceMesh;
import org.jcae.viewer3d.cad.FaceMesh;
/**
* Meshing parameters were taken from Opencascade sources. See
* <ul>
* <li>src/AIS/AIS_TexturedShape.cxx</li>
* <li>src/AIS/AIS_Shape.cxx</li>
* <li>src/Prs3d/Prs3d_Drawer.cxx</li>
* <li>src/BRepMesh/BRepMesh.cxx</li>
* <li>inc/BRepMesh_IncrementalMesh.hxx</li>
* </ul>
* @author Jerome Robert
*
*/
public class OCCFaceDomain extends CADDomainAdaptor
{
private static int meshIter=3;
private ArrayList<FaceMesh> faceMeshes;
private Color[] facesColors;
private static boolean debug=false;
public OCCFaceDomain(TopoDS_Shape shape)
{
TopExp_Explorer explorer = new TopExp_Explorer();
TopLoc_Location loc = new TopLoc_Location();
faceMeshes=new ArrayList<FaceMesh>();
for (explorer.init(shape, TopAbs_ShapeEnum.FACE); explorer.more(); explorer.next())
{
TopoDS_Shape s = explorer.current();
if (!(s instanceof TopoDS_Face)) continue; // should not happen!
TopoDS_Face face = (TopoDS_Face)s;
Poly_Triangulation pt = BRep_Tool.triangulation(face,loc);
float error=0.001f*getMaxBound(s)*4;
//float error=0.0001f;
int iter=0;
while((pt==null)&(iter<meshIter)){
new BRepMesh_IncrementalMesh(face,error, false);
//new BRepMesh_IncrementalMesh(face,error, true);
pt = BRep_Tool.triangulation(face,loc);
error/=10;
iter++;
}
/*if(pt==null)
{
//System.err.println("Meshing "+face);
//BRepMesh.mesh(face, boundingBoxDeflection);
new BRepMesh_IncrementalMesh(face, 0.1, true);
pt = BRep_Tool.triangulation(face,loc);
} */
if (pt==null)
{
System.err.println("Triangulation failed for face "+face+". Trying other mesh parameters.");
faceMeshes.add(new DefaultFaceMesh(new float[0], new int[0]));
continue;
/*BRepMesh_IncrementalMesh mesh = new BRepMesh_IncrementalMesh();
mesh.setAngle(angle);
mesh.setRatio(ratio);
mesh.update(face);
pt = BRep_Tool.triangulation(face,loc);
if (pt==null)
{
System.err.println("Cannot triangulate face "+face);
continue;
}*/
}
else if(debug){
System.out.println("Triangulation succed for face "+face);
}
double[] dnodes = pt.nodes();
final int[] itriangles = pt.triangles();
/*double[] point=new double[3];
double[] meshNormal=computeMeshNormal(dnodes, itriangles, point);
double[] cadNormal=getNormal(point, face);
System.out.println("meshNormal "+meshNormal[0]+" "+meshNormal[1]+" "+meshNormal[2]);
System.out.println("cadNormal "+cadNormal[0]+" "+cadNormal[1]+" "+cadNormal[2]);
if(scalarProduct(meshNormal, cadNormal)<0)
{
System.out.println("reverseMesh");
reverseMesh(itriangles);
}*/
if(face.orientation()==TopAbs_Orientation.REVERSED)
{
reverseMesh(itriangles);
}
final float[] fnodes=new float[dnodes.length];
if(loc.isIdentity())
{
for(int i=0; i<dnodes.length; i++)
{
fnodes[i]=(float) dnodes[i];
}
}
else
transformMesh(loc, dnodes, fnodes);
faceMeshes.add(new DefaultFaceMesh(fnodes, itriangles));
}
}
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;
}
}
/**
* Compute the bounding box of the shape and
* return the maximum bound value
* @param shape
* @return
*/
private static float getMaxBound(TopoDS_Shape shape){
Bnd_Box box = new Bnd_Box();
BRepBndLib.add(shape,box);
double[] bbox = box.get();
double minBoundingBox=
Math.max(Math.max(bbox[3]-bbox[0], bbox[4]-bbox[1]), bbox[5]-bbox[2]);
return (float)minBoundingBox;
}
/**
* Create a Colored Shape
* @param shape
* @param facesColors
*/
public OCCFaceDomain(TopoDS_Shape shape,Color[] facesColors){
this(shape);
this.facesColors=facesColors;
}
/**
* @param itriangles
*/
static private void reverseMesh(int[] itriangles)
{
int tmp;
for(int i=0; i<itriangles.length; i+=3)
{
tmp=itriangles[i];
itriangles[i]=itriangles[i+1];
itriangles[i+1]=tmp;
}
}
static private double scalarProduct(double[] v1, double[] v2)
{
return v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2];
}
/**
*
* @param nodes the nodes of the mesh
* @param trias the triangles of the mesh
* @param point double[3] array which will receive the point were
* the normal is computed
* @return The normal a the point <code>point</code>
*/
static private double[] computeMeshNormal(double[] nodes, int[] trias, double[] point)
{
double[] toReturn=new double[3];
int index=(trias.length/6)*3;
double[] v1=new double[3];
double[] v2=new double[3];
v1[0]=nodes[trias[index]*3]-nodes[trias[index+1]*3];
v1[1]=nodes[trias[index]*3+1]-nodes[trias[index+1]*3+1];
v1[2]=nodes[trias[index]*3+2]-nodes[trias[index+1]*3+2];
v2[0]=nodes[trias[index+2]*3]-nodes[trias[index+1]*3];
v2[1]=nodes[trias[index+2]*3+1]-nodes[trias[index+1]*3+1];
v2[2]=nodes[trias[index+2]*3+2]-nodes[trias[index+1]*3+2];
//compute center of the triangle
point[0]=nodes[trias[index]*3]+nodes[trias[index]*3+1]+nodes[trias[index]*3+2];
point[1]=nodes[trias[index+1]*3]+nodes[trias[index+1]*3+1]+nodes[trias[index+1]*3+2];
point[2]=nodes[trias[index+2]*3]+nodes[trias[index+2]*3+1]+nodes[trias[index+2]*3+2];
/*System.out.println("v1 "+v1[0]+" "+v1[1]+" "+v1[2]);
System.out.println("v2 "+v2[0]+" "+v2[1]+" "+v2[2]);
System.out.println("point "+point[0]+" "+point[1]+" "+point[2]);*/
//compute the normal
toReturn[0]=(v1[1]*v2[2])-(v1[2]*v2[1]);
toReturn[1]=(v2[0]*v1[2])-(v2[2]*v1[0]);
toReturn[2]=(v1[0]*v2[1])-(v1[1]*v2[0]);
return toReturn;
}
static private double[] getNormal(double[] point, TopoDS_Face face)
{
Geom_Surface gs=BRep_Tool.surface(face);
GeomLProp_SLProps myLprop = new GeomLProp_SLProps(2, 0.0001);
myLprop.setSurface(gs);
GeomAPI_ProjectPointOnSurf pof=new GeomAPI_ProjectPointOnSurf(point, gs);
double[] uv=new double[2];
pof.lowerDistanceParameters(uv);
myLprop.setParameters(uv[0], uv[1]);
double[] toReturn=myLprop.normal();
if(face.orientation()==TopAbs_Orientation.REVERSED)
{
toReturn[0]*=-1;
toReturn[1]*=-1;
toReturn[2]*=-1;
}
return toReturn;
}
/* (non-Javadoc)
* @see org.jcae.viewer3d.cad.CADDomainAdaptor#getFaceIterator()
*/
@Override
public Iterator<FaceMesh> getFaceIterator()
{
return faceMeshes.iterator();
}
/**
* Return the Color of the ith Face of the iterator
* @param i
* @return
*/
public Color getFaceColor(int i){
if(facesColors==null) return null;
return facesColors[i];
}
}