Add a second page with the solution to the PDF.
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Manuel Friedli 2020-10-03 23:09:50 +02:00
parent 74441c91cc
commit 911aa35577
2 changed files with 82 additions and 22 deletions

View file

@ -13,7 +13,9 @@ public class Labyrinth {
private final int width; private final int width;
@Getter @Getter
private final int height; private final int height;
@Getter
private final Position start; private final Position start;
@Getter
private final Position end; private final Position end;
public Labyrinth(final int width, final int height) { public Labyrinth(final int width, final int height) {

View file

@ -5,12 +5,16 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.jetbrains.annotations.Nullable;
import java.awt.*; import java.awt.*;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
public class PDFRenderer implements Renderer<byte[]> { public class PDFRenderer implements Renderer<byte[]> {
private static final float MARGIN = 10;
private static final float SCALE = 10;
private PDFRenderer() { private PDFRenderer() {
} }
@ -20,13 +24,15 @@ public class PDFRenderer implements Renderer<byte[]> {
return new PDFRenderer(); return new PDFRenderer();
} }
private static boolean isValid(@NonNull final Position position) {
return position.getX() >= 0 && position.getY() >= 0;
}
@Override @Override
@NonNull @NonNull
public byte[] render(@NonNull final Labyrinth labyrinth) { public byte[] render(@NonNull final Labyrinth labyrinth) {
final float scale = 10; final float pageWidth = labyrinth.getWidth() * SCALE + 2 * MARGIN;
final float margin = 5; final float pageHeight = labyrinth.getHeight() * SCALE + 2 * MARGIN;
final float pageWidth = labyrinth.getWidth() * scale + 2 * margin;
final float pageHeight = labyrinth.getHeight() * scale + 2 * margin;
final PDDocument pdDocument = new PDDocument(); final PDDocument pdDocument = new PDDocument();
final PDPage page = new PDPage(new PDRectangle(pageWidth, pageHeight)); final PDPage page = new PDPage(new PDRectangle(pageWidth, pageHeight));
@ -37,8 +43,22 @@ public class PDFRenderer implements Renderer<byte[]> {
pdPageContentStream.setLineWidth(1.0f); pdPageContentStream.setLineWidth(1.0f);
pdPageContentStream.setStrokingColor(Color.BLACK); pdPageContentStream.setStrokingColor(Color.BLACK);
pdPageContentStream.setNonStrokingColor(Color.BLACK); pdPageContentStream.setNonStrokingColor(Color.BLACK);
this.drawHorizonzalLines(labyrinth, pdPageContentStream, margin, scale); this.drawHorizonzalLines(labyrinth, pdPageContentStream);
this.drawVerticalLines(labyrinth, pdPageContentStream, margin, scale); this.drawVerticalLines(labyrinth, pdPageContentStream);
} catch (IOException e) {
e.printStackTrace();
}
final PDPage solution = new PDPage(new PDRectangle(pageWidth, pageHeight));
pdDocument.addPage(solution);
try (PDPageContentStream pdPageContentStream = new PDPageContentStream(pdDocument, solution)) {
pdPageContentStream.setLineCapStyle(BasicStroke.CAP_ROUND);
pdPageContentStream.setLineJoinStyle(BasicStroke.JOIN_MITER);
pdPageContentStream.setLineWidth(1.0f);
pdPageContentStream.setStrokingColor(Color.BLACK);
pdPageContentStream.setNonStrokingColor(Color.BLACK);
this.drawHorizonzalLines(labyrinth, pdPageContentStream);
this.drawVerticalLines(labyrinth, pdPageContentStream);
this.drawSolution(labyrinth, pdPageContentStream);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -52,28 +72,29 @@ public class PDFRenderer implements Renderer<byte[]> {
return output.toByteArray(); return output.toByteArray();
} }
private void drawHorizonzalLines(@NonNull final Labyrinth labyrinth, @NonNull final PDPageContentStream pdPageContentStream, final float margin, final float scale) throws IOException { private void drawHorizonzalLines(@NonNull final Labyrinth labyrinth,
@NonNull final PDPageContentStream pdPageContentStream) throws IOException {
// PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required. // PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
final float labyrinthHeight = labyrinth.getHeight() * scale; final float labyrinthHeight = labyrinth.getHeight() * SCALE;
for (int y = 0; y < labyrinth.getHeight(); y++) { for (int y = 0; y < labyrinth.getHeight(); y++) {
boolean isPainting = false; boolean isPainting = false;
for (int x = 0; x < labyrinth.getWidth(); x++) { for (int x = 0; x < labyrinth.getWidth(); x++) {
final Tile currentTile = labyrinth.getTileAt(x, y); final Tile currentTile = labyrinth.getTileAt(x, y);
if (currentTile.hasWallAt(Direction.TOP)) { if (currentTile.hasWallAt(Direction.TOP)) {
if (!isPainting) { if (!isPainting) {
pdPageContentStream.moveTo(x * scale + margin, labyrinthHeight - y * scale + margin); pdPageContentStream.moveTo(x * SCALE + MARGIN, labyrinthHeight - y * SCALE + MARGIN);
isPainting = true; isPainting = true;
} }
} else { } else {
if (isPainting) { if (isPainting) {
pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - y * scale + margin); pdPageContentStream.lineTo(x * SCALE + MARGIN, labyrinthHeight - y * SCALE + MARGIN);
pdPageContentStream.stroke(); pdPageContentStream.stroke();
isPainting = false; isPainting = false;
} }
} }
} }
if (isPainting) { if (isPainting) {
pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, labyrinthHeight - y * scale + margin); pdPageContentStream.lineTo(labyrinth.getWidth() * SCALE + MARGIN, labyrinthHeight - y * SCALE + MARGIN);
pdPageContentStream.stroke(); pdPageContentStream.stroke();
} }
} }
@ -83,45 +104,46 @@ public class PDFRenderer implements Renderer<byte[]> {
final Tile currentTile = labyrinth.getTileAt(x, y - 1); final Tile currentTile = labyrinth.getTileAt(x, y - 1);
if (currentTile.hasWallAt(Direction.BOTTOM)) { if (currentTile.hasWallAt(Direction.BOTTOM)) {
if (!isPainting) { if (!isPainting) {
pdPageContentStream.moveTo(x * scale + margin, labyrinthHeight - y * scale + margin); pdPageContentStream.moveTo(x * SCALE + MARGIN, labyrinthHeight - y * SCALE + MARGIN);
isPainting = true; isPainting = true;
} }
} else { } else {
if (isPainting) { if (isPainting) {
pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - y * scale + margin); pdPageContentStream.lineTo(x * SCALE + MARGIN, labyrinthHeight - y * SCALE + MARGIN);
pdPageContentStream.stroke(); pdPageContentStream.stroke();
isPainting = false; isPainting = false;
} }
} }
} }
if (isPainting) { if (isPainting) {
pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, labyrinthHeight - labyrinth.getHeight() * scale + margin); pdPageContentStream.lineTo(labyrinth.getWidth() * SCALE + MARGIN, labyrinthHeight - labyrinth.getHeight() * SCALE + MARGIN);
pdPageContentStream.stroke(); pdPageContentStream.stroke();
} }
} }
private void drawVerticalLines(@NonNull final Labyrinth labyrinth, @NonNull final PDPageContentStream pdPageContentStream, final float margin, final float scale) throws IOException { private void drawVerticalLines(@NonNull final Labyrinth labyrinth,
@NonNull final PDPageContentStream pdPageContentStream) throws IOException {
// PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required. // PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
final float labyrinthHeight = labyrinth.getHeight() * scale; final float labyrinthHeight = labyrinth.getHeight() * SCALE;
for (int x = 0; x < labyrinth.getWidth(); x++) { for (int x = 0; x < labyrinth.getWidth(); x++) {
boolean isPainting = false; boolean isPainting = false;
for (int y = 0; y < labyrinth.getHeight(); y++) { for (int y = 0; y < labyrinth.getHeight(); y++) {
final Tile currentTile = labyrinth.getTileAt(x, y); final Tile currentTile = labyrinth.getTileAt(x, y);
if (currentTile.hasWallAt(Direction.LEFT)) { if (currentTile.hasWallAt(Direction.LEFT)) {
if (!isPainting) { if (!isPainting) {
pdPageContentStream.moveTo(x * scale + margin, labyrinthHeight - y * scale + margin); pdPageContentStream.moveTo(x * SCALE + MARGIN, labyrinthHeight - y * SCALE + MARGIN);
isPainting = true; isPainting = true;
} }
} else { } else {
if (isPainting) { if (isPainting) {
pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - y * scale + margin); pdPageContentStream.lineTo(x * SCALE + MARGIN, labyrinthHeight - y * SCALE + MARGIN);
pdPageContentStream.stroke(); pdPageContentStream.stroke();
isPainting = false; isPainting = false;
} }
} }
} }
if (isPainting) { if (isPainting) {
pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - labyrinth.getHeight() * scale + margin); pdPageContentStream.lineTo(x * SCALE + MARGIN, labyrinthHeight - labyrinth.getHeight() * SCALE + MARGIN);
pdPageContentStream.stroke(); pdPageContentStream.stroke();
} }
} }
@ -131,20 +153,56 @@ public class PDFRenderer implements Renderer<byte[]> {
final Tile currentTile = labyrinth.getTileAt(x - 1, y); final Tile currentTile = labyrinth.getTileAt(x - 1, y);
if (currentTile.hasWallAt(Direction.RIGHT)) { if (currentTile.hasWallAt(Direction.RIGHT)) {
if (!isPainting) { if (!isPainting) {
pdPageContentStream.moveTo(x * scale + margin, labyrinthHeight - y * scale + margin); pdPageContentStream.moveTo(x * SCALE + MARGIN, labyrinthHeight - y * SCALE + MARGIN);
isPainting = true; isPainting = true;
} }
} else { } else {
if (isPainting) { if (isPainting) {
pdPageContentStream.lineTo(x * scale + margin, labyrinthHeight - y * scale + margin); pdPageContentStream.lineTo(x * SCALE + MARGIN, labyrinthHeight - y * SCALE + MARGIN);
pdPageContentStream.stroke(); pdPageContentStream.stroke();
isPainting = false; isPainting = false;
} }
} }
} }
if (isPainting) { if (isPainting) {
pdPageContentStream.lineTo(labyrinth.getWidth() * scale + margin, labyrinthHeight - labyrinth.getHeight() * scale + margin); pdPageContentStream.lineTo(labyrinth.getWidth() * SCALE + MARGIN, labyrinthHeight - labyrinth.getHeight() * SCALE + MARGIN);
pdPageContentStream.stroke(); pdPageContentStream.stroke();
} }
} }
private void drawSolution(@NonNull final Labyrinth labyrinth,
@NonNull final PDPageContentStream pdPageContentStream) throws IOException {
// Draw the solution in red
pdPageContentStream.setStrokingColor(Color.RED);
// PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
final float labyrinthHeight = labyrinth.getHeight() * SCALE;
final Position end = labyrinth.getEnd();
Position currentPosition = labyrinth.getStart();
Position previousPosition = null;
pdPageContentStream.moveTo(MARGIN + currentPosition.getX() * SCALE + SCALE / 2, MARGIN + labyrinthHeight - (currentPosition.getY() * SCALE + SCALE / 2));
do {
Position newCurrent = this.findNextSolutionPosition(labyrinth, previousPosition, currentPosition);
previousPosition = currentPosition;
currentPosition = newCurrent;
pdPageContentStream.lineTo(MARGIN + currentPosition.getX() * SCALE + SCALE / 2, MARGIN + labyrinthHeight - (currentPosition.getY() * SCALE + SCALE / 2));
} while (!currentPosition.equals(end));
pdPageContentStream.stroke();
}
private Position findNextSolutionPosition(@NonNull final Labyrinth labyrinth, @Nullable final Position previousPosition, @NonNull final Position currentPosition) {
final Tile currentTile = labyrinth.getTileAt(currentPosition);
for (final Direction direction : Direction.values()) {
if (!currentTile.hasWallAt(direction)) {
final Position position = direction.translate(currentPosition);
if (position.equals(previousPosition) || !isValid(position)) {
continue;
}
if (labyrinth.getTileAt(position).isSolution()) {
return position;
}
}
}
// We *SHOULD* never get here. ... famous last words ...
return null;
}
} }