Package bs.bs2d.gui.plot

Source Code of bs.bs2d.gui.plot.ScalarPlotUtils

package bs.bs2d.gui.plot;

import bs.bs2d.fea.Edge;
import bs.bs2d.fea.Node2D;
import bs.bs2d.fea.TriElement;
import bs.bs2d.fea.TriMesh2D;
import bs.bs2d.geom.JTSUtils;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.MultiPolygon;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.geom.GeneralPath;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.vecmath.Point2f;

/**
* Provides utility methods for creating stress plots. The methods providied by
* this class are used by the StressPlot class internally. To create a stress
* plot use the static factory methods of the StressPlot class.
* @author Djen
*/
public abstract class ScalarPlotUtils {
   
    /**
     * Generates thresholds for a linear scale scalar plot. Used by ScalarPlot
     * internally. To create scalar plots use the static factory methods of the
     * ScalarPlot class.
     * @param scalar the scalar
     * @param res the plot resolution
     * @return linearly scaled thrsholds for the given scalar field
     */
    public static float[] getLinScaleThresholds(float[] scalar, int res){
        // determine min and max
        float min, max;
        min = max = scalar[0];
        for(float d : scalar){
            if(d < min)
                min = d;
            if(d > max)
                max = d;
        }
       
        // split range in equal sections
        float[] thrsh = new float[res+1];
        float step = (max-min)/(res);
       
        for (int i = 0; i < res; i++) {
            thrsh[i] = min + i*step;
        }
        thrsh[res] = max; // avoid rounding error
       
        return thrsh;
    }
   
    /**
     * Generates thresholds for a uniform area scalar plot. Used by ScalarPlot
     * internally. To create scalar plots use the static factory methods of the
     * ScalarPlot class.
     * @param scalar the scalar
     * @param res the plot resolution
     * @return uniform area thrsholds for the given scalar field
     */
    public static float[] getUniAreaThresholds(float[] scalar, int res){
        // get sorted stress values
        float[] sstress = Arrays.copyOf(scalar, scalar.length);
        Arrays.sort(sstress);

        // divide into groups of equal size
        float[] thrsh = new float[res+1];
        float step = (float)sstress.length/res;
        for(int i=0; i < res; i++){
            thrsh[i] = sstress[(int)(i*step)];
        }
        thrsh[res] = sstress[sstress.length-1];

        return thrsh;
    }
   
   
    /**
     * Computes the relative position along the given edge where the scalar
     * value linearly interpolated between the two nodes crosses the threshold
     * @param e the edge
     * @param scalar the list of scalars to retrieve the significant values in
     * the two nodes (identified by global index)
     * @param thrsh the threshold
     * @return the relative position of the threshold value on the edge
     */
    private static float getRelPosOnEdge(Edge e, float[] scalar, float thrsh){
        float sstart = scalar[e.getNode(0).getIndex()];
        float send = scalar[e.getNode(1).getIndex()];
        if((sstart >= thrsh && send >= thrsh) || (sstart < thrsh && send < thrsh))
            System.err.println("invalid edge");
        if(sstart == send)
            return 0;
       
        return Math.abs((thrsh-sstart)/(send-sstart));
    }
   
    /**
     * Computes the coordinates of the point at 'pos' between the start and
     * end point of the given edge.
     * @param edge the Edge
     * @param pos relative position on the edge (0 <= pos <= 1)
     * @return the Point at relative position 'pos' on the given edge
     */
    private static Point2f getPointOnEdge(Edge edge, float pos){
        // start point vector
        Point2f s = edge.getNode(0).getPoint2f();
        // end point vector
        Point2f e = edge.getNode(1).getPoint2f();
       
        s.interpolate(e, pos);
        return s;
    }

