diff --git a/out.html b/out.html
new file mode 100644
index 0000000..154a417
--- /dev/null
+++ b/out.html
@@ -0,0 +1,8 @@
+
Reactive shapes HTML
+
+
+
+
+
+
+
diff --git a/out.svg b/out.svg
new file mode 100644
index 0000000..8c6cbd7
--- /dev/null
+++ b/out.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/src/main/java/ovh/gasser/newshapes/App.java b/src/main/java/ovh/gasser/newshapes/App.java
index e9d872b..f66788e 100644
--- a/src/main/java/ovh/gasser/newshapes/App.java
+++ b/src/main/java/ovh/gasser/newshapes/App.java
@@ -3,9 +3,7 @@ package ovh.gasser.newshapes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ovh.gasser.newshapes.attributes.ColorAttributes;
-import ovh.gasser.newshapes.shapes.SCircle;
-import ovh.gasser.newshapes.shapes.SCollection;
-import ovh.gasser.newshapes.shapes.SRectangle;
+import ovh.gasser.newshapes.shapes.*;
import ovh.gasser.newshapes.shapes.Shape;
import ovh.gasser.newshapes.ui.ShapesView;
import ovh.gasser.newshapes.ui.listeners.MenuAddListener;
@@ -51,6 +49,7 @@ public class App {
private void buildModel() {
model = SCollection.of(
+ STriangle.create(200, 200, 50, Color.YELLOW, Color.BLACK),
SRectangle.create(10, 10, 40, 60, Color.RED),
SRectangle.create(70, 10, 40, 60),
SCollection.of(
diff --git a/src/main/java/ovh/gasser/newshapes/ShapeVisitor.java b/src/main/java/ovh/gasser/newshapes/ShapeVisitor.java
index c5e3d5d..c2024b9 100644
--- a/src/main/java/ovh/gasser/newshapes/ShapeVisitor.java
+++ b/src/main/java/ovh/gasser/newshapes/ShapeVisitor.java
@@ -3,6 +3,7 @@ package ovh.gasser.newshapes;
import ovh.gasser.newshapes.shapes.SCircle;
import ovh.gasser.newshapes.shapes.SCollection;
import ovh.gasser.newshapes.shapes.SRectangle;
+import ovh.gasser.newshapes.shapes.STriangle;
public interface ShapeVisitor {
void visitRectangle(SRectangle sRectangle);
@@ -10,4 +11,6 @@ public interface ShapeVisitor {
void visitCollection(SCollection collection);
void visitCircle(SCircle sCircle);
+
+ void visitTriangle(STriangle sTriangle);
}
diff --git a/src/main/java/ovh/gasser/newshapes/shapes/STriangle.java b/src/main/java/ovh/gasser/newshapes/shapes/STriangle.java
new file mode 100644
index 0000000..fc2afd6
--- /dev/null
+++ b/src/main/java/ovh/gasser/newshapes/shapes/STriangle.java
@@ -0,0 +1,31 @@
+package ovh.gasser.newshapes.shapes;
+
+import ovh.gasser.newshapes.ShapeVisitor;
+import ovh.gasser.newshapes.attributes.ColorAttributes;
+import ovh.gasser.newshapes.attributes.SelectionAttributes;
+
+import java.awt.*;
+
+public class STriangle extends AbstractShape {
+ private STriangle(int x, int y, int size){
+ super(new Rectangle(x, y, size, size));
+ }
+
+ @Override
+ public void accept(ShapeVisitor visitor) {
+ visitor.visitTriangle(this);
+ }
+
+ @Override
+ public Shape clone() {
+ var color = (ColorAttributes) getAttributes(ColorAttributes.ID);
+ return STriangle.create(super.getBounds().x, super.getBounds().y, super.getBounds().height, color.strokedColor, color.filledColor);
+ }
+
+ public static STriangle create(int x, int y, int size, Color filledColor, Color strokedColor) {
+ var tri = new STriangle(x, y, size);
+ tri.addAttributes(new SelectionAttributes());
+ tri.addAttributes(new ColorAttributes(true, true, filledColor, strokedColor));
+ return tri;
+ }
+}
diff --git a/src/main/java/ovh/gasser/newshapes/ui/ShapeDraftman.java b/src/main/java/ovh/gasser/newshapes/ui/ShapeDraftman.java
index 7eb9af5..bd439b5 100644
--- a/src/main/java/ovh/gasser/newshapes/ui/ShapeDraftman.java
+++ b/src/main/java/ovh/gasser/newshapes/ui/ShapeDraftman.java
@@ -3,9 +3,7 @@ package ovh.gasser.newshapes.ui;
import ovh.gasser.newshapes.ShapeVisitor;
import ovh.gasser.newshapes.attributes.ColorAttributes;
import ovh.gasser.newshapes.attributes.SelectionAttributes;
-import ovh.gasser.newshapes.shapes.SCircle;
-import ovh.gasser.newshapes.shapes.SCollection;
-import ovh.gasser.newshapes.shapes.SRectangle;
+import ovh.gasser.newshapes.shapes.*;
import ovh.gasser.newshapes.shapes.Shape;
import java.awt.*;
@@ -64,6 +62,32 @@ public class ShapeDraftman implements ShapeVisitor {
drawHandlerIfSelected(circle);
}
+ @Override
+ public void visitTriangle(STriangle tri) {
+ ColorAttributes colAttrs = (ColorAttributes) tri.getAttributes(ColorAttributes.ID);
+ var bounds = tri.getBounds();
+ var size = Math.min(bounds.width, bounds.height); // Should be the same because we only support equilateral triangles
+ if (colAttrs == null){
+ colAttrs = DEFAULT_COLOR_ATTRIBUTES;
+ }
+ if (colAttrs.filled) {
+ this.g2d.setColor(colAttrs.filledColor);
+ this.g2d.fillPolygon(
+ new int[]{ bounds.x, bounds.x + (size/2), bounds.x + size },
+ new int[]{ bounds.y + size, bounds.y, bounds.y + size },
+ 3);
+ }
+ if (colAttrs.stroked) {
+ this.g2d.setColor(colAttrs.strokedColor);
+ this.g2d.drawPolygon(
+ new int[]{bounds.x, bounds.x + (size / 2), bounds.x + size},
+ new int[]{bounds.y + size, bounds.y, bounds.y + size},
+ 3);
+ }
+
+ drawHandlerIfSelected(tri);
+ }
+
private void drawHandlerIfSelected(Shape s) {
SelectionAttributes selAttrs = (SelectionAttributes) s.getAttributes(SelectionAttributes.ID);
if ((selAttrs != null) && (selAttrs.selected)){
diff --git a/src/main/java/ovh/gasser/newshapes/ui/visitors/HTMLDraftman.java b/src/main/java/ovh/gasser/newshapes/ui/visitors/HTMLDraftman.java
index 637da66..0f12728 100644
--- a/src/main/java/ovh/gasser/newshapes/ui/visitors/HTMLDraftman.java
+++ b/src/main/java/ovh/gasser/newshapes/ui/visitors/HTMLDraftman.java
@@ -2,13 +2,15 @@ package ovh.gasser.newshapes.ui.visitors;
import ovh.gasser.newshapes.ShapeVisitor;
import ovh.gasser.newshapes.attributes.ColorAttributes;
-import ovh.gasser.newshapes.shapes.SCircle;
-import ovh.gasser.newshapes.shapes.SCollection;
-import ovh.gasser.newshapes.shapes.SRectangle;
+import ovh.gasser.newshapes.shapes.*;
import ovh.gasser.newshapes.shapes.Shape;
import java.awt.*;
import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class HTMLDraftman implements ShapeVisitor {
@@ -50,19 +52,39 @@ public class HTMLDraftman implements ShapeVisitor {
@Override
public void visitCircle(SCircle circle) {
- htmlOutput.println("");
- cssOutput.println(".circle" + circle.hashCode() + "{ ");
- cssOutput.println("position: absolute;");
- cssOutput.println("top:" + circle.getBounds().y + "px;");
- cssOutput.println("left:" + circle.getBounds().x + "px;");
- cssOutput.println("width:" + circle.getRadius() + "px;");
- cssOutput.println("height:" + circle.getRadius() + "px;");
- cssOutput.println("border-radius:" + circle.getRadius() / 2 + "px;");
- cssOutput.println("-webkit-border-radius:" + circle.getRadius() / 2 + "px;");
- cssOutput.println("-o-border-radius:" + circle.getRadius() / 2 + "px;");
- cssOutput.println("-moz-border-radius:" + circle.getRadius() / 2 + "px;");
- cssOutput.println("position: absolute;");
- cssOutput.println(this.attributesToCss(circle) + " }");
+ htmlOutput.printf("\n", circle.hashCode());
+ cssOutput.println(" .circle" + circle.hashCode() + "{\n");
+ cssOutput.println(" position: absolute;");
+ cssOutput.println(" top:" + circle.getBounds().y + "px;");
+ cssOutput.println(" left:" + circle.getBounds().x + "px;");
+ cssOutput.println(" width:" + circle.getRadius() + "px;");
+ cssOutput.println(" height:" + circle.getRadius() + "px;");
+ cssOutput.println(" border-radius:" + circle.getRadius() / 2 + "px;");
+ cssOutput.println(" -webkit-border-radius:" + circle.getRadius() / 2 + "px;");
+ cssOutput.println(" -o-border-radius:" + circle.getRadius() / 2 + "px;");
+ cssOutput.println(" -moz-border-radius:" + circle.getRadius() / 2 + "px;");
+ cssOutput.println(" position: absolute;");
+ cssOutput.println(this.attributesToCss(circle) + "\n}\n");
+ }
+
+ @Override
+ public void visitTriangle(STriangle sTriangle) {
+ htmlOutput.printf("\n", this.hashCode());
+ var bounds = sTriangle.getBounds();
+ ColorAttributes colAttrs = (ColorAttributes) sTriangle.getAttributes(ColorAttributes.ID);
+ String colorString = formatCSSColor(colAttrs.filledColor);
+ StringJoiner joiner = new StringJoiner("\n", ".triangle%d{\n".formatted(this.hashCode()), "\n}\n");
+ joiner.add(" position: absolute;");
+ joiner.add(" top: %dpx;".formatted(bounds.y));
+ joiner.add(" left: %dpx;".formatted(bounds.x));
+ joiner.add(" width: 0px;");
+ joiner.add(" height: 0px;");
+ joiner.add(" border: 0 solid transparent;");
+ joiner.add(" border-left-width: %spx;".formatted(Math.ceil(bounds.width / 1.72)));
+ joiner.add(" border-right-width: %spx;".formatted(Math.ceil(bounds.width / 1.72)));
+ joiner.add(" border-bottom: %dpx solid %s;".formatted(bounds.width, colorString));
+ String strBuilder = joiner.toString();
+ cssOutput.write(strBuilder);
}
private String attributesToCss(Shape shape) {
diff --git a/src/main/java/ovh/gasser/newshapes/ui/visitors/SVGDraftman.java b/src/main/java/ovh/gasser/newshapes/ui/visitors/SVGDraftman.java
index 202ce81..c1e1f7c 100644
--- a/src/main/java/ovh/gasser/newshapes/ui/visitors/SVGDraftman.java
+++ b/src/main/java/ovh/gasser/newshapes/ui/visitors/SVGDraftman.java
@@ -6,9 +6,11 @@ import ovh.gasser.newshapes.attributes.ColorAttributes;
import ovh.gasser.newshapes.shapes.SCircle;
import ovh.gasser.newshapes.shapes.SCollection;
import ovh.gasser.newshapes.shapes.SRectangle;
+import ovh.gasser.newshapes.shapes.STriangle;
import java.awt.*;
import java.io.PrintWriter;
+import java.util.StringJoiner;
public class SVGDraftman implements ShapeVisitor {
private static final String SVG_PRELUDE = """
@@ -51,6 +53,24 @@ public class SVGDraftman implements ShapeVisitor {
x, y, r, buildColorParameters(attrs)));
}
+ @Override
+ public void visitTriangle(STriangle sTriangle) {
+ int x = sTriangle.getBounds().x;
+ int y = sTriangle.getBounds().y;
+ int size = sTriangle.getBounds().width;
+ var points = new StringJoiner(" ", "", "");
+ points.add("%d,%d".formatted(x, y + size));
+ points.add("%d,%d".formatted(x + size / 2, y));
+ points.add("%d,%d".formatted(x + size, y + size));
+ var attrs = (ColorAttributes) sTriangle.getAttributes(ColorAttributes.ID);
+ var style = new StringJoiner(";", "fill:", "");
+ style.add(attrs.filled ? colorToHex(attrs.filledColor) : "none");
+ if (attrs.stroked) {
+ style.add("stroke:%s;stroke-width:1".formatted(colorToHex(attrs.strokedColor)));
+ }
+ this.output.printf("\n", points, style);
+ }
+
public void generateSVG(SCollection model) {
output.println(String.format(SVG_PRELUDE, App.WIN_SIZE.width, App.WIN_SIZE.height));
visitCollection(model);
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..f4fd3e3
--- /dev/null
+++ b/style.css
@@ -0,0 +1,54 @@
+.triangle110717522{
+ position: absolute;
+ top: 162px;
+ left: 367px;
+ width: 0px;
+ height: 0px;
+ border: 0 solid transparent;
+ border-left-width: 30.0px;
+ border-right-width: 30.0px;
+ border-bottom: 50px solid #ffff00;
+}
+#rec209793789{
+position:absolute;
+top:10px;
+left:10px;
+width:40px;
+height:60px;
+background:#ffffff;border:1px solid #ff0000; }
+#rec365617083{
+position:absolute;
+top:10px;
+left:70px;
+width:40px;
+height:60px;
+background:#ffffff;border:1px solid #000000; }
+#rec1121327988{
+position:absolute;
+top:200px;
+left:100px;
+width:40px;
+height:60px;
+background:#ffffff;border:1px solid #ff00ff; }
+#rec256914054{
+position:absolute;
+top:200px;
+left:150px;
+width:40px;
+height:60px;
+background:#ffffff;border:1px solid #ff00ff; }
+ .circle172224331{
+
+ position: absolute;
+ top:250px;
+ left:200px;
+ width:30px;
+ height:30px;
+ border-radius:15px;
+ -webkit-border-radius:15px;
+ -o-border-radius:15px;
+ -moz-border-radius:15px;
+ position: absolute;
+background:#ffffff;border:1px solid #000000;
+}
+