Skip to content

Instantly share code, notes, and snippets.

@agnes1
Created September 21, 2015 22:28
Show Gist options
  • Save agnes1/ee346d353bb10b5e01c1 to your computer and use it in GitHub Desktop.
Save agnes1/ee346d353bb10b5e01c1 to your computer and use it in GitHub Desktop.
Draw assemblies of polygons. Supports nudge shape, nudge color, copy-paste, randomize verteces, randomize color, undo, redo, rotate, shrink, grow, and copy-paste color.
package com.clarke.agnes.polyperfect;
import org.treeml.Parser;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
/**
* @author agnes.clarke
* @since 8/13/2015
*/
public class Model {
private List<ModelStep> undo = new ArrayList<>();
private List<ModelStep> redo = new ArrayList<>();
private ModelStep currentStep = new ModelStep();
public void addPoint(ModelStep.Point p) {
createStep();
currentStep.currentPoly.points.add(p);
}
public void newPoly() {
if (currentStep.currentPoly.points.size() < 3) {
return;
}
createStep();
currentStep.polies.add(currentStep.currentPoly);
currentStep.currentPoly = new ModelStep.Poly();
}
public void setColor(double r, double g, double b, double a) {
if (currentStep.polies.isEmpty()) {
return;
}
if (currentStep.selectedCompletePoly == null) {
return;
}
createStep();
currentStep.selectedCompletePoly.r = r;
currentStep.selectedCompletePoly.g = g;
currentStep.selectedCompletePoly.b = b;
currentStep.selectedCompletePoly.a = a;
}
public void copyPoly() {
if (currentStep.polies.isEmpty()) {
return;
}
if (currentStep.selectedCompletePoly == null) {
return;
}
createStep();
ModelStep.Poly poly = currentStep.selectedCompletePoly.deepCopy();
poly.nudge(10, 10);
currentStep.polies.add(poly);
currentStep.selectedCompletePoly = poly;
}
public void rotate() {
if (currentStep.polies.isEmpty()) {
return;
}
if (currentStep.selectedCompletePoly == null) {
return;
}
createStep();
currentStep.selectedCompletePoly.rotate(-20);
}
public void counterRotate() {
if (currentStep.polies.isEmpty()) {
return;
}
if (currentStep.selectedCompletePoly == null) {
return;
}
createStep();
currentStep.selectedCompletePoly.rotate(20);
}
public void larger() {
if (currentStep.polies.isEmpty()) {
return;
}
if (currentStep.selectedCompletePoly == null) {
return;
}
createStep();
currentStep.selectedCompletePoly.resize(1.02);
}
public void smaller() {
if (currentStep.polies.isEmpty()) {
return;
}
if (currentStep.selectedCompletePoly == null) {
return;
}
createStep();
currentStep.selectedCompletePoly.resize(0.98);
}
public void nudge(double x, double y) {
if (currentStep.polies.isEmpty()) {
return;
}
if (currentStep.selectedCompletePoly == null) {
return;
}
createStep();
currentStep.selectedCompletePoly.nudge(x, y);
}
public void nudgeColor(double r, double g, double b, double a) {
if (currentStep.polies.isEmpty()) {
return;
}
if (currentStep.selectedCompletePoly == null) {
return;
}
createStep();
currentStep.selectedCompletePoly.nudgeColor(r, g, b, a);
}
public void nudgePoints(Random random, boolean big) {
if (currentStep.polies.isEmpty()) {
return;
}
if (currentStep.selectedCompletePoly == null) {
return;
}
createStep();
currentStep.selectedCompletePoly.nudgePoints(random, big);
}
private void createStep() {
undo.add(currentStep);
currentStep = currentStep.deepCopy();
}
public void setScale(double td) {
createStep();
currentStep.scale = td;
}
public double getScale() {
return currentStep.scale;
}
public void undo() {
if (undo.size() == 0) {
return;
}
redo.add(currentStep);
currentStep = undo.remove(undo.size() - 1);
}
public void redo() {
if (redo.size() == 0) {
return;
}
undo.add(currentStep);
currentStep = redo.remove(redo.size() - 1);
}
public List<ModelStep.Poly> getPolies() {
return Collections.unmodifiableList(currentStep.polies);
}
public ModelStep.Poly getCurrentPoly() {
return currentStep.currentPoly;
}
public ModelStep.Poly getSelectedCompletePoly() {
return currentStep.selectedCompletePoly;
}
public int getCurrentPolySize() {
return currentStep.currentPoly.points.size();
}
public void nextPoly() {
//if (currentStep.polies.isEmpty() || (currentStep.polies.size() == 1 && currentStep.selectedCompletePoly != null)) {return;}
//createStep();
currentStep.nextPoly();
}
public Parser.Node toNodeTree() {
return currentStep.toNodeTree();
}
public void load(Reader reader) {
undo.clear();
redo.clear();
currentStep = new ModelStep(reader);
}
}
package com.clarke.agnes.polyperfect;
import javafx.geometry.Point2D;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform;
import org.treeml.Parser;
import java.io.IOException;
import java.io.Reader;
import java.util.*;
/**
* @author agnes.clarke
* @since 8/4/2015
*/
public class ModelStep {
private static final double center = 320;
double scale = 10.0;
List<Poly> polies = new ArrayList<>();
List<String> animationNames = new ArrayList<>();
Poly selectedCompletePoly = null;
Poly currentPoly = new Poly();
public static final Random RANDOM = new Random();
public ModelStep() {}
public ModelStep(List<Poly> polies, List<String> animationNames, Poly currentPoly, Poly selectedCompletePoly) {
int i = polies.indexOf(selectedCompletePoly);
for (Poly poly : polies) {
this.polies.add(poly.deepCopy());
}
this.selectedCompletePoly = i == -1 ? null : this.polies.get(i);
this.animationNames.addAll(animationNames);
this.currentPoly = currentPoly.deepCopy();
}
public ModelStep(Reader reader) {
try {
Parser.Node tree = new Parser().parse(reader);
Parser.Node node = tree.getNode("poly");
scale = tree.getValueAt("scale", 10.0);
currentPoly = new Poly(node, scale);
Parser.Node polygons = tree.getNode("polygons");
for (Parser.Node p : polygons.children) {
polies.add(new Poly(p, scale));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
ModelStep deepCopy() {
return new ModelStep(polies, animationNames, currentPoly, selectedCompletePoly);
}
public void nextPoly() {
boolean choice = false;
for (Poly poly : polies) {
if (choice) {
selectedCompletePoly = poly;
return;
}
choice = poly.equals(selectedCompletePoly);
}
if ((selectedCompletePoly == null && !polies.isEmpty()) || choice) {
selectedCompletePoly = polies.get(0);
}
}
public Parser.Node toNodeTree() {
Parser.RootNode root = new Parser.RootNode();
Parser.Node currentPolygon = currentPoly.toNodeTree(0, scale);
root.children.add(new Parser.Node("scale", scale));
root.children.add(currentPolygon);
Parser.Node polygons = new Parser.Node("polygons", polies.size());
root.children.add(polygons);
int counter = 1;
for (Poly poly : polies) {
Parser.Node node = poly.toNodeTree(counter++, scale);
polygons.children.add(node);
}
return root;
}
public static class Point{
public double x, y;
public Point(double x, double y) {this.x = x; this.y = y;}
public Point deepCopy() {return new Point(x,y);}
}
public static class Poly {
public String name;
public List<Point> points = new ArrayList<>();
public List<Bone> bones = new ArrayList<>();
public Map<String, Integer> animationSequence = new TreeMap<>();
public double r = RANDOM.nextDouble();
public double g = RANDOM.nextDouble();
public double b = RANDOM.nextDouble();
public double a = 0.1 + RANDOM.nextDouble() / 3.0;
public double z = 0;
public Poly(){}
public Poly(String name, List<Point> points, List<Bone> bones, Map<String, Integer> animationSequence, double r, double g, double b, double a, double z) {
this.name = name;
for (Point point : points){
this.points.add(point.deepCopy());
}
for (Bone bone : bones) {
this.bones.add(bone.deepCopy());
}
this.animationSequence = new TreeMap<>(animationSequence);
this.r = r;
this.g = g;
this.b = b;
this.a = a;
this.z = z;
}
public Poly(Parser.Node node, double scale) {
r = node.getValueAt("r");
g = node.getValueAt("g");
b = node.getValueAt("b");
a = node.getValueAt("a");
z = node.getValueAt("z");
List<Double> xPoints = node.getValueAt("xPoints");
List<Double> yPoints = node.getValueAt("yPoints");
if (xPoints == null || yPoints == null) {
return;
}
for (int i = 0; i < xPoints.size(); i++) {
try {
points.add(new Point(toPx(xPoints.get(i), scale), toPx(yPoints.get(i), scale)));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public Poly deepCopy() {
return new Poly(name, points, bones, animationSequence, r, g, b, a, z);
}
public Point getCenterPoint() {
double minX = Double.MAX_VALUE, maxX = Double.MIN_VALUE, minY = Double.MAX_VALUE, maxY = Double.MIN_VALUE;
for (Point p : points) {
minX = Math.min(minX, p.x);
maxX = Math.max(maxX, p.x);
minY = Math.min(minY, p.y);
maxY = Math.max(maxY, p.y);
}
return new Point((minX + maxX) / 2.0, (minY + maxY) / 2.0);
}
public void rotate(double angle) {
Point center = getCenterPoint();
Rotate rotate = Transform.rotate(Math.toRadians(angle), center.x, center.y);
for (Point p : points) {
Point2D point2D = rotate.transform(p.x, p.y);
p.x = point2D.getX();
p.y = point2D.getY();
}
}
public void resize(double scale) {
Point center = getCenterPoint();
for (Point p : points) {
double deltaX = (p.x - center.x) * scale;
double deltaY = (p.y - center.y) * scale;
p.x = deltaX + center.x;
p.y = deltaY + center.y;
}
}
public void nudge(double x, double y) {
for (Point p : points) {
p.x = p.x + x;
p.y = p.y + y;
}
}
public int pointsSize() {
return points.size();
}
public Point getPoints(int i) {
return points.get(i);
}
public double[] xPoints() {
double[] result = new double[points.size()];
int i = 0;
for (Point point : points) {
result[i++] = point.x;
}
return result;
}
public double[] yPoints() {
double[] result = new double[points.size()];
int i = 0;
for (Point point : points) {
result[i++] = point.y;
}
return result;
}
public void nudgeColor(double r, double g, double b, double a) {
this.r += r;
this.g += g;
this.b += b;
this.a += a;
this.r = Math.min(1.0, Math.max(0.0, this.r));
this.g = Math.min(1.0, Math.max(0.0, this.g));
this.b = Math.min(1.0, Math.max(0.0, this.b));
this.a = Math.min(1.0, Math.max(0.0, this.a));
}
public List<Double> xPointsList(double scale) {
List<Double> result = new ArrayList<>(points.size());
for (double x : xPoints()) {
x = (x - center) / center / 2.0 * scale;
result.add(x);
}
return result;
}
public double toPx(double v, double scale) {
return ((v * center) * 2.0 / scale) + center;
}
public List<Double> yPointsList(double scale) {
List<Double> result = new ArrayList<>(points.size());
for (double y : yPoints()) {
y = (y - center) / center / 2.0 * scale;
result.add(y);
}
return result;
}
public void nudgePoints(Random random, boolean big) {
double factor = big ? 5.0 : 1.0;
for (Point point : points) {
point.x += (random.nextBoolean() ? -2.0 : 2.0) * random.nextDouble() * factor;
point.y += (random.nextBoolean() ? -2.0 : 2.0) * random.nextDouble() * factor;
}
}
public Parser.Node toNodeTree(int counter, double scale) {
Parser.Node node = new Parser.Node("poly", counter);
node.children.add(new Parser.Node("r", r));
node.children.add(new Parser.Node("g", g));
node.children.add(new Parser.Node("b", b));
node.children.add(new Parser.Node("a", a));
node.children.add(new Parser.Node("xPoints", xPointsList(scale)));
node.children.add(new Parser.Node("yPoints", yPointsList(scale)));
node.children.add(new Parser.Node("z", z));
return node;
}
}
public static class Bone {
public double x;
public double y;
public Bone(double x, double y) {
this.x = x;
this.y = y;
}
public Bone deepCopy() {
return new Bone(x, y);
}
}
}
package org.treeml;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
/**
* Parses a tab-indented file into a tree of Nodes.
* @author agnes.clarke
*/
public class Parser {
public static final String NADA = "nada", NULL = "null", TRUE = "true", FALSE = "false";
private int indentOfLastLine = 0;
public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
public static final DecimalFormat LONG_FORMAT = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
static {
DECIMAL_FORMAT.setMaximumFractionDigits(18);
DECIMAL_FORMAT.setMinimumFractionDigits(1);
LONG_FORMAT.setMaximumFractionDigits(0);
}
public Node parse(Reader input) throws IOException {
int lineNumber = 1;
List<Node> nodeStack = new ArrayList<Node>();
root = new RootNode();
nodeStack.add(root);
BufferedReader reader = new BufferedReader(input);
String line = reader.readLine();
while (line != null) {
doLine(line, nodeStack, lineNumber++);
line = reader.readLine();
}
return root;
}
private final List<String> valueList = new ArrayList<>();
private RootNode root;
private void doLine(String line, List<Node> nodeStack, int lineNumber) {
if (lineNumber == 1 && line.startsWith("#")) {
root.value = line.substring(1).trim();
return;
}
if (lineNumber == 2 && line.startsWith("#")) {
root.requires = line.substring(1).trim();
return;
}
valueList.clear();
Node newNode = new Node(null, null);
int stackSize = nodeStack.size();
int stackPointer = 0; // node to append to
boolean startOfLine = true;
boolean insideValue = false;
boolean insideString = false;
boolean valueSeparatedByWhitespace = false;
StringBuilder nameOrValue = new StringBuilder();
for (int index = 0; index < line.length(); index++) {
char c = line.charAt(index);
if (c == '\t' && startOfLine) {
stackPointer++;
} else {
if (startOfLine) {
if (c == '/' && nextCharEquals(line, index, '/')) {
// disregard line - it is a comment
return;
}
startOfLine = false;
// drop nodes higher than current stackPointer
for (int i = stackSize - 1; i > stackPointer; i--) {
nodeStack.remove(i);
}
if (stackPointer > indentOfLastLine + 1) {
throw new RuntimeException("Line " + lineNumber + ": illegal indentation");
} else {
indentOfLastLine = stackPointer;
}
nodeStack.get(stackPointer).children.add(newNode);
nodeStack.add(newNode);
}
if (!insideString && c == '/' && nextCharEquals(line, index, '/')) {
break;
}
if (!insideString && (c == ':' || c == ',')) {
if (newNode.name != null) {
// add values to list
valueList.add(nameOrValue.toString());
} else {
newNode.name = nameOrValue.toString();
}
insideValue = false;
nameOrValue = new StringBuilder();
valueSeparatedByWhitespace = false;
} else if (!insideString && (c == ' ' || c == '\t')) {
if (insideValue && !insideString) {
valueSeparatedByWhitespace = true;
}
} else {
if (c == '"') {
if (!insideString && insideValue) {
throw new RuntimeException("Line " + lineNumber + ", char " + index
+ ": Illegal quote");
}
insideString = !insideString;
insideValue = true;
} else {
insideValue = true;
if (valueSeparatedByWhitespace) {
throw new RuntimeException("Line " + lineNumber + ", char " + index
+ ": Illegal whitespace");
}
if (insideString && (c == '\\' && nextCharEquals(line, index, 'n'))) {
nameOrValue.append('\n');
index++;
} else if (insideString && (c == '\\' && nextCharEquals(line, index, 'r'))) {
nameOrValue.append('\r');
index++;
} else if (insideString && (c == '\\' && nextCharEquals(line, index, '"'))) {
nameOrValue.append('"');
index++;
} else if (insideString && (c == '\\' && nextCharEquals(line, index, '\\'))) {
nameOrValue.append('\\');
index++;
} else {
nameOrValue.append(c);
}
}
}
}
}
Object value;
if (valueList.isEmpty()) {
value = nameOrValue.toString();
} else {
valueList.add(nameOrValue.toString());
value = new ArrayList<>((valueList));
}
newNode.value = value;
typifyNode(newNode, nodeStack);
}
// Get the types of values right, nada the nada elements
private void typifyNode(Node newNode, List<Node> nodeStack) {
Object val = newNode.value;
if (NADA.equals(val)) {
dropNewNode(newNode, nodeStack);
} else if ("".equals(val)) {
newNode.value = null;
} else if (!(val instanceof List<?>)) {
newNode.value = typifyValue(null, val);
} else {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) val;
if (list.removeAll(Arrays.asList(NADA)) && list.isEmpty()) {
dropNewNode(newNode, nodeStack);
} else {
List<Object> newList = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
Object untypified = list.get(i);
typifyValue(newList, untypified);
}
newNode.value = newList;
}
}
}
private Object typifyValue(List<Object> valueList, Object untypified) {
if (NULL.equals(untypified)) {
return setValue(null, valueList);
} else if (TRUE.equals(untypified)) {
return setValue(true, valueList);
} else if (FALSE.equals(untypified)) {
return setValue(false, valueList);
} else if (untypified.toString().matches("[+-]?[0-9]+")) {
return setValue(Long.parseLong(untypified.toString()), valueList);
} else if (untypified.toString().matches("[+-]?[0-9]+(\\.[0-9]+)")) {
return setValue(Double.parseDouble(untypified.toString()), valueList);
}
return setValue(untypified.toString(), valueList);
}
private Object setValue(Object value, List<Object> valueList) {
if (valueList != null) {
valueList.add(value);
}
return value;
}
private void dropNewNode(Node newNode, List<Node> nodeStack) {
nodeStack.remove(newNode);
nodeStack.get(nodeStack.size() - 1).children.remove(newNode);
}
private boolean nextCharEquals(String line, int index, char... cs) {
boolean result = true;
int max = line.length();
for (int i = 0; i < cs.length; i++) {
char c = cs[i];
//noinspection StatementWithEmptyBody
if (index + 1 + i < max && line.charAt(index + 1 + i) == c) {
// match, do nothing
} else {
result = false;
}
}
return result;
}
public static class Node {
public String name;
public Object value;
public List<Node> children = new ArrayList<>();
public Node(String name, Object value) {
this.name = name;
this.value = value;
}
public String toString() {
StringBuilder sb = new StringBuilder();
toStringHelper(sb, this, "");
return sb.toString();
}
private void toStringHelper(StringBuilder sb, Node node, String indent) {
sb.append(indent).append(node.name).append("---").append(node.value).append("\r\n");
for (Node child : node.children) {
toStringHelper(sb, child, indent + " ");
}
}
public List<Node> getNodes(String name) {
List<Node> result = new ArrayList<>();
for (int i = 0; i < children.size(); i++) {
if (name.equals(children.get(i).name)) {
result.add(children.get(i));
}
}
return result;
}
public <T> T getValueAt(String name, T defaultValue) {
T t = getValueAt(name);
return t == null ? defaultValue : t;
}
public <T> T getValueAt(String name) {
String[] steps = name.split("\\.");
Node node = this;
for (String step : steps) {
List<Node> nodes = node.getNodes(step);
if (nodes.isEmpty()) return null;
node = nodes.get(0);
}
//noinspection unchecked
return (T) node.value;
}
public Node copy() {
return new Node(name, value);
}
public Node getNode(String nameForNode) {
for (int i = 0; i < children.size(); i++) {
if (nameForNode.equals(children.get(i).name)) {
return children.get(i);
}
}
return null;
}
public String toTreeML() {
StringBuilder sb = new StringBuilder();
toTreeMLImpl(sb, 0);
return sb.toString();
}
public void toTreeMLImpl(StringBuilder sb, int indent) {
for (int i = 0; i < indent; i++) {
sb.append('\t');
}
sb.append(this.name).append(" : ").append(val(this.value)).append('\n');
for (Node child : this.children) {
child.toTreeMLImpl(sb, indent + 1);
}
}
private String val(Object v) {
if (v instanceof String) {
String s = (String) v;
s = s.replace("\r", "\\r").replace("\n", "\\n").replace("\"", "\"\"");
if (s.contains(" ") || !s.equals(v)) {
return '"' + s + '"';
} else {
return (String)v;
}
} else if (v instanceof Double) {
Double d = (Double) v;
if (Math.abs(d) < 0.001 || Math.abs(d) > 999999) {
return (DECIMAL_FORMAT.format(v));
}
} else if (v instanceof Long) {
Long lo = (Long) v;
if (Math.abs(lo) > 999999) {
return (LONG_FORMAT.format(lo));
}
} else if (v instanceof List<?>) {
boolean b = true;
StringBuilder sb2 = new StringBuilder();
for (Object o : (List<?>)v) {
if (b) {
b = false;
} else {
sb2.append(", ");
}
sb2.append(val(o));
}
return sb2.toString();
}
return String.valueOf(v);
}
}
public static class RootNode extends Node {
public String requires;
public RootNode() {
super("root", null);
}
public String toString() {
return requires == null ? super.toString() : requires + "\r\n" + super.toString();
}
public Node copy() {
RootNode rootNode = new RootNode();
rootNode.requires = requires;
return rootNode;
}
public String toTreeML() {
StringBuilder sb = new StringBuilder();
int indent = 0;
for (Node child : children) {
child.toTreeMLImpl(sb, indent);
}
return sb.toString();
}
}
}
package com.clarke.agnes.polyperfect;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.control.TextField;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
//Credit: https://docs.oracle.com/javafx/2/canvas/jfxpub-canvas.htm
public class PolyMain extends Application {
private static final int DIM = 640;
//private List<Point> points;
//private List<List<Point>> donePolies = new ArrayList<>();
private final List<Boolean> gridFlag = new ArrayList<>(1);
private final Random random = new Random();
Model model = new Model();
private double copyR, copyG, copyB, copyA;
public static void main(String[] args) throws InterruptedException {
launch(args);
}
@Override
public void start(Stage primaryStage) {
gridFlag.add(true);
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(10, 10, 10, 10));
primaryStage.setTitle("PolyPerfect 5.1");
Canvas canvas = new Canvas(DIM, DIM);
grid.add(canvas, 5, 0, 15, 15);
Text scaleTxt = new Text("Scale: ");
grid.add(scaleTxt, 0, 1, 1, 1);
final TextField scaleTF = new TextField();
grid.add(scaleTF, 1, 1, 3, 1);
scaleTF.setText("" + model.getScale());
scaleTF.setOnAction(event -> {
String t = scaleTF.getText();
double td = Double.parseDouble(t);
model.setScale(td);
});
Text rtxt = new Text("Color: ");
grid.add(rtxt, 0, 0, 1, 1);
final TextField rgba = new TextField();
grid.add(rgba, 1, 0, 3, 1);
final TextField colorDisplay = new TextField();
final GraphicsContext gc = canvas.getGraphicsContext2D();
rgba.setOnAction(event -> {
String[] color = rgba.getText().split(",");
int v0 = (int) (255 * Double.parseDouble(color[0]));
int v1 = (int) (255 * Double.parseDouble(color[1]));
int v2 = (int) (255 * Double.parseDouble(color[2]));
double v3 = Double.parseDouble(color[3]);
colorDisplay.setStyle("-fx-background-color:rgba(" + String.format("%s,%s,%s,%.2f", v0, v1, v2, v3) + ")");
});
colorDisplay.setOnAction(event -> {
try {
String[] color = rgba.getText().split(",");
model.setColor(
Double.parseDouble(color[0]),
Double.parseDouble(color[1]),
Double.parseDouble(color[2]),
Double.parseDouble(color[3]));
drawShapes(gc);
colorDisplay.getParent().requestFocus();
} catch (Throwable ignore) {
}
});
//grid.add(dummy, 4, 0, 1, 1);
grid.add(colorDisplay, 4, 0, 1, 1);
canvas.setFocusTraversable(true);
canvas.addEventFilter(MouseEvent.ANY, (e) -> canvas.requestFocus());
primaryStage.addEventHandler(KeyEvent.KEY_TYPED, event -> {
String character = event.getCharacter();
if ("p".equals(character) && model.getCurrentPolySize() > 2) {
model.newPoly();
}
if ("]".equals(character)) {
model.nextPoly();
}
if ("z".equals(character)) {
model.undo();
}
if ("y".equals(character)) {
model.redo();
}
if ("w".equals(character)) {
model.nudge(0, -2);
}
if ("a".equals(character)) {
model.nudge(-2, 0);
}
if ("s".equals(character)) {
model.nudge(0, 2);
}
if ("d".equals(character)) {
model.nudge(2, 0);
}
if ("<".equals(character)) {
model.rotate();
}
if (">".equals(character)) {
model.counterRotate();
}
if ("+".equals(character)) {
model.larger();
}
if ("-".equals(character)) {
model.smaller();
}
if ("r".equals(character)) {
model.nudgeColor(0.05, 0, 0, 0);
}
if ("R".equals(character)) {
model.nudgeColor(-0.05, 0, 0, 0);
}
if ("g".equals(character)) {
model.nudgeColor(0, 0.05, 0, 0);
}
if ("G".equals(character)) {
model.nudgeColor(0, -0.05, 0, 0);
}
if ("b".equals(character)) {
model.nudgeColor(0, 0, 0.05, 0);
}
if ("B".equals(character)) {
model.nudgeColor(0, 0, -0.05, 0);
}
if ("t".equals(character)) {
model.nudgeColor(0, 0, 0, 0.05);
}
if ("T".equals(character)) {
model.nudgeColor(0, 0, 0, -0.05);
}
if ("o".equals(character)) {
model.nudgePoints(random, false);
}
if ("O".equals(character)) {
model.nudgePoints(random, true);
}
if ("x".equals(character)) {
if (model.getSelectedCompletePoly() != null) {
copyR = model.getSelectedCompletePoly().r;
copyG = model.getSelectedCompletePoly().g;
copyB = model.getSelectedCompletePoly().b;
copyA = model.getSelectedCompletePoly().a;
}
}
if ("X".equals(character)) {
model.setColor(copyR, copyG, copyB, copyA);
}
if ("i".equals(character)) {
model.nudgeColor(
random.nextBoolean() ? -0.01 : +0.01,
random.nextBoolean() ? -0.01 : +0.01,
random.nextBoolean() ? -0.01 : +0.01,
0
);
}
if ("I".equals(character)) {
model.nudgeColor(
random.nextBoolean() ? -0.05 : +0.05,
random.nextBoolean() ? -0.05 : +0.05,
random.nextBoolean() ? -0.05 : +0.05,
0
);
}
if ("c".equals(character)) {
model.copyPoly();
}
if ("S".equals(character)) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Save to PP5 format");
File saveFile = fileChooser.showSaveDialog(primaryStage);
if (saveFile != null) {
try {
FileWriter writer = new FileWriter(saveFile);
writer.write(model.toNodeTree().toTreeML());
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
if ("Q".equals(character)) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Load PP5 format");
File savedFile = fileChooser.showOpenDialog(primaryStage);
if (savedFile != null) {
try {
FileReader reader = new FileReader(savedFile);
model.load(reader);
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
ModelStep.Poly p = model.getSelectedCompletePoly();
if (p != null) {
rgba.setText(String.format("%.3f,%.3f,%.3f,%.3f", p.r, p.g, p.b, p.a));
colorDisplay.setStyle("-fx-background-color:rgba(" + String.format("%.0f,%.0f,%.0f,%.2f", p.r * 255, p.g * 255, p.b * 255, p.a) + ")");
}
drawShapes(gc);
scaleTF.setText("" + model.getScale());
});
canvas.setOnMouseClicked(e -> {
if (e.getButton().equals(MouseButton.PRIMARY) && e.isStillSincePress()) {
int x = (int) e.getX();
int y = (int) e.getY();
model.addPoint(new ModelStep.Point(x, y));
} else {
gridFlag.add(0, !gridFlag.get(0));
gridFlag.remove(1);
}
drawShapes(gc);
});
drawShapes(gc);
//root.getChildren().add(canvas);
primaryStage.setScene(new Scene(grid));
primaryStage.show();
}
private void drawShapes(GraphicsContext gc) {
gc.setFill(Color.WHITE);
gc.setStroke(Color.BLACK);
gc.setLineWidth(1);
gc.fillRect(0, 0, 1920, 1080);
gc.strokeRect(0, 0, 640, 640);
//grid
if (gridFlag.get(0)) {
gc.setFill(Color.AQUAMARINE);
gc.setStroke(Color.AQUAMARINE);
gc.setLineWidth(1);
for (int x = 32; x < DIM; x += 32) {
gc.strokeLine(x, 0, x, DIM);
}
for (int y = 32; y < DIM; y += 32) {
gc.strokeLine(0, y, DIM, y);
}
}
int r = 4;
drawPoint(320, 320, gc, r);
//draw DonePolies
for (ModelStep.Poly poly : model.getPolies()) {
gc.setFill(new Color(poly.r, poly.g, poly.b, poly.a));
gc.setStroke(new Color(poly.r, poly.g, poly.b, poly.a));
double[] xPoints = poly.xPoints();
double[] yPoints = poly.yPoints();
int nPoints = poly.pointsSize();
gc.fillPolygon(xPoints, yPoints, nPoints);
}
//highlight selected complete poly
if (null != model.getSelectedCompletePoly()) {
gc.setLineWidth(1);
ModelStep.Poly poly = model.getSelectedCompletePoly();
gc.setStroke(Color.BLACK);
double[] xPoints = poly.xPoints();
double[] yPoints = poly.yPoints();
int nPoints = poly.pointsSize();
gc.strokePolygon(xPoints, yPoints, nPoints);
gc.setFill(new Color(1, 0.5, 0.5, 1));
ModelStep.Point p = model.getSelectedCompletePoly().getCenterPoint();
drawPoint((int) p.x, (int) p.y, gc, 2);
}
if (model.getCurrentPolySize() == 0) {
return;
}
if (model.getCurrentPolySize() == 1) {
gc.setFill(Color.AQUAMARINE);
gc.setStroke(Color.AQUAMARINE);
gc.setLineWidth(1);
ModelStep.Point p1 = model.getCurrentPoly().getPoints(0);
drawPoint((int) p1.x, (int) p1.y, gc, r * 2);
return;
}
for (int i = 1; i < model.getCurrentPolySize(); i++) {
ModelStep.Point p1 = model.getCurrentPoly().getPoints(i - 1);
ModelStep.Point p2 = model.getCurrentPoly().getPoints(i);
drawLine(p1.x, p1.y, p2.x, p2.y, gc, r, false);
}
ModelStep.Point p1 = model.getCurrentPoly().getPoints(0);
ModelStep.Point p2 = model.getCurrentPoly().getPoints(model.getCurrentPolySize() - 1);
drawLine(p1.x, p1.y, p2.x, p2.y, gc, r, true);
}
private void drawLine(double x, double y, double x2, double y2, GraphicsContext gc, int r, boolean closer) {
if (closer) {
gc.setFill(Color.AQUAMARINE);
gc.setStroke(Color.AQUAMARINE);
gc.setLineWidth(3);
} else {
gc.setFill(Color.GREY);
gc.setStroke(Color.GREY);
gc.setLineWidth(3);
}
gc.strokeLine(x, y, x2, y2);
if (closer) {
gc.setFill(Color.AQUAMARINE);
gc.setStroke(Color.AQUAMARINE);
gc.setLineWidth(3);
drawPoint((int) x, (int) y, gc, r * 2);
drawPoint((int) x2, (int) y2, gc, r * 2);
} else {
gc.setFill(Color.BLACK);
gc.setStroke(Color.BLACK);
gc.setLineWidth(3);
drawPoint((int) x, (int) y, gc, r);
drawPoint((int) x2, (int) y2, gc, r);
}
}
private void drawPoint(int x, int y, GraphicsContext gc, int r) {
gc.fillOval(x - r, y - r, 2 * r, 2 * r);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment