Add builder-pattern to all Markers, MarkerSet, Line and Shape

This commit is contained in:
Lukas Rieger (Blue) 2022-08-05 19:50:57 +02:00
parent 8265cdfe05
commit 97c7f7a916
No known key found for this signature in database
GPG Key ID: 2D09EC5ED2687FF2
12 changed files with 890 additions and 33 deletions

View File

@ -101,4 +101,42 @@ public int hashCode() {
return result; return result;
} }
public static abstract class Builder<T extends DistanceRangedMarker, B extends DistanceRangedMarker.Builder<T, B>>
extends Marker.Builder<T, B> {
Double minDistance, maxDistance;
/**
* Sets the minimum distance of the camera to the position of the {@link Marker} for it to be displayed.<br>
* 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.<br>
* 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);
}
}
} }

View File

@ -64,12 +64,17 @@ private ExtrudeMarker() {
* @see #setShape(Shape, float, float) * @see #setShape(Shape, float, float)
*/ */
public ExtrudeMarker(String label, Shape shape, float shapeMinY, float shapeMaxY) { 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}. * Creates a new {@link ExtrudeMarker}.
* <p><i>(Since the shape has its own positions, the position is only used to determine e.g. the distance to the camera)</i></p> * <p><i>(Since the shape has its own positions, the position is only used to determine
* e.g. the distance to the camera)</i></p>
* *
* @param label the label of the marker * @param label the label of the marker
* @param position the coordinates 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}. * Getter for {@link Shape} of this {@link ExtrudeMarker}.
* <p>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.</p> * <p>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.</p>
* @return the {@link Shape} * @return the {@link Shape}
*/ */
public Shape getShape() { public Shape getShape() {
@ -117,7 +123,8 @@ public float getShapeMaxY() {
/** /**
* Sets the {@link Shape} of this {@link ExtrudeMarker}. * Sets the {@link Shape} of this {@link ExtrudeMarker}.
* <p>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.</p> * <p>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.</p>
* <i>(The shape will be extruded from minY to maxY on the map)</i> * <i>(The shape will be extruded from minY to maxY on the map)</i>
* @param shape the new {@link Shape} * @param shape the new {@link Shape}
* @param minY the new min-height (y-coordinate) of the shape on the map * @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 <code>true</code> if the depthTest is enabled * @return <code>true</code> if the depthTest is enabled
*/ */
public boolean isDepthTestEnabled() { 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} * @param enabled if the depth-test should be enabled for this {@link ExtrudeMarker}
*/ */
public void setDepthTestEnabled(boolean enabled) { 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()); 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<ExtrudeMarker, Builder> {
Shape shape;
float shapeMinY, shapeMaxY;
Boolean depthTest;
Integer lineWidth;
Color lineColor;
Color fillColor;
/**
* Sets the {@link Shape} of the {@link ExtrudeMarker}.
* <p>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.</p>
* <i>(The shape will be extruded from minY to maxY on the map)</i>
* @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.<br>
* The minimum required settings to build this marker are:
* <ul>
* <li>{@link #label(String)}</li>
* <li>{@link #shape(Shape, float, float)}</li>
* </ul>
* @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);
}
}
} }

View File

@ -120,7 +120,8 @@ public String getHtml() {
* *
* <p> * <p>
* <b>Important:</b><br> * <b>Important:</b><br>
* Make sure you escape all html-tags from possible user inputs to prevent possible <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client! * Make sure you escape all html-tags from possible user inputs to prevent possible
* <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client!
* </p> * </p>
* *
* @param html the html that will be inserted as the marker. * @param html the html that will be inserted as the marker.
@ -149,4 +150,77 @@ public int hashCode() {
return result; 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<HtmlMarker, 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}.
*
* <p>
* <b>Important:</b><br>
* Make sure you escape all html-tags from possible user inputs to prevent possible <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client!
* </p>
*
* @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.<br>
* The minimum required settings to build this marker are:
* <ul>
* <li>{@link #setLabel(String)}</li>
* <li>{@link #setPosition(Vector3d)}</li>
* <li>{@link #setHtml(String)}</li>
* </ul>
* @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);
}
}
} }

View File

@ -59,12 +59,13 @@ private LineMarker() {
* @see #setLine(Line) * @see #setLine(Line)
*/ */
public LineMarker(String label, Line 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}. * Creates a new {@link LineMarker}.
* <p><i>(Since the line has its own positions, the position is only used to determine e.g. the distance to the camera)</i></p> * <p><i>(Since the line has its own positions, the position is only used to determine
* e.g. the distance to the camera)</i></p>
* *
* @param label the label of the marker * @param label the label of the marker
* @param position the coordinates 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 <code>true</code> if the depthTest is enabled * @return <code>true</code> if the depthTest is enabled
*/ */
public boolean isDepthTestEnabled() { 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} * @param enabled if the depth-test should be enabled for this {@link LineMarker}
*/ */
public void setDepthTestEnabled(boolean enabled) { public void setDepthTestEnabled(boolean enabled) {
@ -181,4 +184,90 @@ private static Vector3d calculateLineCenter(Line line) {
return line.getMin().add(line.getMax()).mul(0.5); 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<LineMarker, 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.<br>
* The minimum required settings to build this marker are:
* <ul>
* <li>{@link #label(String)}</li>
* <li>{@link #line(Line)}</li>
* </ul>
* @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);
}
}
} }

View File

@ -75,7 +75,7 @@ public String getLabel() {
*/ */
public void setLabel(String label) { public void setLabel(String label) {
//escape html-tags //escape html-tags
this.label = label this.label = Objects.requireNonNull(label, "label cannot be null")
.replace("&", "&amp;") .replace("&", "&amp;")
.replace("<", "&lt;") .replace("<", "&lt;")
.replace(">", "&gt;"); .replace(">", "&gt;");
@ -94,7 +94,7 @@ public Vector3d getPosition() {
* @param position the new position * @param position the new position
*/ */
public void setPosition(Vector3d 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; return result;
} }
public static abstract class Builder<T extends Marker, B extends Marker.Builder<T, B>> {
String label;
Vector3d position;
/**
* Sets the label of the {@link Marker}.
* <p><i>(HTML-Tags will be escaped.)</i></p>
* @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> O checkNotNull(O object, String name) {
if (object == null) throw new IllegalStateException(name + " has to be set and cannot be null");
return object;
}
}
} }

View File

@ -82,15 +82,20 @@ public MarkerSet(String label, boolean toggleable, boolean defaultHidden) {
/** /**
* Getter for the label of this {@link MarkerSet}. * Getter for the label of this {@link MarkerSet}.
* <p>The label is used in the web-app to name the toggle-button of this {@link MarkerSet} if it is toggleable. ({@link #isToggleable()})</p> * <p>The label is used in the web-app to name the toggle-button of this {@link MarkerSet} if it is toggleable.
* ({@link #isToggleable()})</p>
*
* @return the label of this {@link MarkerSet} * @return the label of this {@link MarkerSet}
*/ */
public String getLabel() { public String getLabel() {
return label; return label;
} }
/** /**
* Sets the label of this {@link MarkerSet}. * Sets the label of this {@link MarkerSet}.
* <p>The label is used in the web-app to name the toggle-button of this {@link MarkerSet} if it is toggleable. ({@link #isToggleable()})</p> * <p>The label is used in the web-app to name the toggle-button of this {@link MarkerSet} if it is toggleable.
* ({@link #isToggleable()})</p>
*
* @param label the new label * @param label the new label
*/ */
public void setLabel(String label) { public void setLabel(String label) {
@ -99,7 +104,9 @@ public void setLabel(String label) {
/** /**
* Checks if the {@link MarkerSet} is toggleable. * Checks if the {@link MarkerSet} is toggleable.
* <p>If this is <code>true</code>, 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.</p> * <p>If this is <code>true</code>, 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.</p>
*
* @return whether this {@link MarkerSet} is toggleable * @return whether this {@link MarkerSet} is toggleable
*/ */
public boolean isToggleable() { public boolean isToggleable() {
@ -108,7 +115,9 @@ public boolean isToggleable() {
/** /**
* Changes if this {@link MarkerSet} is toggleable. * Changes if this {@link MarkerSet} is toggleable.
* <p>If this is <code>true</code>, 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.</p> * <p>If this is <code>true</code>, 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.</p>
*
* @param toggleable whether this {@link MarkerSet} should be toggleable * @param toggleable whether this {@link MarkerSet} should be toggleable
*/ */
public void setToggleable(boolean toggleable) { public void setToggleable(boolean toggleable) {
@ -117,7 +126,9 @@ public void setToggleable(boolean toggleable) {
/** /**
* Checks if this {@link MarkerSet} is hidden by default. * Checks if this {@link MarkerSet} is hidden by default.
* <p>This is basically the default-state of the toggle-button from {@link #isToggleable()}. If this is <code>true</code> the markers of this marker set will initially be hidden and can be displayed using the toggle-button.</p> * <p>This is basically the default-state of the toggle-button from {@link #isToggleable()}.
* If this is <code>true</code> the markers of this marker set will initially be hidden and can be displayed
* using the toggle-button.</p>
* *
* @return whether this {@link MarkerSet} is hidden by default * @return whether this {@link MarkerSet} is hidden by default
* @see #isToggleable() * @see #isToggleable()
@ -128,13 +139,14 @@ public boolean isDefaultHidden() {
/** /**
* Sets if this {@link MarkerSet} is hidden by default. * Sets if this {@link MarkerSet} is hidden by default.
* <p>This is basically the default-state of the toggle-button from {@link #isToggleable()}. If this is <code>true</code> the markers of this marker set will initially be hidden and can be displayed using the toggle-button.</p> * <p>This is basically the default-state of the toggle-button from {@link #isToggleable()}. If this is
* <code>true</code> the markers of this marker set will initially be hidden and can be displayed using the toggle-button.</p>
* *
* @param defaultHide whether this {@link MarkerSet} should be hidden by default * @param defaultHidden whether this {@link MarkerSet} should be hidden by default
* @see #isToggleable() * @see #isToggleable()
*/ */
public void setDefaultHidden(boolean defaultHide) { public void setDefaultHidden(boolean defaultHidden) {
this.defaultHidden = defaultHide; this.defaultHidden = defaultHidden;
} }
/** /**
@ -169,4 +181,83 @@ public int hashCode() {
return result; 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}.
* <p>The label is used in the web-app to name the toggle-button of the {@link MarkerSet} if it is toggleable.
* ({@link #toggleable(Boolean)})</p>
*
* @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.
* <p>If this is <code>true</code>, 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.</p>
*
* @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.
* <p>This is basically the default-state of the toggle-button from {@link #toggleable(Boolean)}.
* If this is <code>true</code> the markers of this marker set will initially be hidden and can be displayed
* using the toggle-button.</p>
*
* @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.<br>
* The minimum required settings to build this marker-set are:
* <ul>
* <li>{@link #setLabel(String)}</li>
* </ul>
* @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> O checkNotNull(O object, String name) {
if (object == null) throw new IllegalStateException(name + " has to be set and cannot be null");
return object;
}
}
} }

View File

@ -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. * 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() { public String getDetail() {
return detail; return detail;
} }
/** /**
* Sets the detail of this {@link ObjectMarker}. The detail can include html-tags.<br> * Sets the detail of this {@link Marker}. The detail can include html-tags.<br>
* This is the text that will be displayed on the popup when you click on this marker. * This is the text that will be displayed on the popup when you click on this marker.
* <p> * <p>
* <b>Important:</b><br> * <b>Important:</b><br>
* Html-tags in the label will not be escaped, so you can use them to style the {@link ObjectMarker}-detail.<br> * Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.<br>
* Make sure you escape all html-tags from possible user inputs to prevent possible <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client! * Make sure you escape all html-tags from possible user inputs to prevent possible
* <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client!
* </p> * </p>
* *
* @param detail the new detail for this {@link ObjectMarker} * @param detail the new detail for this {@link ObjectMarker}
@ -129,4 +130,61 @@ public int hashCode() {
return result; return result;
} }
public static abstract class Builder<T extends ObjectMarker, B extends ObjectMarker.Builder<T, B>>
extends DistanceRangedMarker.Builder<T, B> {
String detail;
String link;
boolean newTab;
/**
* Sets the detail of the {@link Marker}. The detail can include html-tags.<br>
* This is the text that will be displayed on the popup when you click on the marker.
* <p>
* <b>Important:</b><br>
* Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.<br>
* Make sure you escape all html-tags from possible user inputs to prevent possible
* <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client!
* </p>
*
* @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}.<br>
* 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 <code>null</code> 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);
}
}
} }

View File

@ -134,4 +134,72 @@ public int hashCode() {
return result; 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<POIMarker, 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.<br>
* The minimum required settings to build this marker are:
* <ul>
* <li>{@link #setLabel(String)}</li>
* <li>{@link #setPosition(Vector3d)}</li>
* </ul>
* @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);
}
}
} }

View File

@ -69,7 +69,8 @@ public ShapeMarker(String label, Shape shape, float shapeY) {
/** /**
* Creates a new {@link ShapeMarker}. * Creates a new {@link ShapeMarker}.
* <p><i>(Since the shape has its own positions, the position is only used to determine e.g. the distance to the camera)</i></p> * <p><i>(Since the shape has its own positions, the position is only used to determine
* e.g. the distance to the camera)</i></p>
* *
* @param label the label of the marker * @param label the label of the marker
* @param position the coordinates 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}. * Getter for {@link Shape} of this {@link ShapeMarker}.
* <p>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.</p> * <p>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.</p>
* @return the {@link Shape} * @return the {@link Shape}
*/ */
public Shape getShape() { public Shape getShape() {
@ -105,7 +107,8 @@ public float getShapeY() {
/** /**
* Sets the {@link Shape} of this {@link ShapeMarker}. * Sets the {@link Shape} of this {@link ShapeMarker}.
* <p>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.</p> * <p>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.</p>
* @param shape the new {@link Shape} * @param shape the new {@link Shape}
* @param y the new height (y-coordinate) of the shape on the map * @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 <code>true</code> if the depthTest is enabled * @return <code>true</code> if the depthTest is enabled
*/ */
public boolean isDepthTestEnabled() { 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} * @param enabled if the depth-test should be enabled for this {@link ShapeMarker}
*/ */
public void setDepthTestEnabled(boolean enabled) { 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()); 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<ShapeMarker, Builder> {
Shape shape;
float shapeY;
Boolean depthTest;
Integer lineWidth;
Color lineColor;
Color fillColor;
/**
* Sets the {@link Shape} of the {@link ShapeMarker}.
* <p>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.</p>
* @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.<br>
* The minimum required settings to build this marker are:
* <ul>
* <li>{@link #label(String)}</li>
* <li>{@link #shape(Shape, float)}</li>
* </ul>
* @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);
}
}
} }

View File

@ -68,6 +68,15 @@ public Color(int i) {
this((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF, ((i >> 24) & 0xFF) / 255f); 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 * The value can be an integer in String-Format (see {@link #Color(int)}) or a string in hexadecimal format
* prefixed with # <i>(css-style: e.g. <code>#f16</code> becomes <code>#ff1166</code>)</i>. * prefixed with # <i>(css-style: e.g. <code>#f16</code> becomes <code>#ff1166</code>)</i>.
@ -78,17 +87,34 @@ public Color(String cssColorString) {
this(parseColorString(Objects.requireNonNull(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() { public int getRed() {
return r; return r;
} }
/**
* Getter for the green-component of the color.
* @return the green-component of the color in range 0-255
*/
public int getGreen() { public int getGreen() {
return g; return g;
} }
/**
* Getter for the blue-component of the color.
* @return the blue-component of the color in range 0-255
*/
public int getBlue() { public int getBlue() {
return b; return b;
} }
/**
* Getter for the alpha-component of the color.
* @return the alpha-component of the color in range 0-1
*/
public float getAlpha() { public float getAlpha() {
return a; return a;
} }
@ -97,7 +123,9 @@ private static int parseColorString(String val) {
if (val.charAt(0) == '#') { if (val.charAt(0) == '#') {
val = val.substring(1); val = val.substring(1);
if (val.length() == 3) val = val + "f"; 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() == 6) val = val + "ff";
if (val.length() != 8) throw new NumberFormatException("Invalid color format!"); if (val.length() != 8) throw new NumberFormatException("Invalid color format!");
val = val.substring(6, 8) + val.substring(0, 6); // move alpha to front val = val.substring(6, 8) + val.substring(0, 6); // move alpha to front

View File

@ -28,7 +28,10 @@
import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.api.debug.DebugDump;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/** /**
* A line consisting of 2 or more {@link Vector3d}-points. * A line consisting of 2 or more {@link Vector3d}-points.
@ -46,6 +49,10 @@ public Line(Vector3d... points) {
this.points = points; this.points = points;
} }
public Line(Collection<Vector3d> points) {
this(points.toArray(Vector3d[]::new));
}
/** /**
* Getter for the amount of points in this line. * Getter for the amount of points in this line.
* @return the amount of points * @return the amount of points
@ -54,6 +61,11 @@ public int getPointCount() {
return points.length; 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) { public Vector3d getPoint(int i) {
return points[i]; return points[i];
} }
@ -112,4 +124,53 @@ public int hashCode() {
return Arrays.hashCode(points); 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<Vector3d> 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.<br>
* 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);
}
}
} }

View File

@ -28,7 +28,10 @@
import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.api.debug.DebugDump;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays; 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. * A shape consisting of 3 or more {@link Vector2d}-points on a plane.
@ -43,10 +46,13 @@ public class Shape {
public Shape(Vector2d... points) { public Shape(Vector2d... points) {
if (points.length < 3) throw new IllegalArgumentException("A shape has to have at least 3 points!"); if (points.length < 3) throw new IllegalArgumentException("A shape has to have at least 3 points!");
this.points = points; this.points = points;
} }
public Shape(Collection<Vector2d> points) {
this(points.toArray(Vector2d[]::new));
}
/** /**
* Getter for the amount of points in this shape. * Getter for the amount of points in this shape.
* @return the amount of points * @return the amount of points
@ -55,6 +61,11 @@ public int getPointCount() {
return points.length; 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) { public Vector2d getPoint(int i) {
return points[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); 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<Vector2d> 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.<br>
* 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);
}
}
} }