    /**
     * Computes the relative point along the given edge where the scalar
     * value linearly interpolated between the two nodes crosses the threshold
     * @param e the edge
     * @param scalar the list of scalars to retrieve the significant values in
     * the two nodes (identified by global index)
     * @param thrsh the threshold
     * @return the relative point of the threshold on the edge
     */
    private static Point2f getRelPointOnEdge(Edge e, float[] scalar, float thrsh){
        return getPointOnEdge(e, getRelPosOnEdge(e, scalar, thrsh));
    }
   
   
    /**
     * Copmutes the area(s) of the given mesh in wich the specified scalar is
     * above the given threshold.
     * @param m the Mesh
     * @param scalar a collection of the scalar values corresponding to the
     * nodes of the given mesh (with 1-based node indices).
     * @param thrsh the threshold for this area plot
     * @return a GeneralPath describing all the areas of the given mesh where
     * the given scalar value is above the threshold.
     */
    private static Shape createScalarPlotArea2(TriMesh2D m, float[] scalar, float thrsh) {
        // collect the outline segments element-wise
        LinkedList<Point2f[]> lines = new LinkedList<>();
        for (TriElement element : m.getElements()) {

            Point2f[] line = new Point2f[2];
            int i = 0;
            for (Edge edge : element.getEdges()) {
                int start = edge.getNode(0).getIndex();
                int end = edge.getNode(1).getIndex();
                float min = Math.min(scalar[start], scalar[end]);
                float max = Math.max(scalar[start], scalar[end]);
                if (max >= thrsh) {
                    if (min < thrsh) {// edge crosses area outlines
                       
                        line[i] = getRelPointOnEdge(edge, scalar, thrsh);

                        if (edge.isOuter()) {// add line segments along outer edge
                            //find node above threshold
                            Node2D n = edge.getNode(0);
                            if (scalar[edge.getNode(1).getIndex()] >= thrsh) {
                                n = edge.getNode(1);
                            }

                            // add line from intersection to node
                            lines.add(new Point2f[]{line[i], n.getPoint2f()});
                        }

                        i++;
                    } else if(edge.isOuter()){ // min is also >= thrsh
                        lines.add(edge.getNodesAsPoints());
                    }
                }
            }
           
            if(i == 2){
                lines.add(line);
            }
        }
       
        // remove invalid segments
        for (int i = 0; i < lines.size(); i++) {
            // check for zero length
            Point2f[] line = lines.get(i);
            if(line[0].equals(line[1])){
                lines.remove(i--);
                continue;
            }
            // check for duplicates
            for (int j = i+1; j < lines.size(); j++) {
                if(Arrays.equals(lines.get(i), lines.get(j))){
                    lines.remove(j);
                }
            }
        }
       
        // sort segments and construct path
        GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
       
        if(lines.isEmpty())
            return p;
       
        Point2f[] points = lines.getFirst();
       
        Point2f point;
        Point2f firstPoint = points[0];
        Point2f prevPoint = points[1];
       
        p.moveTo(firstPoint.x, firstPoint.y);
        p.lineTo(prevPoint.x, prevPoint.y);
        lines.remove(points);
       
        while(true){
            point = null;
            for(int i=0; i<lines.size(); i++){
                points = lines.get(i);
                if(points[0].equals(prevPoint)){
                    point = points[1];
                    break;
                }
                if (points[1].equals(prevPoint)){
                    point = points[0];
                    break;
                }
            }
           
            if(point == null){
                System.out.println("zero length line: " + points[0].equals(points[1]));
//                throw new NullPointerException("Error constructing scalar plot: area outlines not closed");
                System.err.println("Error constructing scalar plot: area outlines not closed");
                Toolkit.getDefaultToolkit().beep();
                return null;
            }
           
            if(firstPoint.equals(point)){
                // close loop
                p.closePath();
                lines.remove(points);
               
                if(lines.isEmpty())
                    break;
               
                // start new loop
                points = lines.getFirst();
                firstPoint = points[0];
                p.moveTo(firstPoint.x, firstPoint.y);
                point = points[1];
            }
           
            p.lineTo(point.x, point.y);
            lines.remove(points);
           
            prevPoint = point;
        }
       
        return p;
    }
   
