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

src.gov.nasa.worldwind.symbology.milstd2525.graphics.areas.AttackByFirePosition Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show newest version
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gov.nasa.worldwind.symbology.milstd2525.graphics.areas;

import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.symbology.TacticalGraphicUtil;
import gov.nasa.worldwind.symbology.milstd2525.AbstractMilStd2525TacticalGraphic;
import gov.nasa.worldwind.symbology.milstd2525.graphics.TacGrpSidc;
import gov.nasa.worldwind.util.Logging;

import java.util.*;

/**
 * Implementation of the Attack By Fire Position graphic (2.X.2.5.3.3).
 *
 * @author pabercrombie
 * @version $Id: AttackByFirePosition.java 555 2012-04-25 18:59:29Z pabercrombie $
 */
public class AttackByFirePosition extends AbstractMilStd2525TacticalGraphic
{
    /** Default length of the arrowhead, as a fraction of the total line length. */
    public final static double DEFAULT_ARROWHEAD_LENGTH = 0.2;
    /** Default angle of the arrowhead. */
    public final static Angle DEFAULT_ARROWHEAD_ANGLE = Angle.fromDegrees(70.0);
    /**
     * Default length of the legs of the graphic's base, as a fraction of the distance between the control points the
     * define the base.
     */
    public final static double DEFAULT_LEG_LENGTH = 0.25;

    /** Length of the arrowhead from base to tip, as a fraction of the total line length. */
    protected Angle arrowAngle = DEFAULT_ARROWHEAD_ANGLE;
    /** Angle of the arrowhead. */
    protected double arrowLength = DEFAULT_ARROWHEAD_LENGTH;
    /**
     * Length of the legs on the graphic's base, as a fraction of the distance between the control points that define
     * the base.
     */
    protected double legLength = DEFAULT_LEG_LENGTH;

    /** First control point. */
    protected Position position1;
    /** Second control point. */
    protected Position position2;
    /** Third control point. */
    protected Position position3;

    /** Path used to render the graphic. */
    protected Path[] paths;

    /**
     * Indicates the graphics supported by this class.
     *
     * @return List of masked SIDC strings that identify graphics that this class supports.
     */
    public static List getSupportedGraphics()
    {
        return Arrays.asList(TacGrpSidc.C2GM_OFF_ARS_AFP);
    }

    /**
     * Create a new arrow graphic.
     *
     * @param sidc Symbol code the identifies the graphic.
     */
    public AttackByFirePosition(String sidc)
    {
        super(sidc);
    }

    /**
     * Indicates the angle of the arrowhead.
     *
     * @return Angle of the arrowhead in the graphic.
     */
    public Angle getArrowAngle()
    {
        return this.arrowAngle;
    }

