de.cau.cs.kieler.klay.layered.p5edges.splines.SplinesMath.java Source code

Java tutorial

Introduction

Here is the source code for de.cau.cs.kieler.klay.layered.p5edges.splines.SplinesMath.java

Source

/*
 * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient
 *
 * http://www.informatik.uni-kiel.de/rtsys/kieler/
 *
 * Copyright 2014 by
 * + Christian-Albrechts-University of Kiel
 *   + Department of Computer Science
 *     + Real-Time and Embedded Systems Group
 *
 * This code is provided under the terms of the Eclipse Public License (EPL).
 * See the file epl-v10.html for the license text.
 */
package de.cau.cs.kieler.klay.layered.p5edges.splines;

import com.google.common.collect.Iterables;

import de.cau.cs.kieler.core.math.KVector;
import de.cau.cs.kieler.kiml.options.PortSide;
import de.cau.cs.kieler.klay.layered.graph.LNode;
import de.cau.cs.kieler.klay.layered.graph.LPort;

/**
 * Mathematics utility class used in the splines routing.
 *
 * @author tit
 *
 */
public final class SplinesMath {

    /** Differences below this value are treated as zero. */
    private static final double EPSILON = 0.00000001;
    /** The representation for a null value in the output. */
    private static final String NULL_STRING = "(null)";
    // some class statics to increase performance //
    /** 1/2 * Pi. */
    public static final double HALF_PI = Math.PI / 2;
    /** 1/4 * Pi. */
    public static final double QUATER_PI = HALF_PI / 2;
    /** 3/2 * Pi. */
    public static final double THREE_HALF_PI = HALF_PI + HALF_PI + HALF_PI;
    /** 2 * Pi. */
    public static final double TWO_PI = 2.0 * Math.PI;
    /** Three. */
    public static final double THREE = 3;

    /**
     * Private default constructor to prevent instantiating this class.
     */
    private SplinesMath() {
        throw new AssertionError("Instantiating utility class SplinesMath.");
    }

    /**
     * Calculates the intersection point of two straight lines. If lines are
     * parallel, null is returned.
     * 
     * @param pt1
     *            Point on first straight line.
     * @param pt2
     *            Point on second straight line.
     * @param dirPT2
     *            Direction of edge going from pt0 to intersection point
     * @param dirPT1
     *            Direction of edge going from pt1 to intersection point
     * @return A new created KVector representing the intersection or null.
     */
    public static KVector intersect(final KVector pt1, final KVector pt2, final double dirPT1,
            final double dirPT2) {
        return intersect(pt1, new KVector(dirPT1).add(pt1), pt2, new KVector(dirPT2).add(pt2));
    }