    private static Shape createScalarPlotArea(TriMesh2D m, float[] scalar, float thrsh){
        List<Edge> edges = m.getEdges();
        HashMap<Edge, Point2f> intPoints = new HashMap<>(edges.size()/3);
        for (Edge edge : edges) {
            Node2D[] n = edge.getNodes();
            float f1 = scalar[n[0].getIndex()];
            float f2 = scalar[n[1].getIndex()];

            if ((f1 >= thrsh && f2 < thrsh) || (f2 >= thrsh && f1 < thrsh)) {
                Point2f p1 = n[0].getPoint2f();
                Point2f p2 = n[1].getPoint2f();
                intPoints.put(edge, interpolate(p1, f1, p2, f2, thrsh));
            }
        }
       
        List<TriElement> elements = m.getElements();
        MultiPolygon[] polys = new MultiPolygon[elements.size()];
       
        for (int i = 0; i < elements.size(); i++) {
            TriElement element = elements.get(i);
           
            Shape s = getElementSubShape(element, scalar, thrsh, intPoints);
           
            if(s != null){
                polys[i] = JTSUtils.createMultiPolygon(s);
            }
        }
       
        GeometryCollection gc;
        gc = JTSUtils.getFactory().createGeometryCollection(polys);
       
        Geometry union = gc.buffer(0);
        return JTSUtils.toShape(union);
    }
   
    /**
     * Computes the region of element e that is above the threshold thrsh.
     * @param e a tri-element
     * @param scalar the scalar field
     * @param thrsh the scalar threshold
     * @return the subshape of e that is above the threshold in the scalar
     * field. If the whole element is above the threshold the result will be 
     * kongurent to e.getShape(). If the whole element is below the threshold
     * an empty shape is returned.
     */
    private static Shape getElementSubShape(TriElement e, float[] scalar, float thrsh, Map<Edge, Point2f> intPoints){
        GeneralPath path = new GeneralPath();
       
        // find first node above threshold
        Point2f p = null;
        Node2D n = null;
        int i;
        for (i = 0; i < 3; i++) {
            n = e.getNode(i);
            if(scalar[n.getIndex()] >= thrsh){
                p = n.getPoint2f();
                break;
            }
        }
       
        // no node found --> return empty path
        if(p == null)
            return path;
       
        path.moveTo(p.x, p.y);
               
        boolean b, prevB = true; // was previous node above thrsh?
        Node2D prevN = n;
       
        int end = i + 3;
        for (i++; i <= end; i++) {
            n = e.getNode(i % 3);
           
            p = n.getPoint2f();
            b = scalar[n.getIndex()] >= thrsh;
           
            if(b != prevB){ // edge crosses threshold -> interpolate point
                Edge edge = new Edge(prevN, n);
                Point2f pInt = intPoints.get(edge);
                path.lineTo(pInt.x, pInt.y);
            }
            if(b){
                path.lineTo(p.x, p.y);
            }
           
            prevB = b;
            prevN = n;
        }
       
        return path;
    }
   
    private static Point2f interpolate(Point2f p1, float f1, Point2f p2, float f2, float thrsh){
        if(f1 == f2) // avoid division by 0;
            return p2;
       
        f2 -= f1;
        thrsh -= f1;
        thrsh /= f2;
       
        if(thrsh < 0 || thrsh > 1)
            System.err.println("Interpolartion factor is out of range [0 1].");
       
        p1.interpolate(p2, thrsh);
        return p1;
    }
   
    public static Shape[] createScalarPlotAreas(TriMesh2D m, float[] scalar, float[] thresholds){
        Shape[] plot = new Shape[thresholds.length - 1];
       
        plot[0] = m.createOutlinePath(); // first area includes the whole mesh
       
        for(int i=1; i<plot.length; i++){
            plot[i] = createScalarPlotArea(m, scalar, thresholds[i]);
        }
       
        return plot;
    }

}
TOP

Related Classes of bs.bs2d.gui.plot.ScalarPlotUtils

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.