    /**
     * Specifies the angle of the arrowhead in the graphic.
     *
     * @param arrowAngle The angle of the arrowhead. Must be greater than zero degrees and less than 90 degrees.
     */
    public void setArrowAngle(Angle arrowAngle)
    {
        if (arrowAngle == null)
        {
            String msg = Logging.getMessage("nullValue.AngleIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        if (arrowAngle.degrees <= 0 || arrowAngle.degrees >= 90)
        {
            String msg = Logging.getMessage("generic.AngleOutOfRange");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        this.arrowAngle = arrowAngle;
    }

    /**
     * Indicates the length of the arrowhead.
     *
     * @return The length of the arrowhead as a fraction of the total line length.
     */
    public double getArrowLength()
    {
        return this.arrowLength;
    }

    /**
     * Specifies the length of the arrowhead.
     *
     * @param arrowLength Length of the arrowhead as a fraction of the total line length. If the arrowhead length is
     *                    0.25, then the arrowhead length will be one quarter of the total line length.
     */
    public void setArrowLength(double arrowLength)
    {
        if (arrowLength < 0)
        {
            String msg = Logging.getMessage("generic.ArgumentOutOfRange");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        this.arrowLength = arrowLength;
    }

    /**
     * Indicates the length of legs of the graphic's base.
     *
     * @return The length of the legs of the base, as a fraction of the distance between the control points that define
     *         the base.
     */
    public double getLegLength()
    {
        return this.arrowLength;
    }

    /**
     * Specifies the length of the legs of the graphic's base.
     *
     * @param legLength Length of the legs of the graphic's base, as a fraction of the distance between the control
     *                  points that define the base.
     */
    public void setLegLength(double legLength)
    {
        if (legLength < 0)
        {
            String msg = Logging.getMessage("generic.ArgumentOutOfRange");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        this.legLength = legLength;
    }

    /**
     * {@inheritDoc}
     *
     * @param positions Control points that orient the graphic. Must provide at least three points.
     */
    public void setPositions(Iterable positions)
    {
        if (positions == null)
        {
            String message = Logging.getMessage("nullValue.PositionsListIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        try
        {
            Iterator iterator = positions.iterator();
            this.position1 = iterator.next();
            this.position2 = iterator.next();
            this.position3 = iterator.next();
        }
        catch (NoSuchElementException e)
        {
            String message = Logging.getMessage("generic.InsufficientPositions");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.paths = null; // Need to recompute path for the new control points
    }

    /** {@inheritDoc} */
    public Iterable getPositions()
    {
        return Arrays.asList(this.position1, this.position2, this.position3);
    }

    /** {@inheritDoc} */
    public Position getReferencePosition()
    {
        return this.position1;
    }

    /** {@inheritDoc} */
    protected void doRenderGraphic(DrawContext dc)
    {
        if (this.paths == null)
        {
            this.createShapes(dc);
        }

        for (Path path : this.paths)
        {
            path.render(dc);
        }
    }

    /** {@inheritDoc} */
    protected void applyDelegateOwner(Object owner)
    {
        if (this.paths == null)
            return;

        for (Path path : this.paths)
        {
            path.setDelegateOwner(owner);
        }
    }

    /**
     * Create the paths required to draw the graphic.
     *
     * @param dc Current draw context.
     */
    protected void createShapes(DrawContext dc)
    {
        this.paths = new Path[3];

        Position baseMidpoint = new Position(LatLon.interpolate(0.5, this.position2, this.position3), 0);

        // Create a path for the line part of the arrow
        this.paths[0] = this.createPath(Arrays.asList(baseMidpoint, this.position1));

        // Create the arrowhead
        List positions = this.computeArrowheadPositions(dc, baseMidpoint, this.position1);
        this.paths[1] = createPath(positions);

        positions = this.computeBasePositions(dc, this.position2, this.position3, this.position1);
        this.paths[2] = createPath(positions);
    }

    /**
     * Determine the positions that make up the base of the graphic (a trapezoid missing one side).
     *
     * @param dc             Current draw context.
     * @param position1      The first control point that defines the graphic base.
     * @param position2      The second control point that defines the graphic base.
     * @param orientationPos A point on the arrow head side of the graphic. The legs of the base will point away from
     *                       this position.
     *
     * @return Positions that define the graphic's base.
     */
    protected List computeBasePositions(DrawContext dc, Position position1, Position position2,
        Position orientationPos)
    {
        Globe globe = dc.getGlobe();
        //   A \
        //      \
        //       | B
        //       |        x Orientation position
        //       | C
        //      /
        //   D /

        Vec4 pB = globe.computePointFromPosition(position1);
        Vec4 pC = globe.computePointFromPosition(position2);

        // Find vector in the direction of the arrow
        Vec4 vBC = pC.subtract3(pB);

        double legLength = vBC.getLength3() * this.getLegLength();

        Vec4 normal = globe.computeSurfaceNormalAtPoint(pB);

        // Compute a vector perpendicular to the segment and the normal vector
        Vec4 perpendicular = vBC.cross3(normal);

        // Compute a vector parallel to the base. We will find points A and D by adding parallel and perpendicular
        // components to the control points.
        Vec4 parallel = vBC.normalize3().multiply3(legLength);

        // Determine which side of the line BC the orientation point falls on. We want the legs of the base to point
        // away from the orientation position, so set the direction of the perpendicular component according to the
        // sign of the scalar triple product.
        Vec4 vOrientation = globe.computePointFromPosition(orientationPos).subtract3(pB);
        double tripleProduct = perpendicular.dot3(vOrientation);
        double sign = (tripleProduct > 0) ? -1 : 1;

        perpendicular = perpendicular.normalize3().multiply3(legLength * sign);

        // Find point A
        Vec4 pA = pB.add3(perpendicular).subtract3(parallel);

        // Find Point D
        normal = globe.computeSurfaceNormalAtPoint(pB);
        perpendicular = vBC.cross3(normal);
        perpendicular = perpendicular.normalize3().multiply3(legLength * sign);

        Vec4 pD = pC.add3(perpendicular).add3(parallel);

        return TacticalGraphicUtil.asPositionList(globe, pA, pB, pC, pD);
    }

    /**
     * Determine the positions that make up the arrowhead.
     *
     * @param dc   Current draw context.
     * @param base Position of the arrow's starting point.
     * @param tip  Position of the arrow head tip.
     *
     * @return Positions that define the arrowhead.
     */
    protected List computeArrowheadPositions(DrawContext dc, Position base, Position tip)
    {
        Globe globe = dc.getGlobe();
        //                  _
        //             A\    | 1/2 width
        // Pt. 1________B\  _|
        //               /
        //            C/
        //             | |
        //            Length

        Vec4 p1 = globe.computePointFromPosition(base);
        Vec4 pB = globe.computePointFromPosition(tip);

        // Find vector in the direction of the arrow
        Vec4 vB1 = p1.subtract3(pB);

        double arrowLengthFraction = this.getArrowLength();

        // Find the point at the base of the arrowhead
        Vec4 arrowBase = pB.add3(vB1.multiply3(arrowLengthFraction));

        Vec4 normal = globe.computeSurfaceNormalAtPoint(arrowBase);

        // Compute the length of the arrowhead
        double arrowLength = vB1.getLength3() * arrowLengthFraction;
        double arrowHalfWidth = arrowLength * this.getArrowAngle().tanHalfAngle();

        // Compute a vector perpendicular to the segment and the normal vector
        Vec4 perpendicular = vB1.cross3(normal);
        perpendicular = perpendicular.normalize3().multiply3(arrowHalfWidth);

        // Find points A and C
        Vec4 pA = arrowBase.add3(perpendicular);
        Vec4 pC = arrowBase.subtract3(perpendicular);

        return TacticalGraphicUtil.asPositionList(globe, pA, pB, pC);
    }

    /**
     * Create and configure the Path used to render this graphic.
     *
     * @param positions Positions that define the path.
     *
     * @return New path configured with defaults appropriate for this type of graphic.
     */
    protected Path createPath(List positions)
    {
        Path path = new Path(positions);
        path.setFollowTerrain(true);
        path.setPathType(AVKey.GREAT_CIRCLE);
        path.setAltitudeMode(WorldWind.CLAMP_TO_GROUND);
        path.setDelegateOwner(this.getActiveDelegateOwner());
        path.setAttributes(this.getActiveShapeAttributes());
        return path;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy