001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert and Contributors.
006 *
007 * Project Info:  http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022 * USA.
023 *
024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * -------------
028 * GeomUtil.java
029 * -------------
030 * (C) Copyright 2021-present, by Yuri Blankenstein and Contributors.
031 *
032 * Original Author:  Yuri Blankenstein (for ESI TNO);
033 *
034 */
035package org.jfree.chart.util;
036
037import java.awt.Shape;
038import java.awt.geom.AffineTransform;
039import java.awt.geom.Line2D;
040import java.awt.geom.PathIterator;
041import java.awt.geom.Point2D;
042import java.util.ArrayList;
043
044/**
045 * Some utility methods for working with geometry in Java2D.
046 */
047public final class GeomUtil {
048    private GeomUtil() {
049        // Empty for utility classes
050    }
051
052    /**
053     * For each line in {@code lines}, calculates its intersection point with
054     * {@code lineA}, possibly no intersection point exists (i.e. parallel
055     * lines).
056     * 
057     * @param lineA line to calculate the intersection point for.
058     * @param lines lines to calculate the intersection points with.
059     * @return all intersections points between {@code lineA} and {@code lines}.
060     * @see #calculateIntersectionPoint(Line2D, Line2D)
061     */
062    public static Point2D[] calculateIntersectionPoints(Line2D lineA,
063                                                        Line2D... lines) {
064        ArrayList<Point2D> intersectionPoints = new ArrayList<>(
065                lines.length);
066        for (Line2D lineB : lines) {
067            if (lineA.intersectsLine(lineB)) {
068                // Why does Java have the tester method, but not the method to
069                // get the point itself :S
070                intersectionPoints.add(calculateIntersectionPoint(lineA, lineB));
071            }
072        }
073        return intersectionPoints.toArray(new Point2D[0]);
074    }
075
076    /**
077     * Calculates the intersection point of {@code lineA} with {@code lineB},
078     * possibly {@code null} if no intersection point exists (i.e. parallel
079     * lines).
080     * 
081     * @param lineA the first line for the calculation
082     * @param lineB the second line for the calculation
083     * @return the intersection point of {@code lineA} with {@code lineB},
084     *         possibly {@code null} if no intersection point exists
085     */
086    public static Point2D calculateIntersectionPoint(Line2D lineA,
087                                                     Line2D lineB) {
088        double x1 = lineA.getX1();
089        double y1 = lineA.getY1();
090        double x2 = lineA.getX2();
091        double y2 = lineA.getY2();
092
093        double x3 = lineB.getX1();
094        double y3 = lineB.getY1();
095        double x4 = lineB.getX2();
096        double y4 = lineB.getY2();
097
098        Point2D p = null;
099
100        double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
101        if (d != 0) {
102            double xi = ((x3 - x4) * (x1 * y2 - y1 * x2)
103                    - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
104            double yi = ((y3 - y4) * (x1 * y2 - y1 * x2)
105                    - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
106
107            p = new Point2D.Double(xi, yi);
108        }
109        return p;
110    }
111
112    /**
113     * Returns all {@link PathIterator#SEG_LINETO line segments} building up a
114     * {@code shape}.
115     * 
116     * @param shape a shape that is built up of {@link PathIterator#SEG_LINETO}
117     *              elements.
118     * @param at    an optional {@code AffineTransform} to be applied to the
119     *              coordinates as they are returned in the iteration, or
120     *              {@code null} if untransformed coordinates are desired
121     * @return all {@link PathIterator#SEG_LINETO line segments} building up the
122     *         {@code shape}
123     * @throws IllegalArgumentException if {@code shape} contains non-straight
124     *                                  line segments (i.e.
125     *                                  {@link PathIterator#SEG_CUBICTO} or
126     *                                  {@link PathIterator#SEG_QUADTO})
127     */
128    public static Line2D[] getLines(Shape shape, AffineTransform at)
129            throws IllegalArgumentException {
130        ArrayList<Line2D> lines = new ArrayList<>();
131        Point2D first = null;
132        Point2D current = null;
133        double[] coords = new double[6];
134        for (PathIterator pathIterator = shape.getPathIterator(at); 
135                !pathIterator.isDone(); pathIterator.next()) {
136            switch (pathIterator.currentSegment(coords)) {
137            case PathIterator.SEG_MOVETO:
138                current = new Point2D.Double(coords[0], coords[1]);
139                break;
140            case PathIterator.SEG_LINETO:
141                Point2D to = new Point2D.Double(coords[0], coords[1]);
142                lines.add(new Line2D.Double(current, to));
143                current = to;
144                break;
145            case PathIterator.SEG_CLOSE:
146                lines.add(new Line2D.Double(current, first));
147                current = first;
148                break;
149            default:
150                throw new IllegalArgumentException(
151                        "Shape contains non-straight line segments");
152            }
153            if (null == first)
154                first = current;
155        }
156        return lines.toArray(new Line2D[0]);
157    }
158}