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