mirror of
https://github.com/AppleCommander/bastools.git
synced 2025-04-06 14:39:28 +00:00
Early copy of bitmap -> vector transformation. #16.
This commit is contained in:
parent
208eeb8b51
commit
834e708356
@ -96,6 +96,9 @@ public class BitmapShape implements Shape {
|
||||
}
|
||||
return grid.get(y).get(x);
|
||||
}
|
||||
public Boolean get(Point point) {
|
||||
return get(point.x, point.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
@ -115,7 +118,112 @@ public class BitmapShape implements Shape {
|
||||
|
||||
@Override
|
||||
public VectorShape toVector() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
VectorShape vshape = new VectorShape();
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
Point pt = new Point(origin);
|
||||
// Relocate to 0,0
|
||||
while (pt.y > 0) {
|
||||
vshape.moveUp();
|
||||
pt.y -= 1;
|
||||
}
|
||||
while (pt.x > 0) {
|
||||
vshape.moveLeft();
|
||||
pt.x -= 1;
|
||||
}
|
||||
VectorMotion motion = new RightVectorMotion(pt, vshape);
|
||||
while (pt.y >= 0 && pt.y < height) {
|
||||
while (pt.x >= 0 && pt.x < width) {
|
||||
if (get(pt)) {
|
||||
motion.plot();
|
||||
} else {
|
||||
motion.move();
|
||||
}
|
||||
}
|
||||
motion = motion.changeDirection();
|
||||
}
|
||||
return vshape;
|
||||
}
|
||||
|
||||
// public static class Whatever {
|
||||
// private VectorCommand movement;
|
||||
// private VectorCommand nextRow;
|
||||
// private Point point;
|
||||
// private BitmapShape bitmapShape;
|
||||
// private VectorShape vectorShape;
|
||||
//
|
||||
// public Whatever(VectorCommand movement, VectorCommand nextRow, Point point, BitmapShape bitmapShape) {
|
||||
// this.movement = movement;
|
||||
// this.nextRow = nextRow;
|
||||
// this.point = point;
|
||||
// this.bitmapShape = bitmapShape;
|
||||
// this.vectorShape = new VectorShape();
|
||||
// }
|
||||
//
|
||||
// public VectorShape transform() {
|
||||
// findStartPosition();
|
||||
// while (hasMoreRows()) {
|
||||
// scanRow();
|
||||
// }
|
||||
// return vectorShape;
|
||||
// }
|
||||
// }
|
||||
|
||||
public interface VectorMotion {
|
||||
public void move();
|
||||
public void plot();
|
||||
public VectorMotion changeDirection();
|
||||
}
|
||||
public static class RightVectorMotion implements VectorMotion {
|
||||
private Point point;
|
||||
private VectorShape vshape;
|
||||
public RightVectorMotion(Point point, VectorShape vshape) {
|
||||
this.point = point;
|
||||
this.vshape = vshape;
|
||||
}
|
||||
@Override
|
||||
public void move() {
|
||||
point.x += 1;
|
||||
vshape.moveRight();
|
||||
}
|
||||
@Override
|
||||
public void plot() {
|
||||
point.x += 1;
|
||||
vshape.plotRight();
|
||||
}
|
||||
@Override
|
||||
public VectorMotion changeDirection() {
|
||||
point.x -= 1;
|
||||
point.y += 1;
|
||||
vshape.moveDown();
|
||||
vshape.moveLeft();
|
||||
return new LeftVectorMotion(point, vshape);
|
||||
}
|
||||
}
|
||||
public static class LeftVectorMotion implements VectorMotion {
|
||||
private Point point;
|
||||
private VectorShape vshape;
|
||||
public LeftVectorMotion(Point point, VectorShape vshape) {
|
||||
this.point = point;
|
||||
this.vshape = vshape;
|
||||
}
|
||||
@Override
|
||||
public void move() {
|
||||
point.x -= 1;
|
||||
vshape.moveLeft();
|
||||
}
|
||||
@Override
|
||||
public void plot() {
|
||||
point.x -= 1;
|
||||
vshape.plotLeft();
|
||||
}
|
||||
@Override
|
||||
public VectorMotion changeDirection() {
|
||||
point.x += 1;
|
||||
point.y += 1;
|
||||
vshape.moveDown();
|
||||
vshape.moveRight();
|
||||
return new RightVectorMotion(point, vshape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,4 +29,14 @@ public enum VectorCommand {
|
||||
this.ymove = (this.ordinal() & 0b011) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public VectorCommand opposite() {
|
||||
int newDirection = this.ordinal() ^ 0b010;
|
||||
return VectorCommand.values()[newDirection];
|
||||
}
|
||||
|
||||
public char shortCommand() {
|
||||
char shortCommand = "urdl".charAt(this.ordinal() & 0b011);
|
||||
return plot ? Character.toUpperCase(shortCommand) : shortCommand;
|
||||
}
|
||||
}
|
@ -9,6 +9,9 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class VectorShape implements Shape {
|
||||
public static VectorShape from(ByteBuffer buf) {
|
||||
@ -53,6 +56,76 @@ public class VectorShape implements Shape {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize the vectors by removing useless vectors or replacing a series with a shorter series.
|
||||
* At this point, everything is based off of a regex with a potential modification.
|
||||
*/
|
||||
public VectorShape optimize() {
|
||||
String commands = toShortCommands();
|
||||
Function<String,String> opts =
|
||||
// Unused moves (left followed by a right with no plotting in between, for instance).
|
||||
VectorRegexOptimization.of("l([ud]*)r")
|
||||
.andThen(VectorRegexOptimization.of("r([ud]*)l"))
|
||||
.andThen(VectorRegexOptimization.of("u([rl]*)d"))
|
||||
.andThen(VectorRegexOptimization.of("d([rl]*)u"))
|
||||
// These are plot/move combinations, such as LEFT>up>right that can be replaced by just UP.
|
||||
.andThen(VectorRegexOptimization.of("L([ud])r", String::toUpperCase))
|
||||
.andThen(VectorRegexOptimization.of("R([ud])l", String::toUpperCase))
|
||||
.andThen(VectorRegexOptimization.of("U([rl])d", String::toUpperCase))
|
||||
.andThen(VectorRegexOptimization.of("D([rl])u", String::toUpperCase))
|
||||
// Base assumption is that any tail moves can be removed as they don't lead to a plot.
|
||||
.andThen(VectorRegexOptimization.of("()[udlr]+$"));
|
||||
|
||||
String oldCommands = null;
|
||||
do {
|
||||
oldCommands = commands;
|
||||
commands = opts.apply(commands);
|
||||
} while (!oldCommands.equals(commands));
|
||||
|
||||
VectorShape newShape = new VectorShape();
|
||||
newShape.appendShortCommands(commands);
|
||||
return newShape;
|
||||
}
|
||||
|
||||
/**
|
||||
* A vector optimization based on regex. Transformation is optional to (for instance) change a {@code move}
|
||||
* to a {@code plot} command. Note that the regex requires a matcher group; also be aware that an empty group
|
||||
* "{@code ()}" is a viable solution.
|
||||
*/
|
||||
public static class VectorRegexOptimization implements Function<String,String> {
|
||||
public static Function<String,String> of(String regex, Function<String,String> transformation) {
|
||||
VectorRegexOptimization opt = new VectorRegexOptimization();
|
||||
opt.pattern = Pattern.compile(regex);
|
||||
opt.fn = transformation;
|
||||
return opt;
|
||||
}
|
||||
public static Function<String,String> of(String regex) {
|
||||
return of(regex, (s) -> s);
|
||||
}
|
||||
|
||||
private Pattern pattern;
|
||||
private Function<String,String> fn;
|
||||
|
||||
private VectorRegexOptimization() { /* Prevent construction */ }
|
||||
|
||||
@Override
|
||||
public String apply(String shortCommands) {
|
||||
Matcher matcher = pattern.matcher(shortCommands);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (matcher.find()) {
|
||||
matcher.appendReplacement(sb, fn.apply(matcher.group(1)));
|
||||
}
|
||||
matcher.appendTail(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public String toShortCommands() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
vectors.stream().map(VectorCommand::shortCommand).forEach(sb::append);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void appendShortCommands(String line) {
|
||||
for (char cmd : line.trim().toCharArray()) {
|
||||
switch (cmd) {
|
||||
|
@ -17,7 +17,7 @@ public class ShapesTest {
|
||||
/**
|
||||
* These shape vectors are taken from the Applesoft BASIC Programmer's Reference Manual (1987), p146.
|
||||
*/
|
||||
public static VectorShape drawStandardBoxShape() {
|
||||
public VectorShape drawStandardBoxShape() {
|
||||
return new VectorShape()
|
||||
.moveDown().moveDown()
|
||||
.plotLeft().plotLeft()
|
||||
@ -27,6 +27,17 @@ public class ShapesTest {
|
||||
.moveLeft().plotLeft();
|
||||
|
||||
}
|
||||
public BitmapShape plotStandardBoxShape() {
|
||||
BitmapShape boxShape = new BitmapShape(5, 5);
|
||||
for (int i=1; i<=3; i++) {
|
||||
boxShape.plot(i, 0);
|
||||
boxShape.plot(i, 4);
|
||||
boxShape.plot(0, i);
|
||||
boxShape.plot(4, i);
|
||||
}
|
||||
boxShape.origin.setLocation(2, 2);
|
||||
return boxShape;
|
||||
}
|
||||
|
||||
public ShapeTable readStandardShapeTable() {
|
||||
ShapeTable st = ShapeTable.read(BOX_SAMPLE);
|
||||
@ -68,18 +79,35 @@ public class ShapesTest {
|
||||
public void testStandardShapeTableBitmap() {
|
||||
ShapeTable st = readStandardShapeTable();
|
||||
|
||||
BitmapShape expected = new BitmapShape(5, 5);
|
||||
for (int i=1; i<=3; i++) {
|
||||
expected.plot(i, 0);
|
||||
expected.plot(i, 4);
|
||||
expected.plot(0, i);
|
||||
expected.plot(4, i);
|
||||
}
|
||||
BitmapShape expected = plotStandardBoxShape();
|
||||
|
||||
Shape s = st.shapes.get(0);
|
||||
assertEquals(expected.grid, s.toBitmap().grid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToVectorFromBitmap() throws IOException {
|
||||
BitmapShape bitmapShape = plotStandardBoxShape();
|
||||
|
||||
VectorShape vectorShape = bitmapShape.toVector();
|
||||
BitmapShape newBitmapShape = vectorShape.toBitmap();
|
||||
|
||||
ShapeExporter exp = ShapeExporter.text().asciiTextBorder().build();
|
||||
System.out.println("Original/expected:");
|
||||
exp.export(bitmapShape, System.out);
|
||||
System.out.println("Transformed/actual:");
|
||||
exp.export(newBitmapShape, System.out);
|
||||
System.out.println("Transformed vectors:");
|
||||
System.out.println(vectorShape.vectors);
|
||||
System.out.println(vectorShape.vectors.size());
|
||||
System.out.println("Optimized vectors:");
|
||||
System.out.println(vectorShape.optimize().vectors);
|
||||
System.out.println(vectorShape.optimize().vectors.size());
|
||||
exp.export(vectorShape.optimize().toBitmap(), System.out);
|
||||
|
||||
assertEquals(bitmapShape.grid, newBitmapShape.grid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTextShapeExporterNoBorder() throws IOException {
|
||||
ShapeTable st = readStandardShapeTable();
|
||||
|
@ -29,4 +29,16 @@ public class VectorCommandTest {
|
||||
assertEquals(plot, command.plot);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpposite() {
|
||||
test(VectorCommand.MOVE_DOWN, VectorCommand.MOVE_UP);
|
||||
test(VectorCommand.MOVE_LEFT, VectorCommand.MOVE_RIGHT);
|
||||
test(VectorCommand.PLOT_DOWN, VectorCommand.PLOT_UP);
|
||||
test(VectorCommand.PLOT_LEFT, VectorCommand.PLOT_RIGHT);
|
||||
}
|
||||
public void test(VectorCommand a, VectorCommand b) {
|
||||
assertEquals(a, b.opposite());
|
||||
assertEquals(b, a.opposite());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user