allows the usage of polygons with fractional coordinates

This commit is contained in:
hneemann 2018-03-18 16:45:42 +01:00
parent 7917e6df43
commit 87f8ae6138
16 changed files with 311 additions and 106 deletions

View File

@ -208,7 +208,7 @@ public class VisualElement implements Drawable, Movable, AttributeListener {
if (transform == null) {
int rotate = getRotate();
if (rotate == 0)
transform = v -> v.add(pos);
transform = new TransformTranslate(pos);
else
transform = new TransformRotate(pos, rotate);
}

View File

@ -57,7 +57,7 @@ public class GraphicMinMax implements Graphic {
@Override
public void drawPolygon(Polygon p, Style style) {
for (Vector v : p)
for (VectorInterface v : p)
check(v);
}
@ -72,10 +72,10 @@ public class GraphicMinMax implements Graphic {
*
* @param p the point to check
*/
public void check(Vector p) {
public void check(VectorInterface p) {
if (min == null || max == null) {
min = new Vector(p.x, p.y);
max = new Vector(p.x, p.y);
min = new Vector(p.getX(), p.getY());
max = new Vector(p.getX(), p.getY());
} else {
min = Vector.min(min, p);
max = Vector.max(max, p);

View File

@ -296,8 +296,8 @@ public class GraphicSVG implements Graphic {
}
}
private String str(Vector p) {
return p.x + "," + p.y;
private String str(VectorInterface p) {
return p.getXFloat() + "," + p.getYFloat();
}
}

View File

@ -68,13 +68,15 @@ public class GraphicSwing implements Graphic {
//CHECKSTYLE.OFF: ModifiedControlVariable
for (int i = 0; i < p.size(); i++) {
if (i == 0) {
path.moveTo(p.get(i).x, p.get(i).y);
path.moveTo(p.get(i).getXFloat(), p.get(i).getYFloat());
} else {
if (p.isBezierStart(i)) {
path.curveTo(p.get(i).x, p.get(i).y, p.get(i + 1).x, p.get(i + 1).y, p.get(i + 2).x, p.get(i + 2).y);
path.curveTo(p.get(i).getXFloat(), p.get(i).getYFloat(),
p.get(i + 1).getXFloat(), p.get(i + 1).getYFloat(),
p.get(i + 2).getXFloat(), p.get(i + 2).getYFloat());
i += 2;
} else
path.lineTo(p.get(i).x, p.get(i).y);
path.lineTo(p.get(i).getXFloat(), p.get(i).getYFloat());
}
}
//CHECKSTYLE.ON: ModifiedControlVariable

View File

@ -13,9 +13,9 @@ import java.util.StringTokenizer;
/**
* A polygon representation used by the {@link Graphic} interface.
*/
public class Polygon implements Iterable<Vector> {
public class Polygon implements Iterable<VectorInterface> {
private final ArrayList<Vector> points;
private final ArrayList<VectorInterface> points;
private final HashSet<Integer> isBezierStart;
private final boolean closed;
@ -41,7 +41,7 @@ public class Polygon implements Iterable<Vector> {
* @param points the polygons points
* @param closed true if polygon is closed
*/
public Polygon(ArrayList<Vector> points, boolean closed) {
public Polygon(ArrayList<VectorInterface> points, boolean closed) {
this.points = points;
this.closed = closed;
isBezierStart = new HashSet<>();
@ -71,7 +71,7 @@ public class Polygon implements Iterable<Vector> {
* @param p the point to add
* @return this for chained calls
*/
public Polygon add(Vector p) {
public Polygon add(VectorInterface p) {
points.add(p);
return this;
}
@ -115,12 +115,12 @@ public class Polygon implements Iterable<Vector> {
* @param i the index
* @return the i'th point
*/
public Vector get(int i) {
public VectorInterface get(int i) {
return points.get(i);
}
@Override
public Iterator<Vector> iterator() {
public Iterator<VectorInterface> iterator() {
return points.iterator();
}
@ -152,14 +152,14 @@ public class Polygon implements Iterable<Vector> {
/**
* @return the first point of the polygon
*/
public Vector getFirst() {
public VectorInterface getFirst() {
return points.get(0);
}
/**
* @return the last point of the polygon
*/
public Vector getLast() {
public VectorInterface getLast() {
return points.get(points.size() - 1);
}
@ -199,7 +199,7 @@ public class Polygon implements Iterable<Vector> {
*/
public Polygon transform(Transform transform) {
Polygon p = new Polygon(closed);
for (Vector v : points)
for (VectorInterface v : points)
p.add(transform.transform(v));
p.isBezierStart.addAll(isBezierStart);
return p;
@ -213,56 +213,56 @@ public class Polygon implements Iterable<Vector> {
*/
public static Polygon createFromPath(String path) {
StringTokenizer tok = new StringTokenizer(path, " ,");
int x = 0;
int y = 0;
float x = 0;
float y = 0;
boolean closed = false;
ArrayList<Vector> list = new ArrayList<>();
ArrayList<VectorInterface> list = new ArrayList<>();
String lastTok = null;
while (tok.hasMoreTokens()) {
final String t = tok.nextToken();
switch (t) {
case "M":
x = Integer.parseInt(tok.nextToken());
y = Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
x = Float.parseFloat(tok.nextToken());
y = Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
lastTok = t;
break;
case "m":
x += Integer.parseInt(tok.nextToken());
y += Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
x += Float.parseFloat(tok.nextToken());
y += Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
lastTok = t;
break;
case "V":
y = Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
y = Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
lastTok = t;
break;
case "v":
y += Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
y += Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
lastTok = t;
break;
case "H":
x = Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
x = Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
lastTok = t;
break;
case "h":
x += Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
x += Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
lastTok = t;
break;
case "l":
x += Integer.parseInt(tok.nextToken());
y += Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
x += Float.parseFloat(tok.nextToken());
y += Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
lastTok = t;
break;
case "L":
x = Integer.parseInt(tok.nextToken());
y = Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
x = Float.parseFloat(tok.nextToken());
y = Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
lastTok = t;
break;
case "Z":
@ -272,30 +272,30 @@ public class Polygon implements Iterable<Vector> {
default:
switch (lastTok) {
case "V":
y = Integer.parseInt(t);
list.add(new Vector(x, y));
y = Float.parseFloat(t);
list.add(new VectorFloat(x, y));
break;
case "v":
y += Integer.parseInt(t);
list.add(new Vector(x, y));
y += Float.parseFloat(t);
list.add(new VectorFloat(x, y));
break;
case "H":
x = Integer.parseInt(t);
list.add(new Vector(x, y));
x = Float.parseFloat(t);
list.add(new VectorFloat(x, y));
break;
case "h":
x += Integer.parseInt(t);
list.add(new Vector(x, y));
x += Float.parseFloat(t);
list.add(new VectorFloat(x, y));
break;
case "l":
x += Integer.parseInt(t);
y += Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
x += Float.parseFloat(t);
y += Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
break;
case "L":
x = Integer.parseInt(t);
y = Integer.parseInt(tok.nextToken());
list.add(new Vector(x, y));
x = Float.parseFloat(t);
y = Float.parseFloat(tok.nextToken());
list.add(new VectorFloat(x, y));
break;
default:
return null;

View File

@ -8,12 +8,35 @@ package de.neemann.digital.draw.graphics;
/**
* A simple transformation to a given vector
*/
public interface Transform {
public abstract class Transform {
/**
* Transforms a vector
* Transforms an integer vector
*
* @param v the vector to transform
* @return the transformed vector
*/
Vector transform(Vector v);
public abstract Vector transform(Vector v);
/**
* Transforms an float vector
*
* @param v the vector to transform
* @return the transformed vector
*/
public abstract VectorFloat transform(VectorFloat v);
/**
* Transforms an vector interface
*
* @param v the vector to transform
* @return the transformed vector
*/
public VectorInterface transform(VectorInterface v) {
if (v instanceof Vector)
return transform((Vector) v);
else
return transform(v.getVectorFloat());
}
}

View File

@ -8,11 +8,11 @@ package de.neemann.digital.draw.graphics;
/**
* Implements a rotation and translation.
*/
public class TransformRotate implements Transform {
public class TransformRotate extends Transform {
private final int sin;
private final int cos;
private final Vector translation;
private final VectorInterface translation;
/**
* Creates a new instance
@ -20,7 +20,7 @@ public class TransformRotate implements Transform {
* @param translation the translation
* @param rot the rotation
*/
public TransformRotate(Vector translation, int rot) {
public TransformRotate(VectorInterface translation, int rot) {
this.translation = translation;
switch (rot) {
case 1:
@ -44,6 +44,13 @@ public class TransformRotate implements Transform {
@Override
public Vector transform(Vector v) {
return new Vector(v.x * cos + v.y * sin, -v.x * sin + v.y * cos).add(translation);
return new Vector(v.getX() * cos + v.getY() * sin + translation.getX(),
-v.getX() * sin + v.getY() * cos + translation.getY());
}
@Override
public VectorFloat transform(VectorFloat v) {
return new VectorFloat(v.getXFloat() * cos + v.getYFloat() * sin + translation.getXFloat(),
-v.getXFloat() * sin + v.getYFloat() * cos + translation.getYFloat());
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
/**
* A translation
*/
public class TransformTranslate extends Transform {
private final VectorInterface trans;
/**
* Creates a new instance
*
* @param trans the translation
*/
public TransformTranslate(VectorInterface trans) {
this.trans = trans;
}
@Override
public Vector transform(Vector v) {
return v.add(trans.getX(), trans.getY());
}
@Override
public VectorFloat transform(VectorFloat v) {
return new VectorFloat(v.getXFloat() + trans.getXFloat(), v.getYFloat() + trans.getYFloat());
}
}

View File

@ -11,7 +11,7 @@ import java.util.List;
/**
* Represents a 2D Vector
*/
public class Vector {
public class Vector implements VectorInterface {
/**
* the x coordinate
@ -62,12 +62,12 @@ public class Vector {
* @param p the vectors to evaluate
* @return the minimum
*/
public static Vector min(Vector... p) {
int x = p[0].x;
int y = p[0].y;
public static Vector min(VectorInterface... p) {
int x = p[0].getX();
int y = p[0].getY();
for (int i = 1; i < p.length; i++) {
if (p[i].x < x) x = p[i].x;
if (p[i].y < y) y = p[i].y;
if (p[i].getX() < x) x = p[i].getX();
if (p[i].getY() < y) y = p[i].getY();
}
return new Vector(x, y);
}
@ -78,12 +78,12 @@ public class Vector {
* @param p the vectors to evaluate
* @return the maximum
*/
public static Vector max(Vector... p) {
int x = p[0].x;
int y = p[0].y;
public static Vector max(VectorInterface... p) {
int x = p[0].getX();
int y = p[0].getY();
for (int i = 1; i < p.length; i++) {
if (p[i].x > x) x = p[i].x;
if (p[i].y > y) y = p[i].y;
if (p[i].getX() > x) x = p[i].getX();
if (p[i].getY() > y) y = p[i].getY();
}
return new Vector(x, y);
}
@ -225,4 +225,28 @@ public class Vector {
return new Vector(Math.round(x * 128 / l), Math.round(y * 128 / l));
}
@Override
public int getX() {
return x;
}
@Override
public int getY() {
return y;
}
@Override
public float getXFloat() {
return x;
}
@Override
public float getYFloat() {
return y;
}
@Override
public VectorFloat getVectorFloat() {
return new VectorFloat(x, y);
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2018 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
import java.util.Objects;
/**
* A vector with float coordinates
*/
public class VectorFloat implements VectorInterface {
private final float x;
private final float y;
/**
* Creates a new vector
*
* @param x the x coordinate
* @param y the x coordinate
*/
public VectorFloat(float x, float y) {
this.x = x;
this.y = y;
}
@Override
public int getX() {
return (int) x;
}
@Override
public int getY() {
return (int) y;
}
@Override
public float getXFloat() {
return x;
}
@Override
public float getYFloat() {
return y;
}
@Override
public VectorFloat getVectorFloat() {
return this;
}
/**
* Subtracts the given vector
*
* @param sub the vector zo subtract
* @return the new vector
*/
public VectorFloat sub(VectorInterface sub) {
return new VectorFloat(x - sub.getXFloat(), y - sub.getYFloat());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VectorFloat that = (VectorFloat) o;
return Float.compare(that.x, x) == 0
&& Float.compare(that.y, y) == 0;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
/**
* The bas class of all vectors
*/
public interface VectorInterface {
/**
* @return the integer x coordinate
*/
int getX();
/**
* @return the integer y coordinate
*/
int getY();
/**
* @return the float x coordinate
*/
float getXFloat();
/**
* @return the float y coordinate
*/
float getYFloat();
/**
* @return this vactor as a {@link VectorFloat} instance
*/
VectorFloat getVectorFloat();
}

View File

@ -27,13 +27,13 @@ public abstract class SevenShape implements Shape {
*/
public static final Polygon FRAME = Polygon.createFromPath("m -10,1 L 70,1 70,139 -10,139 z");
private static final Polygon[] POLYGONS = new Polygon[]{
Polygon.createFromPath("m 57,14 L 62,10 57,5 10,5 5,10 9,14 z"), // 0,
Polygon.createFromPath("m 53,65 L 57,14 62,10 66,14 63,65 57,70 z"), // 1,
Polygon.createFromPath("m 49,126 L 52,75 57,70 62,75 58,126 53,130 z"), // 2,
Polygon.createFromPath("m 48,135 L 53,130 49,126 1,126 -3,130 1,135 z"), // 3,
Polygon.createFromPath("m -7,126 L -4,75 1,70 5,75 1,126 -3,130 z"), // 4,
Polygon.createFromPath("m -3,65 L 0,14 5,10 9,14 6,65 1,70 z"), // 5,
Polygon.createFromPath("m 52,75 L 57,70 53,65 6,65 1,70 5,75 z"), // 6,
Polygon.createFromPath("m 12.04,5.0 L 52.8,5.0 58.15,10.61 52.28,16.22 11.52,16.22 6.17,10.61 z"), // 0,
Polygon.createFromPath("m 59.49,12.01 L 64.84,17.62 62.74,62.99 56.87,68.6 51.52,62.99 53.62,17.62 z"), // 1,
Polygon.createFromPath("m 56.74,71.4 L 62.09,77.01 60.0,122.38 54.13,127.99 48.78,122.38 50.88,77.01 z"), // 2,
Polygon.createFromPath("m 6.55,123.78 L 47.32,123.78 52.67,129.39 46.8,135.0 6.04,135.0 0.69,129.39 z"), // 3,
Polygon.createFromPath("m 1.96,71.4 L 7.31,77.01 5.22,122.38 -0.64,127.99 -5.99,122.38 -3.9,77.01 z"), // 4,
Polygon.createFromPath("m 4.7,12.01 L 10.05,17.62 7.96,62.99 2.09,68.6 -3.25,62.99 -1.15,17.62 z"), // 5,
Polygon.createFromPath("m 9.3,64.39 L 50.06,64.39 55.41,70.0 49.54,75.61 8.78,75.61 3.43,70.0 z"), // 6,
};
private static final Vector DOT = new Vector(58, 127);

View File

@ -29,22 +29,22 @@ import static de.neemann.digital.draw.shapes.GenericShape.SIZE;
public class SixteenShape implements Shape {
private static final Polygon[] POLYGONS = new Polygon[]{
Polygon.createFromPath("m 10,5 L 29,5 33,10 28,14 9,14 5,10 z"), // 0,
Polygon.createFromPath("m 38,5 L 57,5 62,10 57,14 38,14 33,10 z"), // 1,
Polygon.createFromPath("m 53,65 L 57,14 62,10 66,14 63,65 57,70 z"), // 2,
Polygon.createFromPath("m 49,126 L 52,75 57,70 62,75 58,126 53,130 z"), // 3,
Polygon.createFromPath("m 30,126 L 49,126 53,130 48,135 29,135 25,130 z"), // 4,
Polygon.createFromPath("m 1,126 L 20,126 25,130 20,135 1,135 -3,130 z"), // 5,
Polygon.createFromPath("m -7,126 L -4,75 1,70 5,75 1,126 -3,130 z"), // 6,
Polygon.createFromPath("m -3,65 L 0,14 5,10 9,14 6,65 1,70 z"), // 7,
Polygon.createFromPath("m 6,65 L 25,65 29,70 24,75 5,75 1,70 z"), // 8,
Polygon.createFromPath("m 34,65 L 53,65 57,70 52,75 33,75 29,70 z"), // 9,
Polygon.createFromPath("m 14,14 L 26,56 25,65 20,65 8,24 9,14 z"), // 10,
Polygon.createFromPath("m 25,65 L 28,14 33,10 38,14 34,65 29,70 z"), // 11,
Polygon.createFromPath("m 52,14 L 35,56 34,65 39,65 56,24 57,14 z"), // 12,
Polygon.createFromPath("m 44,126 L 32,84 33,75 38,75 50,116 49,126 z"), // 13,
Polygon.createFromPath("m 20,126 L 24,75 29,70 33,75 30,126 25,130 z"), // 14,
Polygon.createFromPath("m 6,126 L 23,84 24,75 19,75 2,116 1,126 z"), // 15,
Polygon.createFromPath("m 12.04,5.0 L 25.41,5.0 30.76,10.61 24.89,16.22 11.52,16.22 6.17,10.61 z"), // 0,
Polygon.createFromPath("m 39.43,5.0 L 52.8,5.0 58.15,10.61 52.28,16.22 38.91,16.22 33.56,10.61 z"), // 1,
Polygon.createFromPath("m 59.49,12.01 L 64.84,17.62 62.74,62.99 56.87,68.6 51.52,62.99 53.62,17.62 z"), // 2,
Polygon.createFromPath("m 56.74,71.4 L 62.09,77.01 60.0,122.38 54.13,127.99 48.78,122.38 50.88,77.01 z"), // 3,
Polygon.createFromPath("m 33.94,123.78 L 47.32,123.78 52.67,129.39 46.8,135.0 33.43,135.0 28.08,129.39 z"), // 4,
Polygon.createFromPath("m 6.55,123.78 L 19.93,123.78 25.28,129.39 19.41,135.0 6.04,135.0 0.69,129.39 z"), // 5,
Polygon.createFromPath("m 1.96,71.4 L 7.31,77.01 5.22,122.38 -0.64,127.99 -5.99,122.38 -3.9,77.01 z"), // 6,
Polygon.createFromPath("m 4.7,12.01 L 10.05,17.62 7.96,62.99 2.09,68.6 -3.25,62.99 -1.15,17.62 z"), // 7,
Polygon.createFromPath("m 9.29,64.39 L 22.67,64.39 28.02,70.0 22.15,75.61 8.78,75.61 3.43,70.0 z"), // 8,
Polygon.createFromPath("m 36.69,64.39 L 50.06,64.39 55.41,70.0 49.54,75.61 36.17,75.61 30.82,70.0 z"), // 9,
Polygon.createFromPath("m 12.01,18.2 L 17.06,18.2 22.73,50.48 22.18,62.41 17.13,62.41 11.46,30.13 z"), // 10,
Polygon.createFromPath("m 32.09,12.01 L 37.44,17.62 35.35,62.99 29.48,68.6 24.13,62.99 26.23,17.62 z"), // 11,
Polygon.createFromPath("m 46.56,18.2 L 51.61,18.2 51.06,30.13 42.41,62.41 37.36,62.41 37.91,50.48 z"), // 12,
Polygon.createFromPath("m 36.66,77.59 L 41.7,77.59 47.38,109.87 46.83,121.8 41.78,121.8 36.11,89.52 z"), // 13,
Polygon.createFromPath("m 29.35,71.4 L 34.7,77.01 32.61,122.38 26.74,127.99 21.39,122.38 23.49,77.01 z"), // 14,
Polygon.createFromPath("m 16.43,77.59 L 21.48,77.59 20.93,89.52 12.27,121.8 7.22,121.8 7.78,109.87 z"), // 15,
};
private static final Vector DOT = new Vector(58, 127);

View File

@ -11,10 +11,7 @@ import de.neemann.digital.core.element.Keys;
import de.neemann.digital.core.element.PinDescriptions;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.draw.elements.Pins;
import de.neemann.digital.draw.graphics.Graphic;
import de.neemann.digital.draw.graphics.GraphicTransform;
import de.neemann.digital.draw.graphics.Style;
import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.model.InverterConfig;
import de.neemann.digital.draw.shapes.GenericShape;
import de.neemann.digital.draw.shapes.InteractorInterface;
@ -65,7 +62,7 @@ public abstract class IEEEGenericShape implements Shape {
@Override
public void drawTo(Graphic graphic, Style highLight) {
int offs = (inputs.size() / 2 - 1) * SIZE;
drawIEEE(new GraphicTransform(graphic, v -> v.add(0, offs)));
drawIEEE(new GraphicTransform(graphic, new TransformTranslate(new Vector(0, offs))));
if (offs > 0) {
graphic.drawLine(new Vector(1, 0), new Vector(1, offs - SIZE2 - 1), Style.NORMAL);

View File

@ -12,6 +12,7 @@ import de.neemann.digital.draw.elements.Wire;
import de.neemann.digital.draw.graphics.Transform;
import de.neemann.digital.draw.graphics.TransformRotate;
import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.draw.graphics.VectorFloat;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.lang.Lang;
@ -70,6 +71,11 @@ public class ModifyMoveSelected implements Modification {
public Vector transform(Vector v) {
return super.transform(v.sub(center));
}
@Override
public VectorFloat transform(VectorFloat v) {
return super.transform(v.sub(center));
}
};
for (Movable m : elements) {

View File

@ -18,9 +18,9 @@ public class PolygonTest extends TestCase {
private void check(Polygon p) {
assertEquals(4, p.size());
assertEquals(new Vector(10, 10), p.get(0));
assertEquals(new Vector(20, 10), p.get(1));
assertEquals(new Vector(20, 20), p.get(2));
assertEquals(new Vector(10, 20), p.get(3));
assertEquals(new VectorFloat(10, 10), p.get(0));
assertEquals(new VectorFloat(20, 10), p.get(1));
assertEquals(new VectorFloat(20, 20), p.get(2));
assertEquals(new VectorFloat(10, 20), p.get(3));
}
}