Add STriangle

This commit is contained in:
2026-03-19 19:38:40 +01:00
parent b8ecf9859b
commit f424735a7b
9 changed files with 193 additions and 22 deletions

8
out.html Normal file
View File

@@ -0,0 +1,8 @@
<!DOCTYPE html><html lang="fr"><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" href="style.css" /> <title>Reactive shapes HTML</title></head><body>
<div class="triangle110717522"></div>
<div id="rec209793789"></div>
<div id="rec365617083"></div>
<div id="rec1121327988"></div>
<div id="rec256914054"></div>
<div class="circle172224331"></div>
</body>

10
out.svg Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600">
<polygon points="200,250 225,200 250,250" style="fill:#ffff00;stroke:#000000;stroke-width:1" />
<rect width="40" height="60" x="10" y="10" stroke="#ff0000" stroke-width="1" fill="none" />
<rect width="40" height="60" x="70" y="10" stroke="#000000" stroke-width="1" fill="none" />
<rect width="40" height="60" x="100" y="200" stroke="#ff00ff" stroke-width="1" fill="none" />
<rect width="40" height="60" x="150" y="200" stroke="#ff00ff" stroke-width="1" fill="none" />
<circle cx="230" cy="280" r="30" stroke="#000000" stroke-width="1" fill="none" />
</svg>

After

Width:  |  Height:  |  Size: 682 B

View File

@@ -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(

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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)){

View File

@@ -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("<div class=\"circle"+circle.hashCode()+"\"></div>");
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("<div class=\"circle%d\"></div>\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("<div class=\"triangle%d\"></div>\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) {

View File

@@ -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("<polygon points=\"%s\" style=\"%s\" />\n", points, style);
}
public void generateSVG(SCollection model) {
output.println(String.format(SVG_PRELUDE, App.WIN_SIZE.width, App.WIN_SIZE.height));
visitCollection(model);

54
style.css Normal file
View File

@@ -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;
}