/* jCAE stand for Java Computer Aided Engineering. Features are : Small CAD modeler, Finite element mesher, Plugin architecture. Copyright (C) 2003,2004,2005, by EADS CRC Copyright (C) 2007,2008,2009, by EADS France This library 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 library 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jcae.mesh.amibe.ds; import org.jcae.mesh.bora.ds.*; import org.jcae.mesh.cad.*; import org.jcae.mesh.amibe.patch.Vertex2D; import java.util.Iterator; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.NoSuchElementException; import java.io.File; import java.util.Collection; import java.util.Collections; import java.util.logging.Logger; /** * 1D discretization of the whole shape. * In order to ensure fitting in 3D space, edges are discretized only once * even if they appear with several orientations. */ public class MMesh1D extends MMesh0D { private final static Logger LOGGER = Logger.getLogger(MMesh1D.class.getName()); private String filename; // Edge map. private Map<CADEdge, SubMesh1D> mapTEdgeToSubMesh1D; private Map<CADEdge, LinkedHashSet<CADFace>> mapTEdgeToFaces; // Ditto for bora data structure. private Map<BDiscretization, SubMesh1D> mapDiscrToSubMesh1D; /** * Creates a <code>MMesh1D</code> instance by discretizing all edges * of a given shape. * * @param cadFile file containing CAD shape */ public MMesh1D(String cadFile) { this(CADShapeFactory.getFactory().newShape(cadFile)); filename = new File(cadFile).getName(); } /** * Creates a <code>MMesh1D</code> instance by discretizing all edges * of a given shape. * * @param shape CAD shape */ private MMesh1D(CADShape shape) { super(shape); CADExplorer expE = CADShapeFactory.getFactory().newExplorer(); // HashMap size will not be greater than the number of edges, // so allocate them after computing their maximal size, they // won't be resized. int edges = 0; for (expE.init(shape, CADShapeEnum.EDGE); expE.more(); expE.next()) edges++; if (edges == 0) return; mapTEdgeToSubMesh1D = new LinkedHashMap<CADEdge, SubMesh1D>(edges); for (expE.init(shape, CADShapeEnum.EDGE); expE.more(); expE.next()) { CADEdge E = (CADEdge) expE.current(); // Edges may get connected to several faces if (mapTEdgeToSubMesh1D.containsKey(E)) continue; SubMesh1D submesh1d = new SubMesh1D(E, this); mapTEdgeToSubMesh1D.put(E, submesh1d); } mapTEdgeToFaces = new HashMap<CADEdge, LinkedHashSet<CADFace>>(edges); for (Iterator<CADEdge> it = mapTEdgeToSubMesh1D.keySet().iterator(); it.hasNext(); ) mapTEdgeToFaces.put(it.next(), new LinkedHashSet<CADFace>()); CADExplorer expF = CADShapeFactory.getFactory().newExplorer(); for (expF.init(shape, CADShapeEnum.FACE); expF.more(); expF.next()) { CADFace F = (CADFace) expF.current(); LinkedHashSet<CADFace> set; for (expE.init(F, CADShapeEnum.EDGE); expE.more(); expE.next()) { CADEdge E = (CADEdge) expE.current(); set = mapTEdgeToFaces.get(E); set.add(F); } } assert(isValid()); } public MMesh1D(BModel model) { super(model); BCADGraphCell root = model.getGraph().getRootCell(); int edgediscrs = 0; // estimation of the maximum number of nodes created on the vertices of the CAD for (Iterator<BCADGraphCell> itn = root.shapesExplorer(CADShapeEnum.EDGE); itn.hasNext(); ) { BCADGraphCell cell = itn.next(); edgediscrs += cell.getDiscretizations().size(); } if (edgediscrs == 0) return; mapDiscrToSubMesh1D = new LinkedHashMap<BDiscretization, SubMesh1D>(edgediscrs); edgediscrs = 0; for (Iterator<BCADGraphCell> itn = root.shapesExplorer(CADShapeEnum.EDGE); itn.hasNext(); ) { BCADGraphCell cell = itn.next(); for (BDiscretization discr : cell.getDiscretizations()) { // Edges may get connected to several faces if (mapDiscrToSubMesh1D.containsKey(discr)) continue; SubMesh1D submesh1d = new SubMesh1D(discr, this); mapDiscrToSubMesh1D.put(discr, submesh1d); edgediscrs++; } } System.out.println("Number of Edge discretizations created in MMesh1D: "+ edgediscrs); Map<BDiscretization, LinkedHashSet<BDiscretization>> mapDiscrToFaces = new HashMap<BDiscretization, LinkedHashSet<BDiscretization>>(edgediscrs); for (BDiscretization d : mapDiscrToSubMesh1D.keySet()) { mapDiscrToFaces.put(d, new LinkedHashSet<BDiscretization>()); } for (Iterator<BCADGraphCell> itp = root.shapesExplorer(CADShapeEnum.FACE); itp.hasNext(); ) { BCADGraphCell pcell = itp.next(); for (BDiscretization pd : pcell.getDiscretizations()) { for (Iterator<BCADGraphCell> itc = pcell.shapesExplorer(CADShapeEnum.EDGE); itc.hasNext(); ) { BCADGraphCell ccell = itc.next(); for (BDiscretization cd : ccell.getDiscretizations()) { if (pd.contained(cd)) { // here mapDiscrToFaces maps the parent discretizations on // the faces to the child discretizations on the edge LinkedHashSet<BDiscretization> set = mapDiscrToFaces.get(cd); set.add(pd); } } } } } assert(isValid()); } /** * Returns the topological shape. * * @return the topological shape. */ public final CADShape getGeometry() { return shape; } /** * Returns the file name containing the topological shape. * Return null is this MMesh1D has been created from a CADShape and not from * a file * @return the file name containing the topological shape. */ public final String getGeometryFilename() { return filename; } /** * Returns the set of faces containing this topological edge. * * @return the set of faces containing this topological edge. */ public final Set<CADFace> getAdjacentFaces(CADEdge E) { Set<CADFace> ret = mapTEdgeToFaces.get(E); // May be null for beams return ret; } /** * Update node labels. */ public final void updateNodeLabels() { LOGGER.fine("Update node labels"); // Resets all labels CADExplorer expE = CADShapeFactory.getFactory().newExplorer(); for (expE.init(shape, CADShapeEnum.EDGE); expE.more(); expE.next()) { CADEdge E = (CADEdge) expE.current(); SubMesh1D submesh1d = mapTEdgeToSubMesh1D.get(E); for (Iterator<MNode1D> it = submesh1d.getNodesIterator(); it.hasNext(); ) it.next().setLabel(0); } int i = 0; for (expE.init(shape, CADShapeEnum.EDGE); expE.more(); expE.next()) { CADEdge E = (CADEdge) expE.current(); SubMesh1D submesh1d = mapTEdgeToSubMesh1D.get(E); for (Iterator<MNode1D> it = submesh1d.getNodesIterator(); it.hasNext(); ) { MNode1D n = it.next(); if (0 == n.getMaster().getLabel()) { i++; n.getMaster().setLabel(i); } } } } /** * Duplicates edges so that boundary faces are closed. * This method must be used after all 1D algorithms have been applied, * and before any 2D meshing is performed. * */ public final void duplicateEdges() { LOGGER.fine("Compute vertex references"); CADExplorer expV = CADShapeFactory.getFactory().newExplorer(); // For each topological vertex, compute the list of // MNode1D objects which are bound to this vertex. int nVertex = 0; for (expV.init(shape, CADShapeEnum.VERTEX); expV.more(); expV.next()) nVertex++; HashMap<CADShape, ArrayList<MNode1D>> vertex2Ref = new HashMap<CADShape, ArrayList<MNode1D>>(nVertex); for (expV.init(shape, CADShapeEnum.VERTEX); expV.more(); expV.next()) vertex2Ref.put(expV.current(), new ArrayList<MNode1D>()); Iterator<SubMesh1D> ite = mapTEdgeToSubMesh1D.values().iterator(); while (ite.hasNext()) { SubMesh1D submesh1d = ite.next(); Iterator<MNode1D> itn = submesh1d.getNodesIterator(); while (itn.hasNext()) { MNode1D pt = itn.next(); if (null != pt.getCADVertex()) vertex2Ref.get(pt.getCADVertex()).add(pt); } } LinkedHashSet<CADVertex> seen = new LinkedHashSet<CADVertex>(); for (expV.init(shape, CADShapeEnum.VERTEX); expV.more(); expV.next()) { CADVertex V = (CADVertex) expV.current(); if (seen.contains(V)) continue; seen.add(V); ArrayList<MNode1D> vnodelist = vertex2Ref.get(V); if (vnodelist.size() <= 1) continue; // Make sure that all MNode1D objects share the same master. MNode1D master = vnodelist.get(0); for (MNode1D pt: vnodelist) pt.setMaster(master); master.setMaster(null); } assert(isValid()); } /** * Returns the list of topological edges. * * @return the list of topological edges. */ public Iterator<CADEdge> getTEdgeIterator() { return mapTEdgeToSubMesh1D.keySet().iterator(); } /** * Returns an unmodifiable view of the list of topological edges. * * @return the list of topological edges. */ public final Collection<CADEdge> getTEdges() { return Collections.unmodifiableCollection(mapTEdgeToSubMesh1D.keySet()); } public Iterator<BDiscretization> getBEdgeIterator() { return mapDiscrToSubMesh1D.keySet().iterator(); } /** * Returns an unmodifiable view of the list of topological edges. * * @return the list of topological edges. */ public final Collection<BDiscretization> getBEdges() { return Collections.unmodifiableCollection(mapDiscrToSubMesh1D.keySet()); } /** * Returns the list of nodes inserted on a given topological edge. * * @param E a topological edge. * @return the list of nodes inserted on this edge. */ private ArrayList<MNode1D> getNodelistFromMap(CADEdge E) throws NoSuchElementException { SubMesh1D submesh1d = mapTEdgeToSubMesh1D.get(E); if (null == submesh1d) throw new NoSuchElementException("TEdge : "+E); return submesh1d.getNodes(); } /** * Returns the list of edges inserted on a given topological edge. * * @param E a topological edge. * @return the list of edges inserted on this edge. */ @SuppressWarnings("unused") private ArrayList<MEdge1D> getEdgelistFromMap(CADEdge E) throws NoSuchElementException { SubMesh1D submesh1d = mapTEdgeToSubMesh1D.get(E); if (null == submesh1d) throw new NoSuchElementException("TEdge : "+E); return submesh1d.getEdges(); } /** * Returns an upper limit of the total number of nodes * * @return an upper limit of the total number of nodes */ private int maximalNumberOfNodes() { int result = 0; for(Iterator<SubMesh1D> it=mapTEdgeToSubMesh1D.values().iterator();it.hasNext();) { SubMesh1D submesh1d = it.next(); if (null != submesh1d) result += submesh1d.getNodes().size(); } return result; } /** * Returns the <code>SubMesh1D</code> instance associated with a * CADEdge. * * @param E a topological edge. * @return the <code>SubMesh1D</code> instance associated with it. */ public final SubMesh1D getSubMesh1DFromMap(CADEdge E) throws NoSuchElementException { if (! mapTEdgeToSubMesh1D.containsKey(E)) throw new NoSuchElementException("TEdge : "+E); return mapTEdgeToSubMesh1D.get(E); } /** * Returns the <code>SubMesh1D</code> instance associated with a * BDiscretization. * * @param discrE discretization of an edge. * @return the <code>SubMesh1D</code> instance associated with it. */ public final SubMesh1D getSubMesh1DFromMap(BDiscretization discrE) throws NoSuchElementException { if (! mapTEdgeToSubMesh1D.containsKey(discrE)) throw new NoSuchElementException("TEdge : "+discrE); return mapTEdgeToSubMesh1D.get(discrE); } public final Vertex2D [] boundaryNodes(CADFace face, MeshParameters mp) { double epsilon = mp.getEpsilon(); boolean accumulateEpsilon = mp.hasCumulativeEpsilon(); double deflection = mp.getDeflection(); boolean hasRelativeDeflection = mp.hasRelativeDeflection(); boolean hasDeflection = mp.hasDeflection(); // Rough approximation of the final size int roughSize = 10*maximalNumberOfNodes(); ArrayList<Vertex2D> result = new ArrayList<Vertex2D>(roughSize); CADExplorer expW = CADShapeFactory.getFactory().newExplorer(); CADWireExplorer wexp = CADShapeFactory.getFactory().newWireExplorer(); for (expW.init(face, CADShapeEnum.WIRE); expW.more(); expW.next()) { MNode1D p1 = null; Vertex2D p20 = null, p2 = null, lastPoint = null; double accumulatedLength = 0.0; ArrayList<Vertex2D> nodesWire = new ArrayList<Vertex2D>(roughSize); for (wexp.init((CADWire) expW.current(), face); wexp.more(); wexp.next()) { CADEdge te = wexp.current(); CADGeomCurve2D c2d = CADShapeFactory.getFactory().newCurve2D(te, face); CADGeomCurve3D c3d = CADShapeFactory.getFactory().newCurve3D(te); ArrayList<MNode1D> nodelist = getNodelistFromMap(te); Iterator<MNode1D> itn = nodelist.iterator(); ArrayList<MNode1D> saveList = new ArrayList<MNode1D>(); while (itn.hasNext()) saveList.add(itn.next()); if (!te.isOrientationForward()) { // Sort in reverse order int size = saveList.size(); for (int i = 0; i < size/2; i++) { MNode1D o = saveList.get(i); saveList.set(i, saveList.get(size - i - 1)); saveList.set(size - i - 1, o); } } itn = saveList.iterator(); // Except for the very first edge, the first // vertex is constrained to be the last one // of the previous edge. p1 = itn.next(); if (null == p2) { p2 = Vertex2D.valueOf(p1, c2d, face); nodesWire.add(p2); p20 = p2; lastPoint = p2; } ArrayList<Vertex2D> newNodes = new ArrayList<Vertex2D>(saveList.size()); while (itn.hasNext()) { p1 = itn.next(); p2 = Vertex2D.valueOf(p1, c2d, face); newNodes.add(p2); } // An edge is skipped if all the following conditions // are met: // 1. It is not degenerated // 2. It has not been discretized in 1D // 3. Edge length is smaller than epsilon // 4. Accumulated points form a curve with a deflection // which meets its criterion boolean canSkip = false; if (nodelist.size() == 2 && !te.isDegenerated()) { // 3. Edge length is smaller than epsilon double edgelen = c3d.length(); if (accumulateEpsilon) canSkip = edgelen + accumulatedLength < epsilon; else canSkip = edgelen < epsilon; if (canSkip) accumulatedLength += edgelen; // 4. Check whether deflection is valid. if (canSkip && hasDeflection) { assert lastPoint != null; double [] start = face.getGeomSurface().value(lastPoint.getX(), lastPoint.getY()); double [] end = face.getGeomSurface().value(p2.getX(), p2.getY()); double dist = Math.sqrt( (start[0] - end[0]) * (start[0] - end[0]) + (start[1] - end[1]) * (start[1] - end[1]) + (start[2] - end[2]) * (start[2] - end[2])); double dmax = deflection; if (hasRelativeDeflection) dmax *= accumulatedLength; if (accumulatedLength - dist > dmax) canSkip = false; } } if (!canSkip) { nodesWire.addAll(newNodes); accumulatedLength = 0.0; lastPoint = p2; } } // If a wire has less than 3 points, it is discarded if (nodesWire.size() > 3) { // Overwrite the last value to close the wire nodesWire.set(nodesWire.size()-1, p20); result.addAll(nodesWire); } } return result.toArray(new Vertex2D[result.size()]); } /** * Checks the validity of a <code>MMesh1D</code> instance. * This method is called within assertions, this is why it returns a * <code>boolean</code>. * * @return <code>true</code> if all checks pass. * @throws AssertionError if a check fails. */ public final boolean isValid() { for(Iterator<SubMesh1D> it=mapTEdgeToSubMesh1D.values().iterator();it.hasNext();) { SubMesh1D submesh1d = it.next(); if (null != submesh1d) assert(submesh1d.isValid()); } return true; } /** * Prints edge lengths of a <code>MMesh1D</code> instance. */ @SuppressWarnings("unused") private void printInfos() { for(Iterator<SubMesh1D> it=mapTEdgeToSubMesh1D.values().iterator();it.hasNext();) { SubMesh1D submesh1d = it.next(); if (null != submesh1d) submesh1d.printInfos(); } } @Override public final String toString() { String cr=System.getProperty("line.separator"); StringBuilder r = new StringBuilder("MMesh1D"+cr); LOGGER.fine("Printing "+r.toString()); for(Iterator<SubMesh1D> it=mapTEdgeToSubMesh1D.values().iterator();it.hasNext();) { SubMesh1D submesh1d = it.next(); if (null != submesh1d) r.append(submesh1d); } LOGGER.fine("...done"); return r.toString(); } }