Package bs.bs2d.fea

Source Code of bs.bs2d.fea.TriMesh2D

package bs.bs2d.fea;

import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import javax.vecmath.Point2f;

/**
* A mesh consisting of 2D triangular (three node) elements.
* @author Djen
*/
public class TriMesh2D {

    private final ArrayList<Node2D> nodes;
    private final ArrayList<TriElement> elements;
    private ArrayList<Node2D> outerNodes;
    private ArrayList<Edge> edges, outerEdges;
    private Rectangle2D bounds;
   
    private boolean finalized;
   
    public TriMesh2D() {
        nodes = new ArrayList<>(300);
        elements = new ArrayList<>(1000);
       
        finalized = false;
    }

    /**
     * Adds a node to this mesh. Checks to prevent duplicates.
     * @param node the node to be added
     */
    public void addNode(Node2D node){
        if(finalized){
            throw new UnsupportedOperationException("Mesh has already been finalized.");
        }
       
        if(!nodes.contains(node))
            nodes.add(node);
    }
   
    /**
     * Constructs a new TriElement from the nodes specified by the given indices
     * and adds it to the mesh.
     * @param index the global index of the new element
     * @param nodeIndex an array containing the indices of three node that have
     * previously been added to this mesh
     */
    public void addNewElement(int index, int[] nodeIndex){
        if(finalized){
            throw new UnsupportedOperationException("Mesh has already been finalized.");
        }
       
        // check for existing element with same index
        if(elements.contains(new TriElement(index)))
                return;
       
        Node2D[] elNode = new Node2D[3];
        for(int i=0; i<3; i++){
            int listIndex = nodes.indexOf(new Node2D(nodeIndex[i]));
           
            if(listIndex != -1){
                elNode[i] = nodes.get(listIndex);
            } else {
                throw new IllegalArgumentException("Node not contained in mesh: " + nodeIndex[i]);
            }
        }
        elements.add(new TriElement(index, elNode));
    }
   
    /**
     * @param index node index
     * @return the node with the given index
     */
    public Node2D getNode(int index){
        return nodes.get(index);
    }
   
    /**
     * Finds a node that matches the given coordinates by the given threshold.
     * A rectangular match ist used (both coordinates checked independently).
     * @param x the x coordinate
     * @param y the y coordinate
     * @param thrsh the threshold
     * @return the first node matching the given coorsinates
     */
    public Node2D getNode(float x, float y, float thrsh){
        for(Node2D node : nodes){
            if(Math.abs(node.getX() - x) < thrsh &&
                    Math.abs(node.getY() - y) < thrsh){
                return node;
            }
        }
        return null;
    }
   
    /**
     * @return the number of nodes in this mesh
     */
    public int getNodeCount(){
        return nodes.size();
    }
   
    /**
     * @return a list of all nodes in this mesh
     */
    public List<Node2D> getNodes() {
        return nodes;
    }

    /**
     * @param index element index
     * @return the element with the given index
     */
    public TriElement getElement(int index){
        return elements.get(index);
    }
   
    public TriElement getElement(Point2D p){
        for (TriElement el : elements) {
            GeneralPath path = new GeneralPath();
            Point2f n0 = el.getNode(0).getPoint2f();
            Point2f n1 = el.getNode(1).getPoint2f();
            Point2f n2 = el.getNode(2).getPoint2f();
           
            path.moveTo(n0.x, n0.y);
            path.lineTo(n1.x, n1.y);
            path.lineTo(n2.x, n2.y);
            path.closePath();
           
            if(path.contains(p))
                return el;
        }
        return null;
    }
   
    /**
     * @return the number of elements in this list
     */
    public int getElementCount(){
        return elements.size();
    }
   
    /**
     * @return a list of all elements in this mesh
     */
    public List<TriElement> getElements() {
        return elements;
    }
   
    /**
     * @param index edge index
     * @return the edge with the given index
     */
    public Edge getEdge(int index){
        return edges.get(index);
    }
   
    /**
     * @return the number of edges in this mesh
     */
    public int getEdgeCount(){
        return edges.size();
    }
   
