view flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Polygon2D.java @ 1797:5eec623db50a

Polygon2D: moved 2D vector operation to separate class. flys-artifacts/trunk@3120 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 31 Oct 2011 10:03:32 +0000
parents fe7f9264a2ed
children 552888e9c64a
line wrap: on
line source
package de.intevation.flys.geom;

import java.io.Serializable;

import java.awt.Shape;

import java.awt.geom.Path2D;
import java.awt.geom.Point2D;

import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Collections;

public class Polygon2D
implements   Serializable
{
    private static final double X(Point2D p) {
        return p.getX();
    }

    private static final double Y(Point2D p) {
        return p.getY();
    }

    public static final Comparator<Point2D> POINT_X_CMP =
        new Comparator<Point2D>() {
            public int compare(Point2D a, Point2D b) {
                double d = X(a) - X(b);
                if (d < 0d) return -1;
                return d > 0d ? + 1 : 0;
            }
        };

    public static final Comparator<Point2D []> FIRST_POINT_X =
        new Comparator<Point2D []>() {
            public int compare(Point2D [] a, Point2D [] b) {
                if (a.length == 0) return -1; // should not happen.
                if (b.length == 0) return +1; // should not happen.
                double d = X(a[0]) - Y(b[0]);
                if (d < 0d) return -1;
                return d > 0d ? + 1 : 0;
            }
        };

    protected List<Point2D> points;

    public Polygon2D() {
        points = new ArrayList<Point2D>();
    }

    public void add(double x, double y) {
        points.add(new Point2D.Double(x, y));
    }

    public boolean addCheck(Point2D p) {
        switch (points.size()) {
            case 0:
                points.add(p);
                return true;
            case 1:
                if (VectorUtils.epsilonEquals(points.get(0), p)) {
                    return false;
                }
                points.add(p);
                return true;
            default:
                int L = points.size()-1;
                Point2D last = points.get(L);
                if (VectorUtils.epsilonEquals(last, p)) {
                    return false;
                }
                Point2D before = points.get(L-1);
                if (VectorUtils.collinear(before, last, p)) {
                    points.set(L, p);
                }
                else {
                    points.add(p);
                }
                return true;
        }
    }

    public void addReversed(List<Point2D> other) {
        for (int i = other.size()-1; i >= 0; --i) {
            addCheck(other.get(i));
        }
    }

    public double area() {
        double area = 0d;

		for (int i = 0, N = points.size(); i < N; ++i) {
			int j = (i + 1) % N;
            Point2D pi = points.get(i);
            Point2D pj = points.get(j);
			area += X(pi)*Y(pj);
			area -= X(pj)*Y(pi);
		}

        return 0.5d*area;
    }

    public Shape toShape() {
        Path2D.Double path = new Path2D.Double();

        int N = points.size();

        if (N > 0) {
            Point2D p = points.get(0);
            path.moveTo(X(p), Y(p));
            for (int i = 1; i < N; ++i) {
                p = points.get(i);
                path.lineTo(X(p), Y(p));
            }
            path.closePath();
        }

        return path;
    }

    protected static List<Point2D []> splitByNaNs(
        double [] xs,
        double [] ys
    ) {
        List<Point2D []> parts = new ArrayList<Point2D []>();

        List<Point2D> tmp = new ArrayList<Point2D>(xs.length);

        for (int i = 0; i < xs.length; ++i) {
            double x = xs[i];
            double y = ys[i];

            if (Double.isNaN(x) || Double.isNaN(y)) {
                if (!tmp.isEmpty()) {
                    Point2D [] part = new Point2D[tmp.size()];
                    parts.add(tmp.toArray(part));
                    tmp.clear();
                }
            }
            else {
                tmp.add(new Point2D.Double(x, y));
            }
        }

        if (!tmp.isEmpty()) {
            Point2D [] part = new Point2D[tmp.size()];
            parts.add(tmp.toArray(part));
        }

        return parts;
    }

    protected static boolean removeNoneIntersecting(
        List<Point2D []> As,
        List<Point2D []> Bs
    ) {
        OUTER: for (int i = 0; i < As.size();) {
            Point2D [] a = As.get(i);
            int lo = 0, hi = Bs.size()-1;
            while (lo <= hi) {
                int mid = (lo + hi) >> 1;
                Point2D [] b = Bs.get(mid);
                     if (X(a[0]) > X(b[b.length-1])) lo = mid+1;
                else if (X(a[a.length-1]) < X(b[0])) hi = mid-1;
                else {
                    // found: keep
                    ++i;
                    continue OUTER;
                }
            }
            // not found: remove
            As.remove(i);
        }

        return As.isEmpty();
    }

    protected static void buildPolygons(
        Point2D []      as,
        Point2D []      bs,
        Point2D [][]    rest,
        List<Polygon2D> positives,
        List<Polygon2D> negatives
    ) {
    }

    public static void createPolygons(
        double [] xAs, double [] yAs,
        double [] xBs, double [] yBs,
        List<Polygon2D> positives,
        List<Polygon2D> negatives
    ) {
        if (xAs.length == 0 || xBs.length == 0) {
            return;
        }

        List<Point2D []> splAs = splitByNaNs(xAs, yAs);
        List<Point2D []> splBs = splitByNaNs(xBs, yBs);

        // They feeded us with NaNs only.
        if (splAs.isEmpty() || splBs.isEmpty()) {
            return;
        }

        // Sort each part by x to ensure that the first
        // is the smallest.
        for (Point2D [] splA: splAs) {
            Arrays.sort(splA, POINT_X_CMP);
        }

        for (Point2D [] splB: splBs) {
            Arrays.sort(splB, POINT_X_CMP);
        }

        // Now sort all parts by there first elements.
        // Should be good enough to find overlapping regions.
        Collections.sort(splAs, FIRST_POINT_X);
        Collections.sort(splBs, FIRST_POINT_X);

        // Check if the two series intersect at all.
        // If no then there will be no area between them.

        Point2D [] p1 = splAs.get(0);
        Point2D [] p2 = splBs.get(splBs.size()-1);

        // Sort out the ranges that are not intersecting
        // the ranges in the other series.
        // We are going to merge them anyway
        // so this is not strictly required. 
        // Keep it to recude cases.
        if (removeNoneIntersecting(splAs, splBs)
        ||  removeNoneIntersecting(splBs, splAs)
        ) {
            // They do not intersect at all.
            return;
        }

        // TODO: Intersect/split the two series parts.
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org