All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.utils.GeometryUtils Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.utils;

import com.alee.api.annotations.NotNull;
import com.alee.api.annotations.Nullable;

import java.awt.*;
import java.util.List;

/**
 * This class provides a set of utilities to work with points and various shapes.
 *
 * @author Mikle Garin
 */
public final class GeometryUtils
{
    /**
     * Private constructor to avoid instantiation.
     */
    private GeometryUtils ()
    {
        throw new UtilityException ( "Utility classes are not meant to be instantiated" );
    }

    /**
     * Returns rectangle containing all specified points.
     *
     * @param points points to process
     * @return rectangle containing all specified points
     */
    @Nullable
    public static Rectangle getContainingRect ( @Nullable final List points )
    {
        return points != null && points.size () > 0 ? getContainingRect ( points.toArray ( new Point[ points.size () ] ) ) : null;
    }

    /**
     * Returns rectangle containing all specified points.
     *
     * @param points points to process
     * @return rectangle containing all specified points
     */
    @Nullable
    public static Rectangle getContainingRect ( @Nullable final Point... points )
    {
        final Rectangle rect;
        if ( points != null && points.length > 0 )
        {
            rect = new Rectangle ( points[ 0 ], new Dimension ( 0, 0 ) );
            int i = 1;
            while ( i < points.length )
            {
                final Point p = points[ i ];
                if ( p.x < rect.x )
                {
                    final int diff = rect.x - p.x;
                    rect.x = p.x;
                    rect.width += diff;
                }
                else if ( rect.x + rect.width < p.x )
                {
                    rect.width = p.x - rect.x;
                }
                if ( p.y < rect.y )
                {
                    final int diff = rect.y - p.y;
                    rect.y = p.y;
                    rect.height += diff;
                }
                else if ( rect.y + rect.height < p.y )
                {
                    rect.height = p.y - rect.y;
                }
                i++;
            }
            if ( rect.width == 0 )
            {
                rect.width = 1;
            }
            if ( rect.height == 0 )
            {
                rect.height = 1;
            }
        }
        else
        {
            rect = null;
        }
        return rect;
    }

    /**
     * Returns rectangle containing all specified rectangles.
     *
     * @param rectangles rectangles to process
     * @return rectangle containing all specified rectangles
     */
    @Nullable
    public static Rectangle getContainingRect ( @Nullable final Rectangle... rectangles )
    {
        Rectangle rect = null;
        if ( rectangles != null && rectangles.length > 0 )
        {
            rect = rectangles[ 0 ];
            int i = 1;
            while ( i < rectangles.length )
            {
                rect = getContainingRect ( rect, rectangles[ i ] );
                i++;
            }
        }
        return rect;
    }

    /**
     * Returns rectangle containing two others.
     *
     * @param r1 first rectangle
     * @param r2 second rectangle
     * @return rectangle containing two others or null if both rectangles are null
     */
    @Nullable
    public static Rectangle getContainingRect ( @Nullable final Rectangle r1, @Nullable final Rectangle r2 )
    {
        final Rectangle rect;
        if ( r1 != null && r2 != null )
        {
            final int minX = Math.min ( r1.x, r2.x );
            final int minY = Math.min ( r1.y, r2.y );
            final int maxX = Math.max ( r1.x + r1.width, r2.x + r2.width );
            final int maxY = Math.max ( r1.y + r1.height, r2.y + r2.height );
            rect = new Rectangle ( minX, minY, maxX - minX, maxY - minY );
        }
        else if ( r1 != null )
        {
            rect = r1;
        }
        else
        {
            rect = r2;
        }
        return rect;
    }

    /**
     * Returns rectangle containing all specified points.
     *
     * @param points points to process
     * @return rectangle containing all specified points
     */
    @NotNull
    public static Rectangle getNonNullContainingRect ( @NotNull final List points )
    {
        final Rectangle rect;
        if ( points.size () > 0 )
        {
            rect = getNonNullContainingRect ( points.toArray ( new Point[ points.size () ] ) );
        }
        else
        {
            throw new RuntimeException ( "At least one point must be specified" );
        }
        return rect;
    }

