mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-18 09:24:42 -04:00
Improved the Polygon and the PolygonParser to deal with compound paths.
This commit is contained in:
parent
b23c430f78
commit
b1a82601c9
@ -57,8 +57,13 @@ public class GraphicMinMax implements Graphic {
|
||||
|
||||
@Override
|
||||
public void drawPolygon(Polygon p, Style style) {
|
||||
for (VectorInterface v : p)
|
||||
check(v);
|
||||
for (Polygon.PathElement v : p) {
|
||||
if (v==null)
|
||||
System.out.println(v);
|
||||
final VectorInterface point = v.getPoint();
|
||||
if (point != null)
|
||||
check(point);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -113,22 +113,11 @@ public class GraphicSVG implements Graphic {
|
||||
@Override
|
||||
public void drawPolygon(Polygon p, Style style) {
|
||||
try {
|
||||
//modification of loop variable i is intended!
|
||||
//CHECKSTYLE.OFF: ModifiedControlVariable
|
||||
w.write("<path d=\"M " + str(p.get(0)));
|
||||
for (int i = 1; i < p.size(); i++)
|
||||
if (p.isBezierStart(i)) {
|
||||
w.write(" C " + str(p.get(i)) + " " + str(p.get(i + 1)) + " " + str(p.get(i + 2)));
|
||||
i += 2;
|
||||
} else
|
||||
w.write(" L " + str(p.get(i)));
|
||||
//CHECKSTYLE.ON: ModifiedControlVariable
|
||||
|
||||
if (p.isClosed())
|
||||
w.write(" Z");
|
||||
|
||||
w.write("\"");
|
||||
w.write("<path d=\"" + p + "\"");
|
||||
addStrokeDash(w, style.getDash());
|
||||
if (p.getEvenOdd() && style.isFilled())
|
||||
w.write(" fill-rule=\"evenodd\"");
|
||||
|
||||
if (style.isFilled() && p.isClosed())
|
||||
w.write(" stroke=\"" + getColor(style) + "\" stroke-width=\"" + getStrokeWidth(style) + "\" fill=\"" + getColor(style) + "\" fill-opacity=\"" + getOpacity(style) + "\"/>\n");
|
||||
else
|
||||
|
@ -64,25 +64,7 @@ public class GraphicSwing implements Graphic {
|
||||
public void drawPolygon(Polygon p, Style style) {
|
||||
applyStyle(style);
|
||||
Path2D path = new GeneralPath();
|
||||
//modification of loop variable i is intended!
|
||||
//CHECKSTYLE.OFF: ModifiedControlVariable
|
||||
for (int i = 0; i < p.size(); i++) {
|
||||
if (i == 0) {
|
||||
path.moveTo(p.get(i).getXFloat(), p.get(i).getYFloat());
|
||||
} else {
|
||||
if (p.isBezierStart(i)) {
|
||||
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).getXFloat(), p.get(i).getYFloat());
|
||||
}
|
||||
}
|
||||
//CHECKSTYLE.ON: ModifiedControlVariable
|
||||
|
||||
if (p.isClosed())
|
||||
path.closePath();
|
||||
p.drawTo(path);
|
||||
|
||||
if (style.isFilled() && p.isClosed())
|
||||
gr.fill(path);
|
||||
|
@ -5,18 +5,19 @@
|
||||
*/
|
||||
package de.neemann.digital.draw.graphics;
|
||||
|
||||
import java.awt.geom.Path2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* A polygon representation used by the {@link Graphic} interface.
|
||||
*/
|
||||
public class Polygon implements Iterable<VectorInterface> {
|
||||
public class Polygon implements Iterable<Polygon.PathElement> {
|
||||
|
||||
private final ArrayList<VectorInterface> points;
|
||||
private final HashSet<Integer> isBezierStart;
|
||||
private final ArrayList<PathElement> path;
|
||||
private boolean closed;
|
||||
private boolean hasSpecialElements = false;
|
||||
private boolean evenOdd;
|
||||
|
||||
/**
|
||||
* Creates e new closed polygon
|
||||
@ -41,9 +42,10 @@ public class Polygon implements Iterable<VectorInterface> {
|
||||
* @param closed true if polygon is closed
|
||||
*/
|
||||
public Polygon(ArrayList<VectorInterface> points, boolean closed) {
|
||||
this.points = points;
|
||||
this.closed = closed;
|
||||
isBezierStart = new HashSet<>();
|
||||
this.path = new ArrayList<>();
|
||||
for (VectorInterface p : points)
|
||||
add(p);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,10 +73,17 @@ public class Polygon implements Iterable<VectorInterface> {
|
||||
* @return this for chained calls
|
||||
*/
|
||||
public Polygon add(VectorInterface p) {
|
||||
points.add(p);
|
||||
if (path.isEmpty())
|
||||
path.add(new MoveTo(p));
|
||||
else
|
||||
path.add(new LineTo(p));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void add(PathElement pe) {
|
||||
path.add(pe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new cubic bezier curve to the polygon.
|
||||
*
|
||||
@ -84,45 +93,29 @@ public class Polygon implements Iterable<VectorInterface> {
|
||||
* @return this for chained calls
|
||||
*/
|
||||
public Polygon add(VectorInterface c1, VectorInterface c2, VectorInterface p) {
|
||||
if (points.size() == 0)
|
||||
if (path.size() == 0)
|
||||
throw new RuntimeException("cubic bezier curve is not allowed to be the first path element");
|
||||
isBezierStart.add(points.size());
|
||||
points.add(c1);
|
||||
points.add(c2);
|
||||
points.add(p);
|
||||
path.add(new CurveTo(c1, c2, p));
|
||||
hasSpecialElements = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the point with the given index is a bezier start point
|
||||
*
|
||||
* @param n the index
|
||||
* @return true if point is bezier start
|
||||
* @return true if filled in even odd mode
|
||||
*/
|
||||
public boolean isBezierStart(int n) {
|
||||
return isBezierStart.contains(n);
|
||||
public boolean getEvenOdd() {
|
||||
return evenOdd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of points
|
||||
*/
|
||||
public int size() {
|
||||
return points.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one of the points
|
||||
* Sets the even odd mode used to fill the polygon
|
||||
*
|
||||
* @param i the index
|
||||
* @return the i'th point
|
||||
* @param evenOdd true is even odd mode is needed
|
||||
* @return this for chained calls
|
||||
*/
|
||||
public VectorInterface get(int i) {
|
||||
return points.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<VectorInterface> iterator() {
|
||||
return points.iterator();
|
||||
public Polygon setEvenOdd(boolean evenOdd) {
|
||||
this.evenOdd = evenOdd;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -140,28 +133,43 @@ public class Polygon implements Iterable<VectorInterface> {
|
||||
}
|
||||
|
||||
private boolean check(VectorInterface p1, VectorInterface p2) {
|
||||
if (closed)
|
||||
return false;
|
||||
|
||||
if (p1.equals(getFirst())) {
|
||||
points.add(0, p2);
|
||||
if (p2.equals(getLast()))
|
||||
closed = true;
|
||||
else {
|
||||
removeInitialMoveTo();
|
||||
path.add(0, new MoveTo(p2));
|
||||
}
|
||||
return true;
|
||||
} else if (p1.equals(getLast())) {
|
||||
points.add(p2);
|
||||
if (p2.equals(getFirst()))
|
||||
closed = true;
|
||||
else
|
||||
path.add(new LineTo(p2));
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
private void removeInitialMoveTo() {
|
||||
path.set(0, new LineTo(path.get(0)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the first point of the polygon
|
||||
*/
|
||||
public VectorInterface getFirst() {
|
||||
return points.get(0);
|
||||
return path.get(0).getPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the last point of the polygon
|
||||
*/
|
||||
public VectorInterface getLast() {
|
||||
return points.get(points.size() - 1);
|
||||
return path.get(path.size() - 1).getPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,10 +179,17 @@ public class Polygon implements Iterable<VectorInterface> {
|
||||
* @return this for chained calls
|
||||
*/
|
||||
public Polygon append(Polygon p2) {
|
||||
if (!p2.isBezierStart.isEmpty())
|
||||
if (hasSpecialElements || p2.hasSpecialElements)
|
||||
throw new RuntimeException("append of bezier not supported");
|
||||
for (int i = 1; i < p2.points.size(); i++)
|
||||
points.add(p2.points.get(i));
|
||||
|
||||
if (p2.getLast().equals(getFirst())) {
|
||||
for (int i = 1; i < p2.path.size() - 1; i++)
|
||||
add(p2.path.get(i).getPoint());
|
||||
closed = true;
|
||||
} else {
|
||||
for (int i = 1; i < p2.path.size(); i++)
|
||||
add(p2.path.get(i).getPoint());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -184,11 +199,11 @@ public class Polygon implements Iterable<VectorInterface> {
|
||||
* @return returns this polygon with reverse order of points
|
||||
*/
|
||||
public Polygon reverse() {
|
||||
if (!isBezierStart.isEmpty())
|
||||
throw new RuntimeException("reverse of bezier not supported");
|
||||
if (hasSpecialElements)
|
||||
throw new RuntimeException("append of bezier not supported");
|
||||
Polygon p = new Polygon(closed);
|
||||
for (int i = points.size() - 1; i >= 0; i--)
|
||||
p.add(points.get(i));
|
||||
for (int i = path.size() - 1; i >= 0; i--)
|
||||
p.add(path.get(i).getPoint());
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -202,43 +217,24 @@ public class Polygon implements Iterable<VectorInterface> {
|
||||
if (transform == Transform.IDENTITY)
|
||||
return this;
|
||||
|
||||
Polygon p = new Polygon(closed);
|
||||
for (VectorInterface v : points)
|
||||
p.add(v.transform(transform));
|
||||
p.isBezierStart.addAll(isBezierStart);
|
||||
Polygon p = new Polygon(closed).setEvenOdd(evenOdd);
|
||||
for (PathElement pe : path)
|
||||
p.add(pe.transform(transform));
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("M ");
|
||||
VectorInterface v = points.get(0);
|
||||
sb.append(str(v.getXFloat())).append(",").append(str(v.getYFloat())).append(" ");
|
||||
//modification of loop variable i is intended!
|
||||
//CHECKSTYLE.OFF: ModifiedControlVariable
|
||||
for (int i = 1; i < points.size(); i++) {
|
||||
v = points.get(i);
|
||||
if (isBezierStart.contains(i)) {
|
||||
sb.append("C ").append(str(v.getXFloat())).append(",").append(str(v.getYFloat())).append(" ");
|
||||
v = points.get(i + 1);
|
||||
sb.append(str(v.getXFloat())).append(",").append(str(v.getYFloat())).append(" ");
|
||||
v = points.get(i + 2);
|
||||
sb.append(str(v.getXFloat())).append(",").append(str(v.getYFloat())).append(" ");
|
||||
i += 2;
|
||||
} else
|
||||
sb.append("L ").append(str(v.getXFloat())).append(",").append(str(v.getYFloat())).append(" ");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (PathElement pe : path) {
|
||||
if (sb.length() > 0)
|
||||
sb.append(' ');
|
||||
sb.append(pe.toString());
|
||||
}
|
||||
//CHECKSTYLE.ON: ModifiedControlVariable
|
||||
if (closed)
|
||||
sb.append("z");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String str(float f) {
|
||||
if (f == Math.round(f))
|
||||
return Integer.toString(Math.round(f));
|
||||
else
|
||||
return Float.toString(f);
|
||||
if (closed)
|
||||
sb.append(" Z");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,4 +254,191 @@ public class Polygon implements Iterable<VectorInterface> {
|
||||
void setClosed(boolean closed) {
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<PathElement> iterator() {
|
||||
return path.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the actual path
|
||||
*/
|
||||
public void addClosePath() {
|
||||
add(new ClosePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a moveto to the path
|
||||
*
|
||||
* @param p the point to move to
|
||||
*/
|
||||
public void addMoveTo(VectorFloat p) {
|
||||
add(new MoveTo(p));
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw this polygon to a {@link Path2D} instance.
|
||||
*
|
||||
* @param path2d the Path2d instance.
|
||||
*/
|
||||
public void drawTo(Path2D path2d) {
|
||||
for (PathElement pe : path)
|
||||
pe.drawTo(path2d);
|
||||
if (closed)
|
||||
path2d.closePath();
|
||||
if (evenOdd)
|
||||
path2d.setWindingRule(Path2D.WIND_EVEN_ODD);
|
||||
}
|
||||
|
||||
/**
|
||||
* A element of the path
|
||||
*/
|
||||
public interface PathElement {
|
||||
/**
|
||||
* @return the coordinate of this path element
|
||||
*/
|
||||
VectorInterface getPoint();
|
||||
|
||||
/**
|
||||
* Returns the transormated path element
|
||||
*
|
||||
* @param transform the transformation
|
||||
* @return the transormated path element
|
||||
*/
|
||||
PathElement transform(Transform transform);
|
||||
|
||||
/**
|
||||
* Draws this path element to a Path2D instance.
|
||||
*
|
||||
* @param path2d the a Path2D instance
|
||||
*/
|
||||
void drawTo(Path2D path2d);
|
||||
}
|
||||
|
||||
private static String str(float f) {
|
||||
if (f == Math.round(f))
|
||||
return Integer.toString(Math.round(f));
|
||||
else
|
||||
return Float.toString(f);
|
||||
}
|
||||
|
||||
private static String str(VectorInterface p) {
|
||||
return str(p.getXFloat()) + "," + str(p.getYFloat());
|
||||
}
|
||||
|
||||
//LineTo can not be final because its overridden. Maybe checkstyle has a bug?
|
||||
//CHECKSTYLE.OFF: FinalClass
|
||||
private static class LineTo implements PathElement {
|
||||
protected final VectorInterface p;
|
||||
|
||||
private LineTo(VectorInterface p) {
|
||||
this.p = p;
|
||||
}
|
||||
|
||||
private LineTo(PathElement pathElement) {
|
||||
this(pathElement.getPoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VectorInterface getPoint() {
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathElement transform(Transform transform) {
|
||||
return new LineTo(p.transform(transform));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTo(Path2D path2d) {
|
||||
path2d.lineTo(p.getXFloat(), p.getYFloat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "L " + str(p);
|
||||
}
|
||||
}
|
||||
//CHECKSTYLE.ON: FinalClass
|
||||
|
||||
private static final class MoveTo extends LineTo {
|
||||
private MoveTo(VectorInterface p) {
|
||||
super(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "M " + str(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTo(Path2D path2d) {
|
||||
path2d.moveTo(p.getXFloat(), p.getYFloat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathElement transform(Transform transform) {
|
||||
return new MoveTo(p.transform(transform));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CurveTo implements PathElement {
|
||||
private final VectorInterface c1;
|
||||
private final VectorInterface c2;
|
||||
private final VectorInterface p;
|
||||
|
||||
private CurveTo(VectorInterface c1, VectorInterface c2, VectorInterface p) {
|
||||
this.c1 = c1;
|
||||
this.c2 = c2;
|
||||
this.p = p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VectorInterface getPoint() {
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathElement transform(Transform transform) {
|
||||
return new CurveTo(
|
||||
c1.transform(transform),
|
||||
c2.transform(transform),
|
||||
p.transform(transform)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "C " + str(c1) + " " + str(c2) + " " + str(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTo(Path2D path2d) {
|
||||
path2d.curveTo(c1.getXFloat(), c1.getYFloat(),
|
||||
c2.getXFloat(), c2.getYFloat(),
|
||||
p.getXFloat(), p.getYFloat());
|
||||
}
|
||||
}
|
||||
|
||||
private class ClosePath implements PathElement {
|
||||
@Override
|
||||
public VectorInterface getPoint() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathElement transform(Transform transform) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTo(Path2D path2d) {
|
||||
path2d.closePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Z";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,17 @@ public class PolygonConverter implements Converter {
|
||||
public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext marshallingContext) {
|
||||
Polygon p = (Polygon) o;
|
||||
writer.addAttribute("path", p.toString());
|
||||
writer.addAttribute("evenOdd", Boolean.toString(p.getEvenOdd()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext unmarshallingContext) {
|
||||
String path = reader.getAttribute("path");
|
||||
return Polygon.createFromPath(path);
|
||||
boolean evenOdd = Boolean.parseBoolean(reader.getAttribute("evenOdd"));
|
||||
final Polygon polygon = Polygon.createFromPath(path);
|
||||
if (polygon != null)
|
||||
polygon.setEvenOdd(evenOdd);
|
||||
return polygon;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -117,6 +117,7 @@ public class PolygonParser {
|
||||
public Polygon create() throws ParserException {
|
||||
Polygon p = new Polygon(false);
|
||||
Token tok;
|
||||
boolean closedPending = false;
|
||||
while ((tok = next()) != Token.EOF) {
|
||||
if (tok == Token.NUMBER) {
|
||||
unreadToken();
|
||||
@ -127,11 +128,19 @@ public class PolygonParser {
|
||||
}
|
||||
switch (command) {
|
||||
case 'M':
|
||||
p.add(nextVector());
|
||||
if (closedPending) {
|
||||
closedPending = false;
|
||||
p.addClosePath();
|
||||
}
|
||||
p.addMoveTo(nextVector());
|
||||
clearControl();
|
||||
break;
|
||||
case 'm':
|
||||
p.add(nextVectorInc());
|
||||
if (closedPending) {
|
||||
closedPending = false;
|
||||
p.addClosePath();
|
||||
}
|
||||
p.addMoveTo(nextVectorInc());
|
||||
clearControl();
|
||||
break;
|
||||
case 'V':
|
||||
@ -196,13 +205,15 @@ public class PolygonParser {
|
||||
break;
|
||||
case 'Z':
|
||||
case 'z':
|
||||
p.setClosed(true);
|
||||
closedPending = true;
|
||||
clearControl();
|
||||
break;
|
||||
default:
|
||||
throw new ParserException("unsupported path command " + command);
|
||||
}
|
||||
}
|
||||
if (closedPending)
|
||||
p.setClosed(true);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ public class GraphicLineCollector implements Graphic {
|
||||
|
||||
private void tryMerge(Polygon p1) {
|
||||
for (Polygon p2 : polyList)
|
||||
if (p1 != p2) {
|
||||
if (p1 != p2 && !p1.isClosed() && !p2.isClosed()) {
|
||||
if (p1.getLast().equals(p2.getFirst())) {
|
||||
p1.append(p2);
|
||||
polyList.remove(p2);
|
||||
|
@ -10,42 +10,46 @@ import junit.framework.TestCase;
|
||||
public class PolygonTest extends TestCase {
|
||||
|
||||
public void testPath() {
|
||||
checkLine(Polygon.createFromPath("m 10,10 L 20,10 20,20 10,20 z"));
|
||||
checkLine(Polygon.createFromPath("m 10,10 l 10,0 0,10 -10,0 z"));
|
||||
checkLine(Polygon.createFromPath("m 10,10 h 10 v 10 h -10 z"));
|
||||
checkLine(Polygon.createFromPath("m 10,10 H 20 V 20 H 10 z"));
|
||||
checkLine(Polygon.createFromPath("m 10,10 L 20,10 20,20 10,20 Z"));
|
||||
checkLine(Polygon.createFromPath("m 10,10 l 10,0 0,10 -10,0 Z"));
|
||||
checkLine(Polygon.createFromPath("m 10,10 h 10 v 10 h -10 Z"));
|
||||
checkLine(Polygon.createFromPath("m 10,10 H 20 V 20 H 10 Z"));
|
||||
}
|
||||
|
||||
private void checkLine(Polygon p) {
|
||||
checkCoor(p);
|
||||
assertEquals("M 10,10 L 20,10 L 20,20 L 10,20 z", p.toString());
|
||||
}
|
||||
|
||||
private void checkCoor(Polygon p) {
|
||||
assertEquals(4, p.size());
|
||||
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));
|
||||
assertNotNull(p);
|
||||
assertEquals("M 10,10 L 20,10 L 20,20 L 10,20 Z", p.toString());
|
||||
}
|
||||
|
||||
private void checkBezier(Polygon p) {
|
||||
checkCoor(p);
|
||||
assertEquals("M 10,10 C 20,10 20,20 10,20 z", p.toString());
|
||||
assertEquals("M 10,10 C 20,10 20,20 10,20 Z", p.toString());
|
||||
}
|
||||
|
||||
public void testBezierPath() {
|
||||
checkBezier(Polygon.createFromPath("m 10,10 C 20 10 20 20 10 20 z"));
|
||||
checkBezier(Polygon.createFromPath("m 10,10 c 10 0 10 10 0 10 z"));
|
||||
checkBezier(Polygon.createFromPath("m 10,10 C 20 10 20 20 10 20 Z"));
|
||||
checkBezier(Polygon.createFromPath("m 10,10 c 10 0 10 10 0 10 Z"));
|
||||
}
|
||||
|
||||
public void testCubicReflect() {
|
||||
assertEquals("M 0,0 C 0,10 10,10 10,0 C 10,-10 20,-10 20,0 C 20,10 30,10 30,0 z",
|
||||
Polygon.createFromPath("m 0,0 c 0,10 10,10 10,0 s 10,-10 10,0 s 10,10 10,0 z").toString());
|
||||
assertEquals("M 0,0 C 0,10 10,10 10,0 C 10,-10 20,-10 20,0 C 20,10 30,10 30,0 Z",
|
||||
Polygon.createFromPath("m 0,0 c 0,10 10,10 10,0 s 10,-10 10,0 s 10,10 10,0 Z").toString());
|
||||
}
|
||||
|
||||
public void testQuadraticReflect() {
|
||||
assertEquals("M 0,0 C 20,20 40,20 60,0 C 80,-20 100,-20 120,0 C 140,20 160,20 180,0 C 200,-20 220,-20 240,0 ",
|
||||
assertEquals("M 0,0 C 20,20 40,20 60,0 C 80,-20 100,-20 120,0 C 140,20 160,20 180,0 C 200,-20 220,-20 240,0",
|
||||
Polygon.createFromPath("m 0,0 Q 30,30 60,0 t 60,0 t 60,0 t 60,0").toString());
|
||||
}
|
||||
|
||||
public void testMultiPath() {
|
||||
assertEquals("M 0,0 C 0,40 20,60 60,60 C 100,60 120,40 120,0 C 120,-40 100,-60 60,-60 C 20,-60 0,-40 0,0 Z M 30,0 C 30,20 40,30 60,30 C 80,30 90,20 90,0 C 90,-20 80,-30 60,-30 C 40,-30 30,-20 30,0 Z",
|
||||
Polygon.createFromPath("M 0,0 Q 0,60 60,60 T 120,0 T 60,-60 T 0,0 z M 30,0 Q 30,30 60,30 T 90,0 T 60,-30 T 30,0 Z").toString());
|
||||
}
|
||||
|
||||
public void testAppend() {
|
||||
Polygon p1 = new Polygon(false).add(0, 0).add(0, 10).add(10, 10);
|
||||
Polygon p2 = new Polygon(false).add(10, 10).add(10, 0).add(0, 0);
|
||||
|
||||
assertEquals("M 0,0 L 0,10 L 10,10 L 10,0 Z", p1.append(p2).toString());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.linemerger;
|
||||
|
||||
import de.neemann.digital.draw.graphics.*;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GraphicLineCollectorTest extends TestCase {
|
||||
|
||||
public void testOpenSquare() {
|
||||
GraphicLineCollector col = new GraphicLineCollector();
|
||||
col.drawLine(new Vector(0, 0), new Vector(10, 0), Style.NORMAL);
|
||||
col.drawLine(new Vector(0, 10), new Vector(10, 10), Style.NORMAL);
|
||||
|
||||
col.drawLine(new Vector(0, 0), new Vector(0, 10), Style.NORMAL);
|
||||
|
||||
ArrayList<Polygon> poly = new ArrayList<>();
|
||||
col.drawTo(new MyGraphic(poly));
|
||||
|
||||
assertEquals(1, poly.size());
|
||||
assertEquals("M 10,0 L 0,0 L 0,10 L 10,10", poly.get(0).toString());
|
||||
}
|
||||
|
||||
public void testClosedSquare() {
|
||||
GraphicLineCollector col = new GraphicLineCollector();
|
||||
col.drawLine(new Vector(0, 0), new Vector(10, 0), Style.NORMAL);
|
||||
col.drawLine(new Vector(0, 10), new Vector(10, 10), Style.NORMAL);
|
||||
|
||||
col.drawLine(new Vector(0, 0), new Vector(0, 10), Style.NORMAL);
|
||||
col.drawLine(new Vector(10, 0), new Vector(10, 10), Style.NORMAL);
|
||||
|
||||
ArrayList<Polygon> poly = new ArrayList<>();
|
||||
col.drawTo(new MyGraphic(poly));
|
||||
|
||||
assertEquals(1, poly.size());
|
||||
assertEquals("M 10,0 L 0,0 L 0,10 L 10,10 Z", poly.get(0).toString());
|
||||
}
|
||||
|
||||
public void testClosedSquare2() {
|
||||
GraphicLineCollector col = new GraphicLineCollector();
|
||||
col.drawLine(new Vector(0, 0), new Vector(10, 0), Style.NORMAL);
|
||||
col.drawLine(new Vector(0, 10), new Vector(10, 10), Style.NORMAL);
|
||||
|
||||
col.drawLine(new Vector(0, 0), new Vector(0, 10), Style.NORMAL);
|
||||
col.drawLine(new Vector(10, 0), new Vector(10, 10), Style.NORMAL);
|
||||
|
||||
ArrayList<Polygon> poly = new ArrayList<>();
|
||||
col.drawTo(new MyGraphic(poly));
|
||||
|
||||
assertEquals(1, poly.size());
|
||||
assertEquals("M 10,0 L 0,0 L 0,10 L 10,10 Z", poly.get(0).toString());
|
||||
}
|
||||
|
||||
private static class MyGraphic implements Graphic {
|
||||
private final ArrayList<Polygon> poly;
|
||||
|
||||
private MyGraphic(ArrayList<Polygon> poly) {
|
||||
this.poly = poly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLine(VectorInterface p1, VectorInterface p2, Style style) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPolygon(Polygon p, Style style) {
|
||||
poly.add(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawCircle(VectorInterface p1, VectorInterface p2, Style style) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawText(VectorInterface p1, VectorInterface p2, String text, Orientation orientation, Style style) {
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user