1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.gui.mappaint.styleelement;
|
---|
3 |
|
---|
4 | import java.awt.Color;
|
---|
5 | import java.awt.Shape;
|
---|
6 | import java.awt.Stroke;
|
---|
7 | import java.awt.geom.Ellipse2D;
|
---|
8 | import java.awt.geom.GeneralPath;
|
---|
9 | import java.awt.geom.Rectangle2D;
|
---|
10 | import java.util.Objects;
|
---|
11 | import java.util.Optional;
|
---|
12 | import java.util.stream.Stream;
|
---|
13 |
|
---|
14 | /**
|
---|
15 | * The definition of a symbol that should be rendered at the node position.
|
---|
16 | * @since 10827 Extracted from {@link NodeElement}
|
---|
17 | */
|
---|
18 | public class Symbol {
|
---|
19 | private final SymbolShape symbolShape;
|
---|
20 | /**
|
---|
21 | * The width and height of this symbol
|
---|
22 | */
|
---|
23 | public final int size;
|
---|
24 | /**
|
---|
25 | * The stroke to use for the outline
|
---|
26 | */
|
---|
27 | public final Stroke stroke;
|
---|
28 | /**
|
---|
29 | * The color to draw the stroke with
|
---|
30 | */
|
---|
31 | public final Color strokeColor;
|
---|
32 | /**
|
---|
33 | * The color to fill the interiour of the shape.
|
---|
34 | */
|
---|
35 | public final Color fillColor;
|
---|
36 |
|
---|
37 | /**
|
---|
38 | * Create a new symbol
|
---|
39 | * @param symbol The symbol type
|
---|
40 | * @param size The overall size of the symbol, both width and height are the same
|
---|
41 | * @param stroke The stroke to use for the outline
|
---|
42 | * @param strokeColor The color to draw the stroke with
|
---|
43 | * @param fillColor The color to fill the interiour of the shape.
|
---|
44 | */
|
---|
45 | public Symbol(SymbolShape symbol, int size, Stroke stroke, Color strokeColor, Color fillColor) {
|
---|
46 | if (stroke != null && strokeColor == null)
|
---|
47 | throw new IllegalArgumentException("Stroke given without color");
|
---|
48 | if (stroke == null && fillColor == null)
|
---|
49 | throw new IllegalArgumentException("Either a stroke or a fill color must be given");
|
---|
50 | this.symbolShape = symbol;
|
---|
51 | this.size = size;
|
---|
52 | this.stroke = stroke;
|
---|
53 | this.strokeColor = strokeColor;
|
---|
54 | this.fillColor = fillColor;
|
---|
55 | }
|
---|
56 |
|
---|
57 | @Override
|
---|
58 | public boolean equals(Object obj) {
|
---|
59 | if (obj == null || getClass() != obj.getClass())
|
---|
60 | return false;
|
---|
61 | final Symbol other = (Symbol) obj;
|
---|
62 | return symbolShape == other.symbolShape &&
|
---|
63 | size == other.size &&
|
---|
64 | Objects.equals(stroke, other.stroke) &&
|
---|
65 | Objects.equals(strokeColor, other.strokeColor) &&
|
---|
66 | Objects.equals(fillColor, other.fillColor);
|
---|
67 | }
|
---|
68 |
|
---|
69 | @Override
|
---|
70 | public int hashCode() {
|
---|
71 | return Objects.hash(symbolShape, size, stroke, strokeColor, fillColor);
|
---|
72 | }
|
---|
73 |
|
---|
74 | @Override
|
---|
75 | public String toString() {
|
---|
76 | return "symbolShape=" + symbolShape + " size=" + size +
|
---|
77 | (stroke != null ? (" stroke=" + stroke + " strokeColor=" + strokeColor) : "") +
|
---|
78 | (fillColor != null ? (" fillColor=" + fillColor) : "");
|
---|
79 | }
|
---|
80 |
|
---|
81 | /**
|
---|
82 | * Builds the shape for this symbol
|
---|
83 | * @param x The center x coordinate
|
---|
84 | * @param y The center y coordinate
|
---|
85 | * @return The symbol shape.
|
---|
86 | */
|
---|
87 | public Shape buildShapeAround(double x, double y) {
|
---|
88 | int radius = size / 2;
|
---|
89 | Shape shape;
|
---|
90 | switch (symbolShape) {
|
---|
91 | case SQUARE:
|
---|
92 | // optimize for performance reasons
|
---|
93 | shape = new Rectangle2D.Double(x - radius, y - radius, size, size);
|
---|
94 | break;
|
---|
95 | case CIRCLE:
|
---|
96 | shape = new Ellipse2D.Double(x - radius, y - radius, size, size);
|
---|
97 | break;
|
---|
98 | default:
|
---|
99 | shape = buildPolygon(x, y, radius);
|
---|
100 | break;
|
---|
101 | }
|
---|
102 | return shape;
|
---|
103 | }
|
---|
104 |
|
---|
105 | private Shape buildPolygon(double cx, double cy, int radius) {
|
---|
106 | GeneralPath polygon = new GeneralPath();
|
---|
107 | for (int i = 0; i < symbolShape.sides; i++) {
|
---|
108 | double angle = ((2 * Math.PI / symbolShape.sides) * i) - symbolShape.rotation;
|
---|
109 | double x = cx + radius * Math.cos(angle);
|
---|
110 | double y = cy + radius * Math.sin(angle);
|
---|
111 | if (i == 0) {
|
---|
112 | polygon.moveTo(x, y);
|
---|
113 | } else {
|
---|
114 | polygon.lineTo(x, y);
|
---|
115 | }
|
---|
116 | }
|
---|
117 | polygon.closePath();
|
---|
118 | return polygon;
|
---|
119 | }
|
---|
120 |
|
---|
121 | /**
|
---|
122 | * A list of possible symbol shapes.
|
---|
123 | */
|
---|
124 | public enum SymbolShape {
|
---|
125 | /**
|
---|
126 | * A square
|
---|
127 | */
|
---|
128 | SQUARE("square", 4, Math.PI / 4),
|
---|
129 | /**
|
---|
130 | * A circle
|
---|
131 | */
|
---|
132 | CIRCLE("circle", 1, 0),
|
---|
133 | /**
|
---|
134 | * A triangle with sides of equal lengh
|
---|
135 | */
|
---|
136 | TRIANGLE("triangle", 3, Math.PI / 2),
|
---|
137 | /**
|
---|
138 | * A pentagon
|
---|
139 | */
|
---|
140 | PENTAGON("pentagon", 5, Math.PI / 2),
|
---|
141 | /**
|
---|
142 | * A hexagon
|
---|
143 | */
|
---|
144 | HEXAGON("hexagon", 6, 0),
|
---|
145 | /**
|
---|
146 | * A heptagon
|
---|
147 | */
|
---|
148 | HEPTAGON("heptagon", 7, Math.PI / 2),
|
---|
149 | /**
|
---|
150 | * An octagon
|
---|
151 | */
|
---|
152 | OCTAGON("octagon", 8, Math.PI / 8),
|
---|
153 | /**
|
---|
154 | * a nonagon
|
---|
155 | */
|
---|
156 | NONAGON("nonagon", 9, Math.PI / 2),
|
---|
157 | /**
|
---|
158 | * A decagon
|
---|
159 | */
|
---|
160 | DECAGON("decagon", 10, 0);
|
---|
161 |
|
---|
162 | private final String name;
|
---|
163 | final int sides;
|
---|
164 |
|
---|
165 | final double rotation;
|
---|
166 |
|
---|
167 | SymbolShape(String name, int sides, double rotation) {
|
---|
168 | this.name = name;
|
---|
169 | this.sides = sides;
|
---|
170 | this.rotation = rotation;
|
---|
171 | }
|
---|
172 |
|
---|
173 | /**
|
---|
174 | * Gets the number of normally straight sides this symbol has. Returns 1 for a circle.
|
---|
175 | * @return The sides of the symbol
|
---|
176 | */
|
---|
177 | public int getSides() {
|
---|
178 | return sides;
|
---|
179 | }
|
---|
180 |
|
---|
181 | /**
|
---|
182 | * Gets the rotateion of the first point of this symbol.
|
---|
183 | * @return The roration
|
---|
184 | */
|
---|
185 | public double getRotation() {
|
---|
186 | return rotation;
|
---|
187 | }
|
---|
188 |
|
---|
189 | /**
|
---|
190 | * Get the MapCSS name for this shape
|
---|
191 | * @return The name
|
---|
192 | */
|
---|
193 | public String getName() {
|
---|
194 | return name;
|
---|
195 | }
|
---|
196 |
|
---|
197 | /**
|
---|
198 | * Get the shape with the given name
|
---|
199 | * @param val The name to search
|
---|
200 | * @return The shape as optional
|
---|
201 | */
|
---|
202 | public static Optional<SymbolShape> forName(String val) {
|
---|
203 | return Stream.of(values()).filter(val::equals).findAny();
|
---|
204 | }
|
---|
205 | }
|
---|
206 | }
|
---|