    /**
     * Returns rectangle containing all specified points.
     *
     * @param points points to process
     * @return rectangle containing all specified points
     */
    @NotNull
    public static Rectangle getNonNullContainingRect ( @NotNull final Point... points )
    {
        final Rectangle rect;
        if ( points.length > 0 )
        {
            rect = new Rectangle ( points[ 0 ], new Dimension ( 0, 0 ) );
            int i = 1;
            while ( i < points.length )
            {
                final Point p = points[ i ];
                if ( p.x < rect.x )
                {
                    final int diff = rect.x - p.x;
                    rect.x = p.x;
                    rect.width += diff;
                }
                else if ( rect.x + rect.width < p.x )
                {
                    rect.width = p.x - rect.x;
                }
                if ( p.y < rect.y )
                {
                    final int diff = rect.y - p.y;
                    rect.y = p.y;
                    rect.height += diff;
                }
                else if ( rect.y + rect.height < p.y )
                {
                    rect.height = p.y - rect.y;
                }
                i++;
            }
            if ( rect.width == 0 )
            {
                rect.width = 1;
            }
            if ( rect.height == 0 )
            {
                rect.height = 1;
            }
        }
        else
        {
            throw new RuntimeException ( "At least one point must be specified" );
        }
        return rect;
    }

    /**
     * Returns rectangle containing all specified rectangles.
     *
     * @param rectangles rectangles to process
     * @return rectangle containing all specified rectangles
     */
    @NotNull
    public static Rectangle getNonNullContainingRect ( @NotNull final Rectangle... rectangles )
    {
        Rectangle rect;
        if ( rectangles.length > 0 )
        {
            rect = rectangles[ 0 ];
            int i = 1;
            while ( i < rectangles.length )
            {
                rect = getNonNullContainingRect ( rect, rectangles[ i ] );
                i++;
            }
        }
        else
        {
            throw new RuntimeException ( "At least one rectangle must be specified" );
        }
        return rect;
    }

    /**
     * Returns rectangle containing two others.
     *
     * @param r1 first rectangle
     * @param r2 second rectangle
     * @return rectangle containing two others or null if both rectangles are null
     */
    @NotNull
    public static Rectangle getNonNullContainingRect ( @NotNull final Rectangle r1, @NotNull final Rectangle r2 )
    {
        final int minX = Math.min ( r1.x, r2.x );
        final int minY = Math.min ( r1.y, r2.y );
        final int maxX = Math.max ( r1.x + r1.width, r2.x + r2.width );
        final int maxY = Math.max ( r1.y + r1.height, r2.y + r2.height );
        return new Rectangle ( minX, minY, maxX - minX, maxY - minY );
    }

    /**
     * Returns middle point for the specified rectangle.
     *
     * @param rectangle rectangle to process
     * @return middle point for the specified rectangle
     */
    @NotNull
    public static Point middle ( @NotNull final Rectangle rectangle )
    {
        return new Point ( rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2 );
    }

    /**
     * Returns middle point between the specified points.
     *
     * @param p1 first point
     * @param p2 second point
     * @return middle point between the specified points
     */
    @NotNull
    public static Point middle ( @NotNull final Point p1, @NotNull final Point p2 )
    {
        return new Point ( ( p1.x + p2.x ) / 2, ( p1.y + p2.y ) / 2 );
    }

    /**
     * Returns middle point between the specified points.
     *
     * @param x1 first point X coordinate
     * @param y1 first point Y coordinate
     * @param x2 second point X coordinate
     * @param y2 second point Y coordinate
     * @return middle point between the specified points
     */
    @NotNull
    public static Point middle ( final int x1, final int y1, final int x2, final int y2 )
    {
        return new Point ( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2 );
    }

    /**
     * Returns rectangle expanded in four directions for the specified value.
     *
     * @param rect      rectangle to expand
     * @param expansion expansion
     * @return rectangle expanded in four directions for the specified values
     */
    @NotNull
    public static Rectangle expand ( @NotNull final Rectangle rect, final int expansion )
    {
        return expand ( rect, expansion, expansion, expansion, expansion );
    }

    /**
     * Returns rectangle expanded in four directions for the specified values.
     *
     * @param rect   rectangle to expand
     * @param top    top expansion
     * @param left   left expansion
     * @param bottom bottom expansion
     * @param right  right expansion
     * @return rectangle expanded in four directions for the specified values
     */
    @NotNull
    public static Rectangle expand ( @NotNull final Rectangle rect, final int top, final int left, final int bottom, final int right )
    {
        return new Rectangle ( rect.x - left, rect.y - top, rect.width + left + right, rect.height + top + bottom );
    }

