001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.draw;
003
004import java.awt.Shape;
005import java.awt.geom.Ellipse2D;
006import java.awt.geom.GeneralPath;
007import java.awt.geom.Rectangle2D;
008import java.util.Optional;
009import java.util.stream.Stream;
010
011import org.openstreetmap.josm.tools.GuiSizesHelper;
012
013/**
014 * A list of possible symbol shapes.
015 * @since 10875
016 */
017public enum SymbolShape {
018    /**
019     * A square
020     */
021    SQUARE("square", 4, Math.PI / 4),
022    /**
023     * A circle
024     */
025    CIRCLE("circle", 1, 0),
026    /**
027     * A triangle with sides of equal length
028     */
029    TRIANGLE("triangle", 3, Math.PI / 2),
030    /**
031     * A pentagon
032     */
033    PENTAGON("pentagon", 5, Math.PI / 2),
034    /**
035     * A hexagon
036     */
037    HEXAGON("hexagon", 6, 0),
038    /**
039     * A heptagon
040     */
041    HEPTAGON("heptagon", 7, Math.PI / 2),
042    /**
043     * An octagon
044     */
045    OCTAGON("octagon", 8, Math.PI / 8),
046    /**
047     * a nonagon
048     */
049    NONAGON("nonagon", 9, Math.PI / 2),
050    /**
051     * A decagon
052     */
053    DECAGON("decagon", 10, 0);
054
055    private final String name;
056    final int sides;
057
058    final double rotation;
059
060    SymbolShape(String name, int sides, double rotation) {
061        this.name = name;
062        this.sides = sides;
063        this.rotation = rotation;
064    }
065
066    /**
067     * Create the path for this shape around the given position
068     * @param x The x position
069     * @param y The y position
070     * @param size The size (width for rect, diameter for rest)
071     * @return The symbol.
072     * @since 10875
073     */
074    public Shape shapeAround(double x, double y, double size) {
075        size = GuiSizesHelper.getSizeDpiAdjusted(size);
076        double radius = size / 2;
077        Shape shape;
078        switch (this) {
079        case SQUARE:
080            // optimize for performance reasons
081            shape = new Rectangle2D.Double(x - radius, y - radius, size, size);
082            break;
083        case CIRCLE:
084            shape = new Ellipse2D.Double(x - radius, y - radius, size, size);
085            break;
086        default:
087            shape = buildPolygon(x, y, radius);
088            break;
089        }
090        return shape;
091    }
092
093    private Shape buildPolygon(double cx, double cy, double radius) {
094        GeneralPath polygon = new GeneralPath();
095        for (int i = 0; i < sides; i++) {
096            double angle = ((2 * Math.PI / sides) * i) - rotation;
097            double x = cx + radius * Math.cos(angle);
098            double y = cy + radius * Math.sin(angle);
099            if (i == 0) {
100                polygon.moveTo(x, y);
101            } else {
102                polygon.lineTo(x, y);
103            }
104        }
105        polygon.closePath();
106        return polygon;
107    }
108
109    /**
110     * Gets the number of normally straight sides this symbol has. Returns 1 for a circle.
111     * @return The sides of the symbol
112     */
113    public int getSides() {
114        return sides;
115    }
116
117    /**
118     * Gets the rotation of the first point of this symbol.
119     * @return The rotation
120     */
121    public double getRotation() {
122        return rotation;
123    }
124
125    /**
126     * Get the MapCSS name for this shape
127     * @return The name
128     */
129    public String getName() {
130        return name;
131    }
132
133    /**
134     * Get the shape with the given name
135     * @param val The name to search
136     * @return The shape as optional
137     */
138    public static Optional<SymbolShape> forName(String val) {
139        return Stream.of(values()).filter(shape -> val.equals(shape.name)).findAny();
140    }
141}