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}