    /**
     * Returns modified point.
     *
     * @param point point to modify
     * @param xMod  X coordinate modifier
     * @param yMod  Y coordinate modifier
     * @return modified point
     */
    @NotNull
    public static Point modify ( @NotNull final Point point, final int xMod, final int yMod )
    {
        return new Point ( point.x + xMod, point.y + yMod );
    }

    /**
     * Returns angle between the line specified by points and y=0 line.
     *
     * @param p1 first line point
     * @param p2 second line point
     * @return angle between the line specified by points and y=0 line
     */
    public static double getAngle ( @NotNull final Point p1, @NotNull final Point p2 )
    {
        return getAngle ( p1.x, p1.y, p2.x, p2.y );
    }

    /**
     * Returns angle between the line specified by points and y=0 line.
     *
     * @param x1 first point X coordinate
     * @param y1 first point Y coordinate
     * @param x2 second point X coordinate
     * @param y2 second point Y coordinate
     * @return angle between the line specified by points and y=0 line
     */
    public static double getAngle ( final int x1, final int y1, final int x2, final int y2 )
    {
        final double angle = Math.asin ( ( y2 - y1 ) / Math.sqrt ( MathUtils.sqr ( x2 - x1 ) + MathUtils.sqr ( y2 - y1 ) ) );
        return x1 > x2 ? -angle - Math.PI : angle;
    }

    /**
     * Returns intersection point of the rectangle and the line going from the middle of that rectangle to the outer point.
     *
     * @param rect  rectangle to process
     * @param outer outer point to process
     * @return intersection point of the rectangle and the line going from the middle of that rectangle to the outer point
     */
    @NotNull
    public static Point findMiddleLineIntersection ( @NotNull final Rectangle rect, @NotNull final Point outer )
    {
        Point intersection = null;
        final Point middle = GeometryUtils.middle ( rect );
        final int x1 = middle.x;
        final int y1 = middle.y;
        final int x2 = outer.x;
        final int y2 = outer.y;
        if ( x2 < rect.x )
        {
            final int x = rect.x;
            final int y = ( x1 * ( y2 - y1 ) - y1 * ( x2 - x1 ) - x * ( y2 - y1 ) ) / ( x1 - x2 );
            if ( y >= rect.y && y <= rect.y + rect.height )
            {
                intersection = new Point ( x, y );
            }
        }
        else if ( x2 > rect.x + rect.width )
        {
            final int x = rect.x + rect.width;
            final int y = ( x1 * ( y2 - y1 ) - y1 * ( x2 - x1 ) - x * ( y2 - y1 ) ) / ( x1 - x2 );
            if ( y >= rect.y && y <= rect.y + rect.height )
            {
                intersection = new Point ( x, y );
            }
        }
        if ( intersection == null )
        {
            if ( y2 < rect.y )
            {
                final int y = rect.y;
                final int x = ( x1 * ( y2 - y1 ) - y1 * ( x2 - x1 ) - y * ( x1 - x2 ) ) / ( y2 - y1 );
                if ( x >= rect.x && x <= rect.x + rect.width )
                {
                    intersection = new Point ( x, y );
                }
            }
            else if ( y2 > rect.y + rect.height )
            {
                final int y = rect.y + rect.height;
                final int x = ( x1 * ( y2 - y1 ) - y1 * ( x2 - x1 ) - y * ( x1 - x2 ) ) / ( y2 - y1 );
                if ( x >= rect.x && x <= rect.x + rect.width )
                {
                    intersection = new Point ( x, y );
                }
            }
            if ( intersection == null )
            {
                intersection = middle;
            }
        }
        return intersection;
    }

    /**
     * Returns angle converted into radians.
     *
     * @param angle angle to convert
     * @return angle converted into radians
     */
    public static double toRadians ( final double angle )
    {
        return angle * Math.PI / 180;
    }

    /**
     * Returns angle converted into degrees.
     *
     * @param angle angle to convert
     * @return angle converted into degrees
     */
    public static double toDegrees ( final double angle )
    {
        return angle * 180 / Math.PI;
    }

    /**
     * Returns distance between two points.
     *
     * @param p1 first point
     * @param p2 second point
     * @return distance between two points
     */
    public static int distance ( @NotNull final Point p1, @NotNull final Point p2 )
    {
        return MathUtils.sqrtToInt ( MathUtils.sqr ( p2.x - p1.x ) + MathUtils.sqr ( p2.y - p1.y ) );
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy