From 97c7f7a916f4ac5f11c4d5ad7907c1551761d9d3 Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Fri, 5 Aug 2022 19:50:57 +0200 Subject: [PATCH] Add builder-pattern to all Markers, MarkerSet, Line and Shape --- .../api/markers/DistanceRangedMarker.java | 38 +++++ .../bluemap/api/markers/ExtrudeMarker.java | 131 +++++++++++++++++- .../bluemap/api/markers/HtmlMarker.java | 76 +++++++++- .../bluemap/api/markers/LineMarker.java | 97 ++++++++++++- .../bluemap/api/markers/Marker.java | 65 ++++++++- .../bluemap/api/markers/MarkerSet.java | 109 +++++++++++++-- .../bluemap/api/markers/ObjectMarker.java | 66 ++++++++- .../bluemap/api/markers/POIMarker.java | 68 +++++++++ .../bluemap/api/markers/ShapeMarker.java | 120 +++++++++++++++- .../bluecolored/bluemap/api/math/Color.java | 30 +++- .../de/bluecolored/bluemap/api/math/Line.java | 61 ++++++++ .../bluecolored/bluemap/api/math/Shape.java | 62 ++++++++- 12 files changed, 890 insertions(+), 33 deletions(-) diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/DistanceRangedMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/DistanceRangedMarker.java index bd58139..4fcbbf6 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/DistanceRangedMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/DistanceRangedMarker.java @@ -101,4 +101,42 @@ public int hashCode() { return result; } + public static abstract class Builder> + extends Marker.Builder { + + Double minDistance, maxDistance; + + /** + * Sets the minimum distance of the camera to the position of the {@link Marker} for it to be displayed.
+ * If the camera is closer to this {@link Marker} than this distance, it will be hidden! + * + * @param minDistance the new minimum distance + * @return this builder for chaining + */ + public B minDistance(double minDistance) { + this.minDistance = minDistance; + return self(); + } + + /** + * Sets the maximum distance of the camera to the position of the {@link Marker} for it to be displayed.
+ * If the camera is further to this {@link Marker} than this distance, it will be hidden! + * + * @param maxDistance the new maximum distance + * @return this builder for chaining + */ + public B maxDistance(double maxDistance) { + this.maxDistance = maxDistance; + return self(); + } + + @Override + T build(T marker) { + if (minDistance != null) marker.setMinDistance(minDistance); + if (maxDistance != null) marker.setMaxDistance(maxDistance); + return super.build(marker); + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/ExtrudeMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/ExtrudeMarker.java index dbfc411..69b77fe 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/ExtrudeMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/ExtrudeMarker.java @@ -64,12 +64,17 @@ private ExtrudeMarker() { * @see #setShape(Shape, float, float) */ public ExtrudeMarker(String label, Shape shape, float shapeMinY, float shapeMaxY) { - this(label, calculateShapeCenter(Objects.requireNonNull(shape, "shape must not be null"), shapeMinY, shapeMaxY), shape, shapeMinY, shapeMaxY); + this( + label, + calculateShapeCenter(Objects.requireNonNull(shape, "shape must not be null"), shapeMinY, shapeMaxY), + shape, shapeMinY, shapeMaxY + ); } /** * Creates a new {@link ExtrudeMarker}. - *

(Since the shape has its own positions, the position is only used to determine e.g. the distance to the camera)

+ *

(Since the shape has its own positions, the position is only used to determine + * e.g. the distance to the camera)

* * @param label the label of the marker * @param position the coordinates of the marker @@ -90,7 +95,8 @@ public ExtrudeMarker(String label, Vector3d position, Shape shape, float shapeMi /** * Getter for {@link Shape} of this {@link ExtrudeMarker}. - *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points are the z-coordinates in the map.

+ *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points are the + * z-coordinates in the map.

* @return the {@link Shape} */ public Shape getShape() { @@ -117,7 +123,8 @@ public float getShapeMaxY() { /** * Sets the {@link Shape} of this {@link ExtrudeMarker}. - *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points will be the z-coordinates in the map.

+ *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points will be + * the z-coordinates in the map.

* (The shape will be extruded from minY to maxY on the map) * @param shape the new {@link Shape} * @param minY the new min-height (y-coordinate) of the shape on the map @@ -140,7 +147,8 @@ public void centerPosition() { } /** - * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, you'll only see the marker when it is not behind anything. + * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, + * you'll only see the marker when it is not behind anything. * @return true if the depthTest is enabled */ public boolean isDepthTestEnabled() { @@ -148,7 +156,8 @@ public boolean isDepthTestEnabled() { } /** - * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, you'll only see the marker when it is not behind anything. + * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, + * you'll only see the marker when it is not behind anything. * @param enabled if the depth-test should be enabled for this {@link ExtrudeMarker} */ public void setDepthTestEnabled(boolean enabled) { @@ -251,4 +260,114 @@ private static Vector3d calculateShapeCenter(Shape shape, float shapeMinY, float return new Vector3d(center.getX(), centerY, center.getY()); } + /** + * Creates a Builder for {@link ExtrudeMarker}s. + * @return a new Builder + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder extends ObjectMarker.Builder { + + Shape shape; + float shapeMinY, shapeMaxY; + Boolean depthTest; + Integer lineWidth; + Color lineColor; + Color fillColor; + + /** + * Sets the {@link Shape} of the {@link ExtrudeMarker}. + *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points will + * be the z-coordinates in the map.

+ * (The shape will be extruded from minY to maxY on the map) + * @param shape the new {@link Shape} + * @param minY the new min-height (y-coordinate) of the shape on the map + * @param maxY the new max-height (y-coordinate) of the shape on the map + * @return this builder for chaining + */ + public Builder shape(Shape shape, float minY, float maxY) { + this.shape = shape; + this.shapeMinY = minY; + this.shapeMaxY = maxY; + return this; + } + + /** + * Sets the position of the {@link ExtrudeMarker} to the center of the {@link Shape} (it's bounding box). + * @return this builder for chaining + */ + public Builder centerPosition() { + position(null); + return this; + } + + /** + * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, + * you'll only see the marker when it is not behind anything. + * @param enabled if the depth-test should be enabled for this {@link ExtrudeMarker} + * @return this builder for chaining + */ + public Builder depthTestEnabled(boolean enabled) { + this.depthTest = enabled; + return this; + } + + /** + * Sets the width of the lines for the {@link ExtrudeMarker}. + * @param width the new width in pixels + * @return this builder for chaining + */ + public Builder lineWidth(int width) { + this.lineWidth = width; + return this; + } + + /** + * Sets the {@link Color} of the border-line of the shape. + * @param color the new line-color + * @return this builder for chaining + */ + public Builder lineColor(Color color) { + this.lineColor = color; + return this; + } + + /** + * Sets the fill-{@link Color} of the shape. + * @param color the new fill-color + * @return this builder for chaining + */ + public Builder fillColor(Color color) { + this.fillColor = color; + return this; + } + + /** + * Creates a new {@link ExtrudeMarker} with the current builder-settings.
+ * The minimum required settings to build this marker are: + *
    + *
  • {@link #label(String)}
  • + *
  • {@link #shape(Shape, float, float)}
  • + *
+ * @return The new {@link ExtrudeMarker}-instance + */ + @Override + public ExtrudeMarker build() { + ExtrudeMarker marker = new ExtrudeMarker( + checkNotNull(label, "label"), + checkNotNull(shape, "shape"), + shapeMinY, + shapeMaxY + ); + if (depthTest != null) marker.setDepthTestEnabled(depthTest); + if (lineWidth != null) marker.setLineWidth(lineWidth); + if (lineColor != null) marker.setLineColor(lineColor); + if (fillColor != null) marker.setFillColor(fillColor); + return build(marker); + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/HtmlMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/HtmlMarker.java index 3ca1d16..a8d4d03 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/HtmlMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/HtmlMarker.java @@ -120,7 +120,8 @@ public String getHtml() { * *

* Important:
- * Make sure you escape all html-tags from possible user inputs to prevent possible XSS-Attacks on the web-client! + * Make sure you escape all html-tags from possible user inputs to prevent possible + * XSS-Attacks on the web-client! *

* * @param html the html that will be inserted as the marker. @@ -149,4 +150,77 @@ public int hashCode() { return result; } + /** + * Creates a Builder for {@link HtmlMarker}s. + * @return a new Builder + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder extends DistanceRangedMarker.Builder { + + Vector2i anchor; + String html; + + /** + * Sets the position (in pixels) where the html-element is anchored to the map. + * @param anchor the anchor-position in pixels + * @return this builder for chaining + */ + public Builder anchor(Vector2i anchor) { + this.anchor = anchor; + return this; + } + + /** + * Sets the position (in pixels) where the html-element is anchored to the map. + * @param x the anchor-x-position in pixels + * @param y the anchor-y-position in pixels + * @return this builder for chaining + */ + public Builder anchor(int x, int y) { + this.anchor = new Vector2i(x, y); + return this; + } + + /** + * Sets the html for the {@link HtmlMarker}. + * + *

+ * Important:
+ * Make sure you escape all html-tags from possible user inputs to prevent possible XSS-Attacks on the web-client! + *

+ * + * @param html the html that will be inserted as the marker. + * @return this builder for chaining + */ + public Builder html(String html) { + this.html = html; + return this; + } + + /** + * Creates a new {@link HtmlMarker} with the current builder-settings.
+ * The minimum required settings to build this marker are: + *
    + *
  • {@link #setLabel(String)}
  • + *
  • {@link #setPosition(Vector3d)}
  • + *
  • {@link #setHtml(String)}
  • + *
+ * @return The new {@link HtmlMarker}-instance + */ + @Override + public HtmlMarker build() { + HtmlMarker marker = new HtmlMarker( + checkNotNull(label, "label"), + checkNotNull(position, "position"), + checkNotNull(html, "html") + ); + if (anchor != null) marker.setAnchor(anchor); + return build(marker); + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/LineMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/LineMarker.java index 6756505..bdd4d36 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/LineMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/LineMarker.java @@ -59,12 +59,13 @@ private LineMarker() { * @see #setLine(Line) */ public LineMarker(String label, Line line) { - this(label, calculateLineCenter(Objects.requireNonNull(line, "shape must not be null")), line); + this(label, calculateLineCenter(Objects.requireNonNull(line, "line must not be null")), line); } /** * Creates a new {@link LineMarker}. - *

(Since the line has its own positions, the position is only used to determine e.g. the distance to the camera)

+ *

(Since the line has its own positions, the position is only used to determine + * e.g. the distance to the camera)

* * @param label the label of the marker * @param position the coordinates of the marker @@ -106,7 +107,8 @@ public void centerPosition() { } /** - * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, you'll only see the marker when it is not behind anything. + * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, + * you'll only see the marker when it is not behind anything. * @return true if the depthTest is enabled */ public boolean isDepthTestEnabled() { @@ -114,7 +116,8 @@ public boolean isDepthTestEnabled() { } /** - * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, you'll only see the marker when it is not behind anything. + * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, + * you'll only see the marker when it is not behind anything. * @param enabled if the depth-test should be enabled for this {@link LineMarker} */ public void setDepthTestEnabled(boolean enabled) { @@ -181,4 +184,90 @@ private static Vector3d calculateLineCenter(Line line) { return line.getMin().add(line.getMax()).mul(0.5); } + /** + * Creates a Builder for {@link LineMarker}s. + * @return a new Builder + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder extends ObjectMarker.Builder { + + Line line; + Boolean depthTest; + Integer lineWidth; + Color lineColor; + + /** + * Sets the {@link Line} of the {@link LineMarker}. + * @param line the new {@link Line} + * @return this builder for chaining + */ + public Builder line(Line line) { + this.line = line; + return this; + } + + /** + * Sets the position of the {@link LineMarker} to the center of the {@link Line} (it's bounding box). + * @return this builder for chaining + */ + public Builder centerPosition() { + position(null); + return this; + } + + /** + * If the depth-test is disabled, you can see the marker fully through all objects on the map. + * If it is enabled, you'll only see the marker when it is not behind anything. + * @param enabled if the depth-test should be enabled for the {@link LineMarker} + * @return this builder for chaining + */ + public Builder depthTestEnabled(boolean enabled) { + this.depthTest = enabled; + return this; + } + + /** + * Sets the width of the lines for this {@link LineMarker}. + * @param width the new width in pixels + * @return this builder for chaining + */ + public Builder lineWidth(int width) { + this.lineWidth = width; + return this; + } + + /** + * Sets the {@link Color} of the border-line of the shape. + * @param color the new line-color + */ + public Builder lineColor(Color color) { + this.lineColor = color; + return this; + } + + /** + * Creates a new {@link LineMarker} with the current builder-settings.
+ * The minimum required settings to build this marker are: + *
    + *
  • {@link #label(String)}
  • + *
  • {@link #line(Line)}
  • + *
+ * @return The new {@link LineMarker}-instance + */ + public LineMarker build() { + LineMarker marker = new LineMarker( + checkNotNull(label, "label"), + checkNotNull(line, "line") + ); + if (depthTest != null) marker.setDepthTestEnabled(depthTest); + if (lineWidth != null) marker.setLineWidth(lineWidth); + if (lineColor != null) marker.setLineColor(lineColor); + return build(marker); + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/Marker.java b/src/main/java/de/bluecolored/bluemap/api/markers/Marker.java index 3439fa0..08ca4e1 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/Marker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/Marker.java @@ -75,7 +75,7 @@ public String getLabel() { */ public void setLabel(String label) { //escape html-tags - this.label = label + this.label = Objects.requireNonNull(label, "label cannot be null") .replace("&", "&") .replace("<", "<") .replace(">", ">"); @@ -94,7 +94,7 @@ public Vector3d getPosition() { * @param position the new position */ public void setPosition(Vector3d position) { - this.position = position; + this.position = Objects.requireNonNull(position, "position cannot be null"); } /** @@ -127,4 +127,65 @@ public int hashCode() { return result; } + public static abstract class Builder> { + + String label; + Vector3d position; + + /** + * Sets the label of the {@link Marker}. + *

(HTML-Tags will be escaped.)

+ * @param label the new label for the {@link Marker} + * @return this builder for chaining + */ + public B label(String label) { + this.label = label; + return self(); + } + + /** + * Sets the position of where the {@link Marker} lives on the map. + * @param position the new position + * @return this builder for chaining + */ + public B position(Vector3d position) { + this.position = position; + return self(); + } + + /** + * Sets the position of where the {@link Marker} lives on the map. + * @param x the x-coordinate of the new position + * @param y the y-coordinate of the new position + * @param z the z-coordinate of the new position + * @return this builder for chaining + */ + public B position(int x, int y, int z) { + return position(new Vector3d(x, y, z)); + } + + /** + * Creates a new {@link Marker} with the current builder-settings + * @return The new {@link Marker}-instance + */ + public abstract T build(); + + T build(T marker) { + if (label != null) marker.setLabel(label); + if (position != null) marker.setPosition(position); + return marker; + } + + @SuppressWarnings("unchecked") + B self() { + return (B) this; + } + + O checkNotNull(O object, String name) { + if (object == null) throw new IllegalStateException(name + " has to be set and cannot be null"); + return object; + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/MarkerSet.java b/src/main/java/de/bluecolored/bluemap/api/markers/MarkerSet.java index e6f40c2..56a0ad3 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/MarkerSet.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/MarkerSet.java @@ -82,15 +82,20 @@ public MarkerSet(String label, boolean toggleable, boolean defaultHidden) { /** * Getter for the label of this {@link MarkerSet}. - *

The label is used in the web-app to name the toggle-button of this {@link MarkerSet} if it is toggleable. ({@link #isToggleable()})

+ *

The label is used in the web-app to name the toggle-button of this {@link MarkerSet} if it is toggleable. + * ({@link #isToggleable()})

+ * * @return the label of this {@link MarkerSet} */ public String getLabel() { return label; } + /** * Sets the label of this {@link MarkerSet}. - *

The label is used in the web-app to name the toggle-button of this {@link MarkerSet} if it is toggleable. ({@link #isToggleable()})

+ *

The label is used in the web-app to name the toggle-button of this {@link MarkerSet} if it is toggleable. + * ({@link #isToggleable()})

+ * * @param label the new label */ public void setLabel(String label) { @@ -99,7 +104,9 @@ public void setLabel(String label) { /** * Checks if the {@link MarkerSet} is toggleable. - *

If this is true, the web-app will display a toggle-button for this {@link MarkerSet} so the user can choose to enable/disable all markers of this set.

+ *

If this is true, the web-app will display a toggle-button for this {@link MarkerSet} so the user + * can choose to enable/disable all markers of this set.

+ * * @return whether this {@link MarkerSet} is toggleable */ public boolean isToggleable() { @@ -108,7 +115,9 @@ public boolean isToggleable() { /** * Changes if this {@link MarkerSet} is toggleable. - *

If this is true, the web-app will display a toggle-button for this {@link MarkerSet} so the user can choose to enable/disable all markers of this set.

+ *

If this is true, the web-app will display a toggle-button for this {@link MarkerSet} so the user + * can choose to enable/disable all markers of this set.

+ * * @param toggleable whether this {@link MarkerSet} should be toggleable */ public void setToggleable(boolean toggleable) { @@ -117,7 +126,9 @@ public void setToggleable(boolean toggleable) { /** * Checks if this {@link MarkerSet} is hidden by default. - *

This is basically the default-state of the toggle-button from {@link #isToggleable()}. If this is true the markers of this marker set will initially be hidden and can be displayed using the toggle-button.

+ *

This is basically the default-state of the toggle-button from {@link #isToggleable()}. + * If this is true the markers of this marker set will initially be hidden and can be displayed + * using the toggle-button.

* * @return whether this {@link MarkerSet} is hidden by default * @see #isToggleable() @@ -128,13 +139,14 @@ public boolean isDefaultHidden() { /** * Sets if this {@link MarkerSet} is hidden by default. - *

This is basically the default-state of the toggle-button from {@link #isToggleable()}. If this is true the markers of this marker set will initially be hidden and can be displayed using the toggle-button.

+ *

This is basically the default-state of the toggle-button from {@link #isToggleable()}. If this is + * true the markers of this marker set will initially be hidden and can be displayed using the toggle-button.

* - * @param defaultHide whether this {@link MarkerSet} should be hidden by default + * @param defaultHidden whether this {@link MarkerSet} should be hidden by default * @see #isToggleable() */ - public void setDefaultHidden(boolean defaultHide) { - this.defaultHidden = defaultHide; + public void setDefaultHidden(boolean defaultHidden) { + this.defaultHidden = defaultHidden; } /** @@ -169,4 +181,83 @@ public int hashCode() { return result; } + /** + * Creates a Builder for {@link MarkerSet}s. + * @return a new Builder + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String label; + private Boolean toggleable, defaultHidden; + + /** + * Sets the label of the {@link MarkerSet}. + *

The label is used in the web-app to name the toggle-button of the {@link MarkerSet} if it is toggleable. + * ({@link #toggleable(Boolean)})

+ * + * @param label the new label + * @return this builder for chaining + */ + public Builder label(String label) { + this.label = label; + return this; + } + + /** + * Changes if the {@link MarkerSet} is toggleable. + *

If this is true, the web-app will display a toggle-button for the {@link MarkerSet} + * so the user can choose to enable/disable all markers of this set.

+ * + * @param toggleable whether the {@link MarkerSet} should be toggleable + * @return this builder for chaining + */ + public Builder toggleable(Boolean toggleable) { + this.toggleable = toggleable; + return this; + } + + /** + * Sets if this {@link MarkerSet} is hidden by default. + *

This is basically the default-state of the toggle-button from {@link #toggleable(Boolean)}. + * If this is true the markers of this marker set will initially be hidden and can be displayed + * using the toggle-button.

+ * + * @param defaultHidden whether this {@link MarkerSet} should be hidden by default + * @return this builder for chaining + * @see #isToggleable() + */ + public Builder defaultHidden(Boolean defaultHidden) { + this.defaultHidden = defaultHidden; + return this; + } + + /** + * Creates a new {@link MarkerSet} with the current builder-settings.
+ * The minimum required settings to build this marker-set are: + *
    + *
  • {@link #setLabel(String)}
  • + *
+ * @return The new {@link MarkerSet}-instance + */ + public MarkerSet build() { + MarkerSet markerSet = new MarkerSet( + checkNotNull(label, "label") + ); + if (toggleable != null) markerSet.setToggleable(toggleable); + if (defaultHidden != null) markerSet.setDefaultHidden(defaultHidden); + return markerSet; + } + + @SuppressWarnings("SameParameterValue") + O checkNotNull(O object, String name) { + if (object == null) throw new IllegalStateException(name + " has to be set and cannot be null"); + return object; + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/ObjectMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/ObjectMarker.java index c248a7c..a2b07df 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/ObjectMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/ObjectMarker.java @@ -47,19 +47,20 @@ public ObjectMarker(String type, String label, Vector3d position) { /** * Getter for the detail of this marker. The label can include html-tags. - * @return the detail of this {@link ObjectMarker} + * @return the detail of this {@link Marker} */ public String getDetail() { return detail; } /** - * Sets the detail of this {@link ObjectMarker}. The detail can include html-tags.
+ * Sets the detail of this {@link Marker}. The detail can include html-tags.
* This is the text that will be displayed on the popup when you click on this marker. *

* Important:
- * Html-tags in the label will not be escaped, so you can use them to style the {@link ObjectMarker}-detail.
- * Make sure you escape all html-tags from possible user inputs to prevent possible XSS-Attacks on the web-client! + * Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.
+ * Make sure you escape all html-tags from possible user inputs to prevent possible + * XSS-Attacks on the web-client! *

* * @param detail the new detail for this {@link ObjectMarker} @@ -129,4 +130,61 @@ public int hashCode() { return result; } + public static abstract class Builder> + extends DistanceRangedMarker.Builder { + + String detail; + String link; + boolean newTab; + + /** + * Sets the detail of the {@link Marker}. The detail can include html-tags.
+ * This is the text that will be displayed on the popup when you click on the marker. + *

+ * Important:
+ * Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.
+ * Make sure you escape all html-tags from possible user inputs to prevent possible + * XSS-Attacks on the web-client! + *

+ * + * @param detail the new detail for the {@link Marker} + * @return this builder for chaining + */ + public B detail(String detail) { + this.detail = detail; + return self(); + } + + /** + * Sets the link-address of the {@link Marker}.
+ * If a link is present, this link will be followed when the user clicks on the marker in the web-app. + * + * @param link the link, or null to disable the link + * @param newTab whether the link should be opened in a new tab + * @return this builder for chaining + */ + public B link(String link, boolean newTab) { + this.link = link; + this.newTab = newTab; + return self(); + } + + /** + * The {@link Marker} will have no link. (See: {@link #link(String, boolean)}) + * @return this builder for chaining + */ + public B noLink() { + this.link = null; + this.newTab = false; + return self(); + } + + T build(T marker) { + if (detail != null) marker.setDetail(detail); + if (link != null) marker.setLink(link, newTab); + return super.build(marker); + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/POIMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/POIMarker.java index 3c7dd3d..234ed11 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/POIMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/POIMarker.java @@ -134,4 +134,72 @@ public int hashCode() { return result; } + /** + * Creates a Builder for {@link POIMarker}s. + * @return a new Builder + */ + public static Builder toBuilder() { + return new Builder(); + } + + public static class Builder extends DistanceRangedMarker.Builder { + + String icon; + Vector2i anchor; + + /** + * Sets the icon for the {@link POIMarker}. + * @param iconAddress the web-address of the icon-image. Can be an absolute or relative. + * You can also use an address returned by {@link WebApp#createImage(java.awt.image.BufferedImage, String)}. + * @param anchorX the x-position of the position (in pixels) where the icon is anchored to the map + * @param anchorY the y-position of the position (in pixels) where the icon is anchored to the map + * @return this builder for chaining + */ + public Builder icon(String iconAddress, int anchorX, int anchorY) { + return icon(iconAddress, new Vector2i(anchorX, anchorY)); + } + + /** + * Sets the icon for the {@link POIMarker}. + * @param iconAddress the web-address of the icon-image. Can be an absolute or relative. + * You can also use an address returned by {@link WebApp#createImage(java.awt.image.BufferedImage, String)}. + * @param anchor the position of the position (in pixels) where the icon is anchored to the map + * @return this builder for chaining + */ + public Builder icon(String iconAddress, Vector2i anchor) { + this.icon = Objects.requireNonNull(iconAddress, "iconAddress must not be null"); + this.anchor = Objects.requireNonNull(anchor, "anchor must not be null"); + return this; + } + + /** + * The {@link POIMarker} will use the default icon. (See: {@link #icon(String, Vector2i)}) + * @return this builder for chaining + */ + public Builder defaultIcon() { + this.icon = null; + this.anchor = null; + return this; + } + + /** + * Creates a new {@link POIMarker} with the current builder-settings.
+ * The minimum required settings to build this marker are: + *
    + *
  • {@link #setLabel(String)}
  • + *
  • {@link #setPosition(Vector3d)}
  • + *
+ * @return The new {@link POIMarker}-instance + */ + public POIMarker build() { + POIMarker marker = new POIMarker( + checkNotNull(label, "label"), + checkNotNull(position, "position") + ); + if (icon != null) marker.setIcon(icon, anchor); + return build(marker); + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/ShapeMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/ShapeMarker.java index 067bf98..ddc5fbc 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/ShapeMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/ShapeMarker.java @@ -69,7 +69,8 @@ public ShapeMarker(String label, Shape shape, float shapeY) { /** * Creates a new {@link ShapeMarker}. - *

(Since the shape has its own positions, the position is only used to determine e.g. the distance to the camera)

+ *

(Since the shape has its own positions, the position is only used to determine + * e.g. the distance to the camera)

* * @param label the label of the marker * @param position the coordinates of the marker @@ -88,7 +89,8 @@ public ShapeMarker(String label, Vector3d position, Shape shape, float shapeY) { /** * Getter for {@link Shape} of this {@link ShapeMarker}. - *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points are the z-coordinates in the map.

+ *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points are + * the z-coordinates in the map.

* @return the {@link Shape} */ public Shape getShape() { @@ -105,7 +107,8 @@ public float getShapeY() { /** * Sets the {@link Shape} of this {@link ShapeMarker}. - *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points will be the z-coordinates in the map.

+ *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points will be + * the z-coordinates in the map.

* @param shape the new {@link Shape} * @param y the new height (y-coordinate) of the shape on the map * @@ -125,7 +128,8 @@ public void centerPosition() { } /** - * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, you'll only see the marker when it is not behind anything. + * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, + * you'll only see the marker when it is not behind anything. * @return true if the depthTest is enabled */ public boolean isDepthTestEnabled() { @@ -133,7 +137,8 @@ public boolean isDepthTestEnabled() { } /** - * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, you'll only see the marker when it is not behind anything. + * If the depth-test is disabled, you can see the marker fully through all objects on the map. If it is enabled, + * you'll only see the marker when it is not behind anything. * @param enabled if the depth-test should be enabled for this {@link ShapeMarker} */ public void setDepthTestEnabled(boolean enabled) { @@ -233,4 +238,109 @@ private static Vector3d calculateShapeCenter(Shape shape, float shapeY) { return new Vector3d(center.getX(), shapeY, center.getY()); } + /** + * Creates a Builder for {@link ShapeMarker}s. + * @return a new Builder + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder extends ObjectMarker.Builder { + + Shape shape; + float shapeY; + Boolean depthTest; + Integer lineWidth; + Color lineColor; + Color fillColor; + + /** + * Sets the {@link Shape} of the {@link ShapeMarker}. + *

The shape is placed on the xz-plane of the map, so the y-coordinates of the {@link Shape}'s points + * will be the z-coordinates in the map.

+ * @param shape the new {@link Shape} + * @param y the new height (y-coordinate) of the shape on the map + * @return this builder for chaining + */ + public Builder shape(Shape shape, float y) { + this.shape = shape; + this.shapeY = y; + return this; + } + + /** + * Sets the position of the {@link ShapeMarker} to the center of the {@link Shape} (it's bounding box). + * @return this builder for chaining + */ + public Builder centerPosition() { + position(null); + return this; + } + + /** + * If the depth-test is disabled, you can see the marker fully through all objects on the map. + * If it is enabled, you'll only see the marker when it is not behind anything. + * @param enabled if the depth-test should be enabled for the {@link ShapeMarker} + * @return this builder for chaining + */ + public Builder depthTestEnabled(boolean enabled) { + this.depthTest = enabled; + return this; + } + + /** + * Sets the width of the border-line for the {@link ShapeMarker}. + * @param width the new width in pixels + * @return this builder for chaining + */ + public Builder lineWidth(int width) { + this.lineWidth = width; + return this; + } + + /** + * Sets the {@link Color} of the border-line of the shape. + * @param color the new line-color + * @return this builder for chaining + */ + public Builder lineColor(Color color) { + this.lineColor = color; + return this; + } + + /** + * Sets the fill-{@link Color} of the shape. + * @param color the new fill-color + * @return this builder for chaining + */ + public Builder fillColor(Color color) { + this.fillColor = color; + return this; + } + + /** + * Creates a new {@link ShapeMarker} with the current builder-settings.
+ * The minimum required settings to build this marker are: + *
    + *
  • {@link #label(String)}
  • + *
  • {@link #shape(Shape, float)}
  • + *
+ * @return The new {@link ShapeMarker}-instance + */ + public ShapeMarker build() { + ShapeMarker marker = new ShapeMarker( + checkNotNull(label, "label"), + checkNotNull(shape, "shape"), + shapeY + ); + if (depthTest != null) marker.setDepthTestEnabled(depthTest); + if (lineWidth != null) marker.setLineWidth(lineWidth); + if (lineColor != null) marker.setLineColor(lineColor); + if (fillColor != null) marker.setFillColor(fillColor); + return build(marker); + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/math/Color.java b/src/main/java/de/bluecolored/bluemap/api/math/Color.java index f196737..681e79b 100644 --- a/src/main/java/de/bluecolored/bluemap/api/math/Color.java +++ b/src/main/java/de/bluecolored/bluemap/api/math/Color.java @@ -68,6 +68,15 @@ public Color(int i) { this((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF, ((i >> 24) & 0xFF) / 255f); } + /** + * Creates a new color from the given integer in the format 0xRRGGBB. + * @param i the integer to create the color from + * @param alpha the alpha value in range 0-1 + */ + public Color(int i, float alpha) { + this((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF, alpha); + } + /** * The value can be an integer in String-Format (see {@link #Color(int)}) or a string in hexadecimal format * prefixed with # (css-style: e.g. #f16 becomes #ff1166). @@ -78,17 +87,34 @@ public Color(String cssColorString) { this(parseColorString(Objects.requireNonNull(cssColorString))); } + /** + * Getter for the red-component of the color. + * @return the red-component of the color in range 0-255 + */ public int getRed() { return r; } + + /** + * Getter for the green-component of the color. + * @return the green-component of the color in range 0-255 + */ public int getGreen() { return g; } + /** + * Getter for the blue-component of the color. + * @return the blue-component of the color in range 0-255 + */ public int getBlue() { return b; } + /** + * Getter for the alpha-component of the color. + * @return the alpha-component of the color in range 0-1 + */ public float getAlpha() { return a; } @@ -97,7 +123,9 @@ private static int parseColorString(String val) { if (val.charAt(0) == '#') { val = val.substring(1); if (val.length() == 3) val = val + "f"; - if (val.length() == 4) val = "" + val.charAt(0) + val.charAt(0) + val.charAt(1) + val.charAt(1) + val.charAt(2) + val.charAt(2) + val.charAt(3) + val.charAt(3); + if (val.length() == 4) val = "" + + val.charAt(0) + val.charAt(0) + val.charAt(1) + val.charAt(1) + + val.charAt(2) + val.charAt(2) + val.charAt(3) + val.charAt(3); if (val.length() == 6) val = val + "ff"; if (val.length() != 8) throw new NumberFormatException("Invalid color format!"); val = val.substring(6, 8) + val.substring(0, 6); // move alpha to front diff --git a/src/main/java/de/bluecolored/bluemap/api/math/Line.java b/src/main/java/de/bluecolored/bluemap/api/math/Line.java index 150b097..34f7993 100644 --- a/src/main/java/de/bluecolored/bluemap/api/math/Line.java +++ b/src/main/java/de/bluecolored/bluemap/api/math/Line.java @@ -28,7 +28,10 @@ import de.bluecolored.bluemap.api.debug.DebugDump; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.List; /** * A line consisting of 2 or more {@link Vector3d}-points. @@ -46,6 +49,10 @@ public Line(Vector3d... points) { this.points = points; } + public Line(Collection points) { + this(points.toArray(Vector3d[]::new)); + } + /** * Getter for the amount of points in this line. * @return the amount of points @@ -54,6 +61,11 @@ public int getPointCount() { return points.length; } + /** + * Getter for the point at the given index. + * @param i the index + * @return the point at the given index + */ public Vector3d getPoint(int i) { return points[i]; } @@ -112,4 +124,53 @@ public int hashCode() { return Arrays.hashCode(points); } + /** + * Creates a builder to build {@link Line}s. + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + List points; + + public Builder() { + this.points = new ArrayList<>(); + } + + /** + * Adds a point to the end of line. + * @param point the point to be added. + * @return this builder for chaining + */ + public Builder addPoint(Vector3d point) { + this.points.add(point); + return this; + } + + /** + * Adds multiple points to the end of line. + * @param points the points to be added. + * @return this builder for chaining + */ + public Builder addPoints(Vector3d... points) { + this.points.addAll(Arrays.asList(points)); + return this; + } + + /** + * Builds a new {@link Line} with the points set in this builder.
+ * There need to be at least 2 points to build a {@link Line}. + * @return the new {@link Line} + * @throws IllegalStateException if there are less than 2 points added to this builder + */ + public Line build() { + if (points.size() < 2) throw new IllegalStateException("A line has to have at least 2 points!"); + return new Line(points); + } + + } + } diff --git a/src/main/java/de/bluecolored/bluemap/api/math/Shape.java b/src/main/java/de/bluecolored/bluemap/api/math/Shape.java index 2dfaf26..ce2f373 100644 --- a/src/main/java/de/bluecolored/bluemap/api/math/Shape.java +++ b/src/main/java/de/bluecolored/bluemap/api/math/Shape.java @@ -28,7 +28,10 @@ import de.bluecolored.bluemap.api.debug.DebugDump; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.List; /** * A shape consisting of 3 or more {@link Vector2d}-points on a plane. @@ -43,10 +46,13 @@ public class Shape { public Shape(Vector2d... points) { if (points.length < 3) throw new IllegalArgumentException("A shape has to have at least 3 points!"); - this.points = points; } + public Shape(Collection points) { + this(points.toArray(Vector2d[]::new)); + } + /** * Getter for the amount of points in this shape. * @return the amount of points @@ -55,6 +61,11 @@ public int getPointCount() { return points.length; } + /** + * Getter for the point at the given index. + * @param i the index + * @return the point at the given index + */ public Vector2d getPoint(int i) { return points[i]; } @@ -201,4 +212,53 @@ public static Shape createCircle(double centerX, double centerY, double radius, return createCircle(new Vector2d(centerX, centerY), radius, points); } + /** + * Creates a builder to build {@link Shape}s. + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + List points; + + public Builder() { + this.points = new ArrayList<>(); + } + + /** + * Adds a point to the end of line. + * @param point the point to be added. + * @return this builder for chaining + */ + public Builder addPoint(Vector2d point) { + this.points.add(point); + return this; + } + + /** + * Adds multiple points to the end of line. + * @param points the points to be added. + * @return this builder for chaining + */ + public Builder addPoints(Vector2d... points) { + this.points.addAll(Arrays.asList(points)); + return this; + } + + /** + * Builds a new {@link Shape} with the points set in this builder.
+ * There need to be at least 3 points to build a {@link Shape}. + * @return the new {@link Shape} + * @throws IllegalStateException if there are less than 3 points added to this builder + */ + public Shape build() { + if (points.size() < 3) throw new IllegalStateException("A shape has to have at least 3 points!"); + return new Shape(points); + } + + } + }