2 * Project Info: http://jcae.sourceforge.net
4 * This program is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License as published by the Free
6 * Software Foundation; either version 2.1 of the License, or (at your option)
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18 * (C) Copyright 2008,2009, by EADS France
20 package org.jcae.opencascade;
22 import java.io.PrintWriter;
23 import java.util.AbstractList;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
31 import java.util.NoSuchElementException;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35 import org.jcae.opencascade.jni.BRepBndLib;
36 import org.jcae.opencascade.jni.BRepBuilderAPI_MakeVertex;
37 import org.jcae.opencascade.jni.BRepBuilderAPI_Sewing;
38 import org.jcae.opencascade.jni.BRepTools;
39 import org.jcae.opencascade.jni.BRep_Builder;
40 import org.jcae.opencascade.jni.BRep_Tool;
41 import org.jcae.opencascade.jni.Bnd_Box;
42 import org.jcae.opencascade.jni.GeomAPI_ProjectPointOnSurf;
43 import org.jcae.opencascade.jni.Geom_Surface;
44 import org.jcae.opencascade.jni.TopAbs_ShapeEnum;
45 import org.jcae.opencascade.jni.TopExp_Explorer;
46 import org.jcae.opencascade.jni.TopoDS_Compound;
47 import org.jcae.opencascade.jni.TopoDS_Face;
48 import org.jcae.opencascade.jni.TopoDS_Iterator;
49 import org.jcae.opencascade.jni.TopoDS_Shape;
50 import org.jcae.opencascade.jni.TopoDS_Vertex;
51 import org.w3c.dom.Element;
52 import org.w3c.dom.Node;
53 import org.w3c.dom.NodeList;
56 * An abstraction level over TopoDS_Shape to easily decorate and serialize the
57 * Opencascade shape graph.
59 * This class use the <a href="http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern">curiously
60 * recurring template pattern</a> to enforce type safety; static polymorphism is
61 * checked when compiling, there is no cast at run time. In short, this recursive
62 * definition <code>class Shape<T extends Shape<T>></code> means that derived
63 * classes must be declared as <code>class Foo extends Shape<Foo></code>,
64 * one cannot declare <code>class Foo extends Shape<Bar></code>.
67 * When a class is declared like <code>class Foo extends Shape<Foo></code>,
68 * this means that all nodes of this graph are of class <code>Foo</code>.
69 * Thus {@link #children} and {@link #parents} members are declared as
70 * <code>T</code> arrays and not <code>Shape</code> arrays. Likewise, the map
71 * used in Shape constructor must be of type <code>Map<TopoDS_Shape, T></code>.
72 * In order to build a node with the right type, it is obvious that a factory
73 * is needed. This factory is parameterized, and the parameter is the derived
74 * class of the node being created. Under some circumstances, we sometimes
75 * need to downcast the current instance. This could be achieved by a method like
84 * But there is a better alternative, declare
86 * protected abstract T getDerived();
88 * in Shape, and let all derived classes declare
90 * protected T getDerived()
95 * @author Jerome Robert
97 public abstract class Shape<T extends Shape<T>> implements Comparable<T>
99 private static final Logger LOGGER = Logger.getLogger(Shape.class.getCanonicalName());
101 private static enum ListOfShapes {
102 COMPOUND (TopAbs_ShapeEnum.COMPOUND, "Compound", "co"),
103 COMPSOLID(TopAbs_ShapeEnum.COMPSOLID, "CompSolid", "cs"),
104 SOLID (TopAbs_ShapeEnum.SOLID, "Solid", "so"),
105 SHELL (TopAbs_ShapeEnum.SHELL, "Shell", "sh"),
106 FACE (TopAbs_ShapeEnum.FACE, "Face", "f"),
107 WIRE (TopAbs_ShapeEnum.WIRE, "Wire", "w"),
108 EDGE (TopAbs_ShapeEnum.EDGE, "Edge", "e"),
109 VERTEX (TopAbs_ShapeEnum.VERTEX, "Vertex", "v"),
110 SHAPE (TopAbs_ShapeEnum.SHAPE, "Shape", null);
112 TopAbs_ShapeEnum type;
115 private ListOfShapes(TopAbs_ShapeEnum type, String label, String xmlName)
119 this.xmlName = xmlName;
122 protected static GeomAPI_ProjectPointOnSurf projectPointOnSurf;
123 /** map TopoDS_Compound.class to "co" */
124 private final static Map<TopAbs_ShapeEnum, String> TYPE_MAP_XML;
125 /** map "co" to TopoDS_Compound.class */
126 private final static Map<String, TopAbs_ShapeEnum> TYPE_MAP_XML_INV;
127 /** map TopoDS_Compound.class to "Compound" */
128 private final static Map<TopAbs_ShapeEnum, String> TYPE_MAP_NAME;
129 private final static TopAbs_ShapeEnum[] TYPE;
130 private final static String[] TYPE_LABEL;
134 HashMap<TopAbs_ShapeEnum, String> m = new HashMap<TopAbs_ShapeEnum, String>();
135 HashMap<TopAbs_ShapeEnum, String> mm = new HashMap<TopAbs_ShapeEnum, String>();
136 HashMap<String, TopAbs_ShapeEnum> mi = new HashMap<String, TopAbs_ShapeEnum>();
137 ListOfShapes[] shapes = ListOfShapes.values();
138 TYPE = new TopAbs_ShapeEnum[shapes.length];
139 TYPE_LABEL = new String[shapes.length];
141 for(ListOfShapes s : shapes)
143 if (s != ListOfShapes.SHAPE)
145 m.put(s.type, s.xmlName);
146 mi.put(s.xmlName, s.type);
148 mm.put(s.type, s.label);
150 TYPE_LABEL[i] = s.label;
153 TYPE_MAP_XML=Collections.unmodifiableMap(m);
154 TYPE_MAP_NAME=Collections.unmodifiableMap(mm);
155 TYPE_MAP_XML_INV=Collections.unmodifiableMap(mi);
158 public interface Attributes
161 void fromXML(Element node);
164 protected interface Factory<T>
166 T create(TopoDS_Shape shape, Map<TopoDS_Shape, T> map, T[] parents);
167 T[] createArray(int length);
170 protected TopoDS_Shape impl;
171 private T[] children;
176 this((TopoDS_Shape)null);
179 protected Shape(TopoDS_Shape shape)
181 this(shape, new HashMap<TopoDS_Shape, T>(), null);
184 protected Shape(String fileName)
186 this(Utilities.readFile(fileName));
189 protected Shape(TopoDS_Shape shape, Map<TopoDS_Shape, T> map, T[] parents)
193 TopoDS_Compound c =new TopoDS_Compound();
194 new BRep_Builder().makeCompound(c);
200 parents = getFactory().createArray(0);
202 this.parents = parents;
204 for (TopoDS_Iterator it = new TopoDS_Iterator(impl); it.more(); it.next())
206 this.children = getFactory().createArray(cntChildren);
208 for (TopoDS_Iterator it = new TopoDS_Iterator(impl); it.more(); it.next())
210 TopoDS_Shape tds = it.value();
211 T css = map.get(tds);
214 T[] pArray = getFactory().createArray(1);
215 pArray[0] = getDerived();
216 css = getFactory().create(tds, map, pArray);
219 children[cntChildren] = css;
222 map.put(shape, getDerived());
225 public static String[] getLabels(TopAbs_ShapeEnum from)
227 int startIndex = from.ordinal();
228 String[] toReturn = new String[TopAbs_ShapeEnum.values().length - 1 - startIndex];
229 System.arraycopy(TYPE_LABEL, startIndex, toReturn, 0, toReturn.length);
233 protected abstract Factory<T> getFactory();
234 protected abstract T getDerived();
236 public void add(T newShape)
239 new BRep_Builder().add(impl, newShape.impl);
240 T[] nc = getFactory().createArray(children.length + 1);
241 System.arraycopy(children, 0, nc, 0, children.length);
242 nc[nc.length - 1] = newShape;
245 T[] np = newShape.getFactory().createArray(newShape.parents.length+1);
246 System.arraycopy(newShape.parents, 0, np, 0, np.length - 1);
247 np[np.length - 1]=getDerived();
248 newShape.parents = np;
252 * Add a Vertex to this shape
253 * @return the created vertex
255 public T addVertex(double[] coords)
257 TopoDS_Vertex v = (TopoDS_Vertex) new BRepBuilderAPI_MakeVertex(
259 T vs = getFactory().create(v, new HashMap<TopoDS_Shape, T>(), getFactory().createArray(0));
261 if(impl instanceof TopoDS_Face)
263 TopoDS_Face face = (TopoDS_Face) impl;
264 //Project p5 on surface
265 double [] uvTol = new double[3];
266 projectPoint(coords, uvTol);
267 new BRep_Builder().updateVertex(v, uvTol[0], uvTol[1], face, uvTol[2]);
273 * Project a point on surface.
274 * result[2] will contains Double.POSITIVE_INFINITY if no projection are
276 * @param result {u, v, distance, x, y, z}
277 * @throws ClassCastException if this shape is not a surface
279 public void projectPoint(double[] coords, double[] result)
281 TopoDS_Face face = (TopoDS_Face) impl;
282 Geom_Surface surface = BRep_Tool.surface(face);
283 if(projectPointOnSurf == null)
284 projectPointOnSurf = new GeomAPI_ProjectPointOnSurf(coords, surface);
286 projectPointOnSurf.init(coords, surface);
287 if(projectPointOnSurf.nbPoints()>0)
289 projectPointOnSurf.lowerDistanceParameters(result);
290 double[] p = projectPointOnSurf.nearestPoint();
291 result[2] = projectPointOnSurf.lowerDistance();
292 System.arraycopy(p, 0, result, 3, 3);
295 result[2] = Double.POSITIVE_INFINITY;
298 /** Remove this shape from its parents */
301 BRep_Builder bb = new BRep_Builder();
302 T shape = getDerived();
303 for(T parent : parents)
305 ArrayList<T> set = new ArrayList<T>(Arrays.asList(parent.children));
306 if(set.contains(shape))
308 parent.impl.free(true);
309 bb.remove(parent.impl, impl);
311 parent.children = set.toArray(getFactory().createArray(set.size()));
314 parents = getFactory().createArray(0);
317 public void reverse()
319 T[] parentSav = parents.clone();
326 //TODO remplace this by a "sew" method which keep track of attributes. See
327 // aseris-hf GUI for an example of that.
328 public T sewed(double tolerance, boolean option, boolean cutting, boolean manifold)
330 BRepBuilderAPI_Sewing sewer=new BRepBuilderAPI_Sewing();
331 sewer.init(tolerance, option, cutting, manifold);
334 return getFactory().create(sewer.sewedShape(), new HashMap<TopoDS_Shape, T>(), getFactory().createArray(0));
337 public void dump(PrintWriter writer)
339 int[] ids = new int[TopAbs_ShapeEnum.values().length - 1];
341 writer.println("<geometry>");
342 dump(writer, new HashSet<T>(), ids);
343 writer.println("</geometry>");
346 private void dump(PrintWriter writer, Set<T> shapeSet, int[] id)
348 T shape = getDerived();
349 if (!shapeSet.contains(shape))
351 int type = getType().ordinal();
353 if(getAttributes()!=null)
355 String e = TYPE_MAP_XML.get(impl.shapeType());
356 writer.println("<" + e + " id=\"" + id[type] + "\">");
357 writer.println(getAttributes().toXML());
358 writer.println("</"+e+">");
362 s.dump(writer, shapeSet, id);
366 public void load(Node node)
368 NodeList nodes = node.getChildNodes();
369 for(int i = 0, imax = nodes.getLength(); i < imax; i++)
371 Node n = nodes.item(i);
372 if(n.getNodeType() == Node.ELEMENT_NODE)
374 TopAbs_ShapeEnum type = TYPE_MAP_XML_INV.get(n.getNodeName());
375 Element e = (Element)n;
376 int id = Integer.parseInt(e.getAttribute("id"));
377 Shape s = getShapeFromID(id, type);
378 if(s.getAttributes()==null)
379 s.createAttributes();
380 s.getAttributes().fromXML(e);
386 * Return the ID of this shape, considering it's in a given root shape
387 * @return the ID of this shape or -1 if this shape is not a child of
390 public int getID(T rootShape)
392 int[] ids = new int[]{0};
393 if (getID(rootShape, new HashSet<T>(), ids, impl.shapeType()))
401 T r = getRootShape();
404 throw new NoSuchElementException("Cannot find " + impl + " in " + r);
408 private boolean getID(T rootShape, Set<T> shapeSet, int[] number,
409 TopAbs_ShapeEnum wantedType)
411 if (shapeSet.contains(rootShape))
414 shapeSet.add(rootShape);
415 //check if the root shape have the right type
416 int compare = rootShape.impl.shapeType().compareTo(wantedType);
420 if(this.equals(rootShape))
422 //A compound can include another compound
423 else if(rootShape.impl instanceof TopoDS_Compound)
425 //So we don't give up but iterate on children
426 for (T s : rootShape.children)
427 //only on TopoDS_Compound children
428 if (s.impl instanceof TopoDS_Compound &&
429 getID(s, shapeSet, number, wantedType))
433 else if (compare < 0)
435 //look for this shape in children
436 for (T s : rootShape.children)
437 if (getID(s, shapeSet, number, wantedType))
444 * @param result will contains the found shapes. Use a HashSet to get unique
445 * shapes (getFromID) and ArrayList to get doublon (explore)
446 * @param wantedType the type of shape to return
447 * @param maxsize the maximum number of returned shapes
448 * @param shape exploration will end when this shape will be found
450 private T explore(Collection<T> result, TopAbs_ShapeEnum wantedType, int maxsize,
453 if(impl.shapeType().equals(wantedType))
455 result.add(getDerived());
456 if(result.size()>=maxsize)
458 if(impl.equals(shape))
464 T toReturn = s.explore(result, wantedType, maxsize, shape);
473 * Return the children of this shape whose type is type
475 public Collection<T> explore(TopAbs_ShapeEnum type)
477 ArrayList<T> toReturn = new ArrayList<T>();
478 explore(toReturn, type, Integer.MAX_VALUE, null);
482 public T getRootShape()
484 T aparent = getDerived();
485 while(aparent.parents.length!=0)
486 aparent = aparent.parents[0];
491 * @param id from 1 to n
492 * @param type shape type
493 * @return the shape of the given type with this id
495 public T getShapeFromID(int id, TopAbs_ShapeEnum type)
498 throw new IllegalArgumentException("Shape ID must be greater than 1");
500 Collection<T> result = new HashSet<T>();
501 T toReturn = explore(result, type, id, null);
504 throw new ArrayIndexOutOfBoundsException("This shape contains only "
505 +result.size()+" elements of type "+type);
510 public T getShapeFromImpl(TopoDS_Shape shape)
512 Collection<T> dummy = new AbstractList<T>()
515 public void add(int index, T element) {}
518 public T get(int index)
520 throw new UnsupportedOperationException();
529 return explore(dummy, shape.shapeType(), Integer.MAX_VALUE, shape);
533 * Return the closest parent shape which is a Compound
534 * @return the closest parent shape which is a Compound
536 public T getCompound()
538 if(impl instanceof TopoDS_Compound)
540 else if(parents.length>0)
541 return parents[0].getCompound();
546 public double getTolerance()
548 return Utilities.tolerance(impl);
551 public TopAbs_ShapeEnum getType()
553 return impl.shapeType();
556 /**return {xmin, ymin, zmin, xmax, ymax, zmax} */
557 public double[] getBounds()
559 Bnd_Box box = new Bnd_Box();
560 BRepBndLib.add(impl,box);
565 * return the attributes of this nodes.
566 * Can be null if it as no attributes (default values)
568 protected Attributes getAttributes()
574 * Initialise the attributes. Ones called getAttributes will not return
577 protected void createAttributes()
581 public String getName()
583 return TYPE_MAP_NAME.get(impl.shapeType());
586 public void saveImpl(String fileName)
588 BRepTools.write(impl, fileName);
591 public int compareTo(T o)
593 int r = getType().compareTo(o.getType());
595 r = getID() - o.getID();
601 private static void dumpTopExp(TopoDS_Shape shape)
603 for (TopAbs_ShapeEnum type : TopAbs_ShapeEnum.values())
605 if (type.equals(TopAbs_ShapeEnum.SHAPE))
607 TopExp_Explorer ex = new TopExp_Explorer(shape, type);
610 System.out.println(ex.current());
617 public static void main(final String[] args)
621 long t1 = System.nanoTime();
622 TopoDS_Shape rootShape = Utilities.readFile("/home/jerome/Models/F1.brep");
623 long t2 = System.nanoTime();
624 LOGGER.info("Time to load brep: " + (t2 - t1) / 1E9);
626 LOGGER.info("Used memory :" +
627 (Runtime.getRuntime().totalMemory() -
628 Runtime.getRuntime().freeMemory()) / 1E6 + " Mb");
629 t1 = System.nanoTime();
630 ShapeImpl rootShapeJ = new ShapeImpl(rootShape, new HashMap<TopoDS_Shape, ShapeImpl>(), new ShapeImpl[0]);
631 t2 = System.nanoTime();
632 LOGGER.info("Time to create dual graph: " + (t2 - t1) / 1E9);
634 LOGGER.info("Used memory :" +
635 (Runtime.getRuntime().totalMemory() -
636 Runtime.getRuntime().freeMemory()) / 1E6 + " Mb");
637 LOGGER.info(rootShapeJ.toString());
638 t1 = System.nanoTime();
639 ShapeImpl s = rootShapeJ.getShapeFromID(330, TopAbs_ShapeEnum.EDGE);
640 t2 = System.nanoTime();
641 ShapeImpl ss = rootShapeJ.getShapeFromImpl(s.impl);
642 long t3 = System.nanoTime();
644 long t4 = System.nanoTime();
645 System.out.println("time for getShapeFromID: "+(t2-t1)/1E9);
646 System.out.println("time for getShapeFromImpl: "+(t3-t2)/1E9);
647 System.out.println("time for getID: "+(t4-t3)/1E9);
651 LOGGER.log(Level.SEVERE, null, ex);
655 private static final Factory<ShapeImpl> DEFAULT_FACTORY=new Factory<ShapeImpl>()
657 public ShapeImpl create(TopoDS_Shape shape, Map<TopoDS_Shape, ShapeImpl> map, ShapeImpl[] parents)
659 return new ShapeImpl(shape, map, parents);
662 public ShapeImpl[] createArray(int length)
664 return new ShapeImpl[length];
668 private static class ShapeImpl extends Shape<ShapeImpl>
670 public ShapeImpl(TopoDS_Shape shape, Map<TopoDS_Shape, ShapeImpl> map, ShapeImpl[] parents)
672 super(shape, map, parents);
675 protected Factory<ShapeImpl> getFactory()
677 return DEFAULT_FACTORY;
680 protected ShapeImpl getDerived()