From f424735a7b0600134b55f2fc30deda84c2a5c330 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Thu, 19 Mar 2026 19:38:40 +0100 Subject: [PATCH] Add STriangle --- out.html | 8 +++ out.svg | 10 ++++ src/main/java/ovh/gasser/newshapes/App.java | 5 +- .../ovh/gasser/newshapes/ShapeVisitor.java | 3 ++ .../gasser/newshapes/shapes/STriangle.java | 31 +++++++++++ .../gasser/newshapes/ui/ShapeDraftman.java | 30 +++++++++-- .../newshapes/ui/visitors/HTMLDraftman.java | 54 +++++++++++++------ .../newshapes/ui/visitors/SVGDraftman.java | 20 +++++++ style.css | 54 +++++++++++++++++++ 9 files changed, 193 insertions(+), 22 deletions(-) create mode 100644 out.html create mode 100644 out.svg create mode 100644 src/main/java/ovh/gasser/newshapes/shapes/STriangle.java create mode 100644 style.css 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; +} +