    /**
     * @return a list of all edges in this mesh
     */
    public List<Edge> getEdges() {
        return edges;
    }

    /**
     * Finalizes the construction of this mesh. This method is to be called
     * after all nodes and elements have been added. It will do the following:
     *  - trim the node and element list to their current size to save memory
     *  - assign each node the elements it belongs to
     *  - ensure counterclockwise order of internal nodes. This will force outer
     * edges to be oriented in counterclockwise direction along the perimeter.
     *  - generate edges
     *  - assign edges to elements, edges to nodes and elements to nodes
     *  - renumber all entities to ensure list indices concur with global indices
     *  - mark outer nodes and edges
     */
    public void finalizeConstruction(){
        if(finalized){
            System.err.println("Mesh has already been finalized.");
            return;
        }
       
        nodes.trimToSize();
        elements.trimToSize();
       
        assignElementsToNodes();
        reorientElements();
        generateEdges();
        renumberAll();
        computeOuterNodesAndEdges();
        computeBounds();
       
        finalized = true;
    }

    /**
     * Assigns to each node all elements in wich it is contained.
     */
    private void assignElementsToNodes(){
        for(TriElement el : elements){
            for(Node2D node : el.getNodes()){
                node.addElement(el);
            }
        }
       
        int count = 0;
        for (int i = 0; i < nodes.size(); i++) {
            if(nodes.get(i).getElementCount() == 0){
                nodes.remove(i);
                i--;
                count++;
            }
        }
        if(count > 0)
            System.out.println(count + " unused nodes removed");
    }
   
    /**
     * Generates a list of global edges and assigns each element its edges. This
     * method will also assign edges to each node and each element to its edges.
     */
    private void generateEdges(){
        edges = new ArrayList<>(2 * elements.size());
       
        for(TriElement el : elements){
           
            int prev = 2;
            for(int i=0; i<3; i++){
                //constuct new edge
                Node2D n1 = el.getNode(prev);
                Node2D n2 = el.getNode(i);
                Edge edge = new Edge(n1, n2);
               
                // check for duplicate (if edge exists use existing edge)
                int index = edges.indexOf(edge);
                if(index == -1)
                    edges.add(edge);
                else
                    edge = edges.get(index);
               
                // assign edge to element and nodes
                el.setEdge(i, edge);
                n1.addEdge(edge);
                n2.addEdge(edge);
                // assign element to edge
                edge.addElement(el);
               
                prev = i;
            }
        }
        edges.trimToSize();
    }
   
    /**
     * renumbers all mesh entities according to their list index to ensure
     * equality between the list index and globalIndex property.
     */
    private void renumberAll(){
        //renumber nodes
        for(int i=0; i<nodes.size(); i++)
            nodes.get(i).setGlobalIndex(i);
       
        //renumber elements
        for(int i=0; i<elements.size(); i++)
            elements.get(i).setGlobalIndex(i);
       
        //renumber edges
        for(int i=0; i<edges.size(); i++)
            edges.get(i).setGlobalIndex(i);
    }
   
    /**
     * Calculates the external rectangular bounds for this mesh.
     */
    private void computeBounds(){
        Point2D p = nodes.get(0).getPoint();
        bounds = new Rectangle2D.Double(p.getX(), p.getY(), 0, 0);
        for (Node2D n : nodes) {
            bounds.add(n.getPoint());
        }
    }
   
