mirror of
https://github.com/AppleCommander/bastools.git
synced 2024-12-22 14:29:22 +00:00
Extract command now supports various ranges for shape numbers as well as
exporting to code. #16.
This commit is contained in:
parent
09fd3a4bca
commit
f025fef9bb
@ -8,6 +8,7 @@ import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.github.applecommander.bastools.api.shapes.exporters.ImageShapeExporter;
|
||||
import io.github.applecommander.bastools.api.shapes.exporters.SourceShapeExporter;
|
||||
import io.github.applecommander.bastools.api.shapes.exporters.TextShapeExporter;
|
||||
|
||||
public interface ShapeExporter {
|
||||
@ -51,4 +52,7 @@ public interface ShapeExporter {
|
||||
public static ImageShapeExporter.Builder image() {
|
||||
return new ImageShapeExporter.Builder();
|
||||
}
|
||||
public static SourceShapeExporter.Builder source() {
|
||||
return new SourceShapeExporter.Builder();
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ public enum VectorCommand {
|
||||
public final int ymove;
|
||||
|
||||
public final char shortCommand;
|
||||
public final String longCommand;
|
||||
public final boolean vertical;
|
||||
public final boolean horizontal;
|
||||
|
||||
@ -37,6 +38,8 @@ public enum VectorCommand {
|
||||
|
||||
char shortCommand = "urdl".charAt(this.ordinal() & 0b011);
|
||||
this.shortCommand = plot ? Character.toUpperCase(shortCommand) : shortCommand;
|
||||
|
||||
this.longCommand = this.name().replaceAll("_", "").toLowerCase();
|
||||
}
|
||||
|
||||
public VectorCommand opposite() {
|
||||
|
@ -0,0 +1,135 @@
|
||||
package io.github.applecommander.bastools.api.shapes.exporters;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import io.github.applecommander.bastools.api.shapes.Shape;
|
||||
import io.github.applecommander.bastools.api.shapes.ShapeExporter;
|
||||
import io.github.applecommander.bastools.api.shapes.ShapeTable;
|
||||
import io.github.applecommander.bastools.api.shapes.VectorCommand;
|
||||
|
||||
public class SourceShapeExporter implements ShapeExporter {
|
||||
private BiConsumer<Shape,PrintWriter> formatFunction = this::exportShapeAsBitmap;
|
||||
private ShapeExporter textExporter;
|
||||
private boolean skipEmptyShapes;
|
||||
|
||||
/** Use the {@code Builder} to create a TextShapeExporter. */
|
||||
private SourceShapeExporter() {
|
||||
this.textExporter = ShapeExporter.text().noBorder().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void export(Shape shape, OutputStream outputStream) throws IOException {
|
||||
PrintWriter pw = new PrintWriter(outputStream);
|
||||
formatFunction.accept(shape, pw);
|
||||
pw.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void export(ShapeTable shapeTable, OutputStream outputStream) throws IOException {
|
||||
PrintWriter pw = new PrintWriter(outputStream);
|
||||
shapeTable.shapes.stream()
|
||||
.filter(this::displayThisShape)
|
||||
.forEach(shape -> formatFunction.accept(shape, pw));
|
||||
pw.flush();
|
||||
}
|
||||
|
||||
private boolean displayThisShape(Shape shape) {
|
||||
return !(skipEmptyShapes && shape.isEmpty());
|
||||
}
|
||||
|
||||
public void exportShapeAsBitmap(Shape shape, PrintWriter pw) {
|
||||
try {
|
||||
pw.printf(".bitmap\n");
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
textExporter.export(shape, new PaddedOutputStream(os, " "));
|
||||
pw.print(new String(os.toByteArray()));
|
||||
pw.printf("\n");
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void exportShapeAsShortCommands(Shape shape, PrintWriter pw) {
|
||||
pw.printf(".short\n");
|
||||
pw.printf(" %s\n", shape.toVector().toShortCommands());
|
||||
pw.printf("\n");
|
||||
}
|
||||
|
||||
public void exportShapeAsLongCommands(Shape shape, PrintWriter pw) {
|
||||
pw.printf(".long\n");
|
||||
Queue<VectorCommand> vectors = new LinkedList<>(shape.toVector().vectors);
|
||||
while (!vectors.isEmpty()) {
|
||||
VectorCommand vector = vectors.remove();
|
||||
int count = 1;
|
||||
while (vectors.peek() == vector) {
|
||||
vectors.remove();
|
||||
count += 1;
|
||||
}
|
||||
if (count == 1) {
|
||||
pw.printf(" %s\n", vector.longCommand);
|
||||
} else {
|
||||
pw.printf(" %s %d\n", vector.longCommand, count);
|
||||
}
|
||||
}
|
||||
pw.printf("\n");
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private SourceShapeExporter exporter = new SourceShapeExporter();
|
||||
|
||||
public Builder bitmap() {
|
||||
exporter.formatFunction = exporter::exportShapeAsBitmap;
|
||||
return this;
|
||||
}
|
||||
public Builder shortCommands() {
|
||||
exporter.formatFunction = exporter::exportShapeAsShortCommands;
|
||||
return this;
|
||||
}
|
||||
public Builder longCommands() {
|
||||
exporter.formatFunction = exporter::exportShapeAsLongCommands;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder skipEmptyShapes() {
|
||||
return skipEmptyShapes(true);
|
||||
}
|
||||
public Builder skipEmptyShapes(boolean skipEmptyShapes) {
|
||||
exporter.skipEmptyShapes = skipEmptyShapes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShapeExporter build() {
|
||||
return exporter;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PaddedOutputStream extends OutputStream {
|
||||
private OutputStream wrappedStream;
|
||||
private boolean needPadding = true;
|
||||
private byte[] padding;
|
||||
|
||||
public PaddedOutputStream(OutputStream outputStream, String padding) {
|
||||
Objects.requireNonNull(outputStream);
|
||||
Objects.requireNonNull(padding);
|
||||
this.wrappedStream = outputStream;
|
||||
this.padding = padding.getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
if (needPadding) {
|
||||
wrappedStream.write(padding);
|
||||
}
|
||||
needPadding = (b == '\n');
|
||||
wrappedStream.write(b);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,14 @@
|
||||
package io.github.applecommander.bastools.tools.st;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.github.applecommander.bastools.api.shapes.Shape;
|
||||
import io.github.applecommander.bastools.api.shapes.ShapeExporter;
|
||||
@ -34,17 +39,21 @@ public class ExtractCommand implements Callable<Void> {
|
||||
@Option(names = "--border", description = "Set border style (none, simple, box)", showDefaultValue = Visibility.ALWAYS)
|
||||
private String borderStyle = "simple";
|
||||
|
||||
@Option(names = "--format", description = "Select output format (text, png, gif, jpeg, bmp, wbmp)", showDefaultValue = Visibility.ALWAYS)
|
||||
@Option(names = "--format", description = "Select output format (text, source, png, gif, jpeg, bmp, wbmp)", showDefaultValue = Visibility.ALWAYS)
|
||||
private String outputFormat = "text";
|
||||
|
||||
@Option(names = "--coding", description = "Select source style (bitmap, long, short)", showDefaultValue = Visibility.ALWAYS)
|
||||
private String codeStyle;
|
||||
|
||||
@Option(names = "--skip-empty", description = "Skip empty shapes")
|
||||
private boolean skipEmptyShapesFlag = false;
|
||||
|
||||
@Option(names = { "-w", "--width" }, description = "Set width (defaults: text=80, image=1024)")
|
||||
private int width = -1;
|
||||
|
||||
@Option(names = "--shape", description = "Extract specific shape")
|
||||
private int shapeNum = 0;
|
||||
@Option(names = "--shapes", description = "Extract specific shape(s); formats are '1' or '1-4' and can be combined with a comma",
|
||||
converter = IntegerRangeTypeConverter.class)
|
||||
private List<Integer> shapeNums = new ArrayList<>();
|
||||
|
||||
@Parameters(arity = "0..1", description = "File to process")
|
||||
private Path inputFile;
|
||||
@ -57,23 +66,34 @@ public class ExtractCommand implements Callable<Void> {
|
||||
|
||||
ShapeTable shapeTable = stdinFlag ? ShapeTable.read(System.in) : ShapeTable.read(inputFile);
|
||||
|
||||
if (shapeNum > 0) {
|
||||
if (shapeNum <= shapeTable.shapes.size()) {
|
||||
Shape shape = shapeTable.shapes.get(shapeNum-1);
|
||||
if (stdoutFlag) {
|
||||
exporter.export(shape, System.out);
|
||||
} else {
|
||||
exporter.export(shape, outputFile);
|
||||
}
|
||||
} else {
|
||||
throw new IOException("Invalid shape number");
|
||||
}
|
||||
} else {
|
||||
if (shapeNums.isEmpty()) {
|
||||
if (stdoutFlag) {
|
||||
exporter.export(shapeTable, System.out);
|
||||
} else {
|
||||
exporter.export(shapeTable, outputFile);
|
||||
}
|
||||
} else {
|
||||
List<Integer> outOfRange = shapeNums.stream()
|
||||
.filter(n -> n > shapeTable.shapes.size())
|
||||
.collect(Collectors.toList());
|
||||
if (!outOfRange.isEmpty()) {
|
||||
throw new IOException("Invalid shape numbers: " + outOfRange);
|
||||
}
|
||||
|
||||
OutputStream outputStream = System.out;
|
||||
try {
|
||||
if (outputFile != null) {
|
||||
outputStream = Files.newOutputStream(outputFile);
|
||||
}
|
||||
for (int shapeNum : shapeNums) {
|
||||
Shape shape = shapeTable.shapes.get(shapeNum - 1);
|
||||
exporter.export(shape, outputStream);
|
||||
}
|
||||
} finally {
|
||||
if (outputFile != null) {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -127,6 +147,30 @@ public class ExtractCommand implements Callable<Void> {
|
||||
.skipEmptyShapes(skipEmptyShapesFlag)
|
||||
.build();
|
||||
break;
|
||||
case "source":
|
||||
switch (codeStyle) {
|
||||
case "bitmap":
|
||||
exporter = ShapeExporter.source()
|
||||
.bitmap()
|
||||
.skipEmptyShapes(skipEmptyShapesFlag)
|
||||
.build();
|
||||
break;
|
||||
case "short":
|
||||
exporter = ShapeExporter.source()
|
||||
.shortCommands()
|
||||
.skipEmptyShapes(skipEmptyShapesFlag)
|
||||
.build();
|
||||
break;
|
||||
case "long":
|
||||
exporter = ShapeExporter.source()
|
||||
.longCommands()
|
||||
.skipEmptyShapes(skipEmptyShapesFlag)
|
||||
.build();
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Please select a valid code style");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Please select a valid output format");
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package io.github.applecommander.bastools.tools.st;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import picocli.CommandLine.ITypeConverter;
|
||||
|
||||
public class IntegerRangeTypeConverter implements ITypeConverter<List<Integer>> {
|
||||
@Override
|
||||
public List<Integer> convert(String value) throws Exception {
|
||||
List<Integer> list = new ArrayList<>();
|
||||
String[] parts = value.split(",");
|
||||
for (String part : parts) {
|
||||
String[] range = part.split("-");
|
||||
if (range.length == 1) {
|
||||
list.add(Integer.parseInt(range[0]));
|
||||
} else if (range.length == 2) {
|
||||
int i0 = Integer.parseInt(range[0]);
|
||||
int i1 = Integer.parseInt(range[1]);
|
||||
for (int i = i0; i <= i1; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Expecting a single integer or two integers for a range");
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user