    /**
     * Calculates the intersection point of two straight lines. If lines are
     * parallel, null is returned.
     * 
     * @param pt1
     *            First point on first straight line.
     * @param pt2
     *            Second point on first straight line.
     * @param pt3
     *            First point on second straight line.
     * @param pt4
     *            Second point on second straight line.
     * @return A new created KVector representing the intersection or null if
     *         lines are parallel.
     */
    public static KVector intersect(final KVector pt1, final KVector pt2, final KVector pt3, final KVector pt4) {
        final double x1 = pt1.x;
        final double y1 = pt1.y;
        final double x2 = pt2.x;
        final double y2 = pt2.y;
        final double x3 = pt3.x;
        final double y3 = pt3.y;
        final double x4 = pt4.x;
        final double y4 = pt4.y;

        final double divisor = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4));
        if (Math.abs(divisor) < EPSILON) {
            // The two lines are parallel or even coincident
            return null;
        }

        final double first = (x1 * y2) - (y1 * x2);
        final double second = (x3 * y4) - (y3 * x4);

        final double newX = ((first * (x3 - x4)) - (second * (x1 - x2))) / divisor;
        final double newY = ((first * (y3 - y4)) - (second * (y1 - y2))) / divisor;

        return new KVector(newX, newY);
    }

    /**
     * Calculates the inner angle of two KVectors.
     * 
     * @param vec1
     *            First vector.
     * @param vec2
     *            Second Vector.
     * @return The inner angle of vec0 and vec1 in radians. Value is always
     *         between 0 and PI (180).
     */
    public static double innerAngle(final KVector vec1, final KVector vec2) {
        return Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) / (vec1.length() * vec2.length()));
    }

    /**
     * Calculates the inner angle of two directions in radians.
     * 
     * @param dir1
     *            First direction.
     * @param dir2
     *            Second direction.
     * @return The inner angle of dir0 and dir1 in radians. Value is always
     *         between 0 and PI (180).
     */
    public static double innerAngle(final double dir1, final double dir2) {
        final double retval = Math.abs(dir1 - dir2);
        return retval % Math.PI;
    }

    // GWTExcludeStart
    // DecimalFormat not available in gwt
    /** Option for debug formating. */

    /**
     * Constructs a string showing the coordinates of the given KVector.
     * 
     * @param vector
     *            The KVector to display.
     * @return A string representation of the KVector.
     */
    public static String convertKVectorToString(final KVector vector) {
        if (vector == null) {
            return NULL_STRING;
        }
        return "(" + vector.x + "," + vector.y + ")";
    }

    /**
     * Converts all given KVectors to a readable string with rounded results.
     * 
     * @param list
     *            The list of KVectors to create the string for.
     * @return The readable string.
     */
    public static String convertKVectorToString(final KVector... list) {
        if ((list == null) || (list.length == 0)) {
            return NULL_STRING;
        }
        final StringBuilder retVal = new StringBuilder();

        for (final KVector vector : list) {
            retVal.append(convertKVectorToString(vector)).append(", ");
        }

        return retVal.substring(0, retVal.length() - 2);
    }

    /**
     * Converts all given KVectors to a readable string with rounded results.
     * 
     * @param list
     *            The list of KVectors to create the string for.
     * @return The readable string.
     */
    public static String convertKVectorToString(final Iterable<KVector> list) {
        if ((list == null) || (Iterables.size(list) == 0)) {
            return NULL_STRING;
        }
        final StringBuilder retVal = new StringBuilder();

        for (final KVector vector : list) {
            retVal.append(convertKVectorToString(vector)).append(", ");
        }

        return retVal.substring(0, retVal.length() - 2);
    }

    // GWTExcludeEnd

    /**
     * Calculates a vector of given direction up to the coordinate, where it's
     * perpendicular through the given point crosses the vector. The vector's
     * length is returned.
     * 
     * @param direction
     *            Direction of the vector.
     * @param point
     *            Point that the perpendicular shall pass through.
     * @return The vector's length.
     */
    public static double lengthToOrthogonal(final double direction, final KVector point) {
        double angle = innerAngle(direction, point.toRadians());
        int factor = 1;

        if (angle > HALF_PI) {
            // point lays in the opposite direction
            factor = -1;
            angle = angle - QUATER_PI;
        }
        return factor * Math.cos(angle) * point.length();
    }

    /**
     * Converts a {@link PortSide} to the direction from a node's center to the
     * given side in radian.
     * 
     * @param side
     *            The portSide to convert.
     * @return The direction in radian.
     */
    public static double portSideToDirection(final PortSide side) {
        switch (side) {
        case NORTH:
            return SplinesMath.THREE_HALF_PI;
        case EAST:
            return 0.0;
        case SOUTH:
            return SplinesMath.HALF_PI;
        case WEST:
            return Math.PI;
        default:
            return 0.0;
        }
    }

    /**
     * Prints the side of a {@link ConnectedSelfLoopComponent} and all of it's
     * ports to sysout.
     * 
     * @param component
     *            The component to print.
     */
    public static void printConnectedComponent(final ConnectedSelfLoopComponent component) {
        System.out.print("Connected component: " + component.getLoopSide().toString() + ": ");
        for (final LPort port : component.getHidablePorts()) {
            System.out.print(port.toString() + " " + port.getSide().toString() + " / ");
        }
        System.out.println();
    }

    /**
     * Calculates the perpendicular to the given nodeSide through the
     * AbsolutAnchor of the given port. The distance of the AbsolutAnchor to the
     * nodeSide on this perpendicular is returned.
     *
     * @param port
     *            The port to calculate the distance for.
     * @param side
     *            The side of the node to calculate the distance for.
     * @return The distance.
     */
    public static double distPortToNodeEdge(final LPort port, final PortSide side) {
        final KVector portPos = port.getPosition().clone().add(port.getAnchor());
        final KVector nodeSize = port.getNode().getSize();

        switch (side) {
        case NORTH:
            return -portPos.y;
        case EAST:
            return -portPos.x + nodeSize.x;
        case SOUTH:
            return -portPos.y + nodeSize.y;
        case WEST:
            return -portPos.x;
        default:
            return 0.0;
        }
    }

    /**
     * Checks if the given value lays between (or on) the two boundaries. No
     * matter which of them is larger.
     * 
     * @param value
     *            The value to check.
     * @param boundary0
     *            The first boundary.
     * @param boundary1
     *            The second boundary.
     * @return {@code true} if one boundary is {@code (<= value)} and the other
     *         one is {@code (>= value)}.
     */
    public static boolean isBetween(final int value, final int boundary0, final int boundary1) {
        return value < boundary0 ? boundary1 <= value : (value <= boundary1) || (value == boundary0);
    }

    /**
     * Checks if the given value lays between (or on) the two boundaries. No
     * matter which of them is larger.
     * 
     * @param value
     *            The value to check.
     * @param boundary0
     *            The first boundary.
     * @param boundary1
     *            The second boundary.
     * @return {@code true} if one boundary is {@code (<= value)} and the other
     *         one is {@code (>= value)}.
     */
    public static boolean isBetween(final double value, final double boundary0, final double boundary1) {
        if ((Math.abs(boundary0 - value) < EPSILON) || (Math.abs(boundary1 - value) < EPSILON)) {
            return true;
        }
        return (boundary0 - value) > EPSILON ? (value - boundary1) > EPSILON : (boundary1 - value) > EPSILON;
    }

    /**
     * Returns the margin on the given side of a node.
     * 
     * @param node
     *            The node those margin to return.
     * @param side
     *            The port-side those margin to return.
     * @return The margin.
     */
    public static double getMarginOnPortSide(final LNode node, final PortSide side) {
        switch (side) {
        case NORTH:
            return node.getMargin().top;
        case EAST:
            return node.getMargin().right;
        case SOUTH:
            return node.getMargin().bottom;
        case WEST:
            return node.getMargin().left;
        default:
            return 0.0;
        }
    }
}