    /**
     * determines which nodes and edges are on the mesh boundary and marks them
     * accordingly.
     */
    private void computeOuterNodesAndEdges(){
       
        HashSet<Edge> outerEdgeSet = new HashSet<>(getEdgeCount()/5);
       
        // compute outer edges
        for (Edge edge : edges) {
            if(edge.getElementCount() == 1){
                edge.setOuter(true);
                outerEdgeSet.add(edge);
            } else {
                edge.setOuter(false);
            }
        }
       
        outerEdges = new ArrayList<>(outerEdgeSet.size() + 5);
        outerNodes = new ArrayList<>(outerEdgeSet.size() + 5);
       
        // sort edges, fill outer edge and node list and mark outer nodes
        Edge current, next, first;
        next = first = null;
       
        while(!outerEdgeSet.isEmpty()){
            if(next == first){ // end of loop --> start new loop
                current = outerEdgeSet.iterator().next();// get any element
                first = current;
            } else {
                current = next;
            }
           
            if(current == null)
                throw new NullPointerException("null edge in outer edge set");
           
            // update lists and mark node
            outerEdges.add(current);
            outerNodes.add(current.getNode(0));
            current.getNode(0).setOuter(true);
            outerEdgeSet.remove(current);
       
            //find next
            next = null;
            for (Edge edge : current.getNode(1).getEdges()) {
                if(edge == current)
                    continue;
                if(edge.isOuter()){
                    next = edge;
                    break;
                }
            }
            if(next == null)
                throw new RuntimeException("Error computing mesh outlines: open loop!");
           
            if(next == first){ // loop closed
                // add 'null' to mark loop end
                outerEdges.add(null);
                outerNodes.add(null);
            }
       
        }
    }
   
    private void reorientElements(){
        for(TriElement el : elements){
           
            Point2f v0 = el.getNode(0).getPoint2f();
            Point2f v1 = el.getNode(1).getPoint2f();
            Point2f v2 = el.getNode(2).getPoint2f();
           
            v1.sub(v0); // from node 0 to 1
            v2.sub(v0); // from node 0 to 2
           
            float cross = v1.x*v2.y - v1.y*v2.x;
           
            if(cross < 0){
                el.reorient();
            } else if(cross == 0){
                System.err.println("Distorted element: " + el.globalIndex);
            }
        }
    }
   
    public TriMesh2D copy(){
        TriMesh2D mesh = new TriMesh2D();
       
        // copy nodes
        for(Node2D n : nodes)
            mesh.addNode(n.copy());
       
        // add new element for each existing element
        for(TriElement el : elements){
            int[] nodeIdx = new int[3];
            for(int i=0; i<3; i++)
                nodeIdx[i] = el.getNode(i).getIndex();
            mesh.addNewElement(el.getIndex(), nodeIdx);
        }
       
        mesh.finalizeConstruction();
       
        return mesh;
    }

    /**
     * @return a list of the outer nodes of this mesh sorted in
     * counter-clockwise sequence. Loops are separated by null values in the
     * list.
     */
    public List<Node2D> getOuterNodes() {
        return outerNodes;
    }

    /**
     * @return a list of the outer edges of this mesh sorted in
     * counter-clockwise sequence. Loops are separated by null values in the
     * list.
     */
    public List<Edge> getOuterEdges() {
        return outerEdges;
    }
   
    public GeneralPath createOutlinePath() {
        GeneralPath p = new GeneralPath();
        p.moveTo(0f, 0f);
       
        Point2f v;
        boolean first = true;
        for (Node2D node : getOuterNodes()) {
            if(node == null){
                p.closePath();
                first = true;
            } else if(first){
                v = node.getPoint2f();
                p.moveTo(v.x, v.y);
                first = false;
            } else {
                v = node.getPoint2f();
                p.lineTo(v.x, v.y);
            }
        }
        return p;
    }
   
    public GeneralPath createWireframePath(){
        GeneralPath wireframe = new GeneralPath();
        Point2f start, end;
       
        for (Edge e : getEdges()) {
           
            // skip border edges
            if (e.isOuter()) {
                continue;
            }

            // inner edge --> add to wireframe
            start = e.getNode(0).getPoint2f();
            end = e.getNode(1).getPoint2f();

            wireframe.moveTo(start.x, start.y);
            wireframe.lineTo(end.x, end.y);
        }
       
        return wireframe;
    }

    @Override
    public String toString() {
        return String.format("Triangular 2D-mesh [%d nodes; %d elements]", getNodeCount(), getElementCount());
    }

    /**
     * @return the bounds
     */
    public Rectangle2D getBounds() {
        return bounds;
    }
   
   
}
TOP

Related Classes of bs.bs2d.fea.TriMesh2D

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.