Refactor the PDFRenderer

This commit is contained in:
Manuel Friedli 2020-10-04 22:47:13 +02:00
parent 66ddb27291
commit 0bdbd7d0ef

View file

@ -1,6 +1,7 @@
package ch.fritteli.labyrinth; package ch.fritteli.labyrinth;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value; import lombok.Value;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation; import org.apache.pdfbox.pdmodel.PDDocumentInformation;
@ -26,19 +27,30 @@ public class PDFRenderer implements Renderer<byte[]> {
return new PDFRenderer(); return new PDFRenderer();
} }
@Override
@NonNull
public byte[] render(@NonNull final Labyrinth labyrinth) {
final Generator generator = new Generator(labyrinth);
return generator.generate();
}
@RequiredArgsConstructor
private static class Generator {
@NonNull
private final Labyrinth labyrinth;
private static boolean isValid(@NonNull final Position position) { private static boolean isValid(@NonNull final Position position) {
return position.getX() >= 0 && position.getY() >= 0; return position.getX() >= 0 && position.getY() >= 0;
} }
@Override public byte[] generate() {
@NonNull final float pageWidth = this.labyrinth.getWidth() * SCALE + 2 * MARGIN;
public byte[] render(@NonNull final Labyrinth labyrinth) { final float pageHeight = this.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 PDDocumentInformation info = new PDDocumentInformation(); final PDDocumentInformation info = new PDDocumentInformation();
info.setTitle("Labyrinth " + labyrinth.getWidth() + "x" + labyrinth.getHeight() + ", ID " + labyrinth.getRandomSeed()); info.setTitle("Labyrinth " + this.labyrinth.getWidth() + "x" + this.labyrinth.getHeight() + ", ID " + this.labyrinth.getRandomSeed());
pdDocument.setDocumentInformation(info); pdDocument.setDocumentInformation(info);
final PDPage page = new PDPage(new PDRectangle(pageWidth, pageHeight)); final PDPage page = new PDPage(new PDRectangle(pageWidth, pageHeight));
final PDPage solution = new PDPage(new PDRectangle(pageWidth, pageHeight)); final PDPage solution = new PDPage(new PDRectangle(pageWidth, pageHeight));
@ -48,9 +60,9 @@ public class PDFRenderer implements Renderer<byte[]> {
final PDPageContentStream solutionPageContentStream = new PDPageContentStream(pdDocument, solution)) { final PDPageContentStream solutionPageContentStream = new PDPageContentStream(pdDocument, solution)) {
setUpPageContentStream(labyrinthPageContentStream); setUpPageContentStream(labyrinthPageContentStream);
setUpPageContentStream(solutionPageContentStream); setUpPageContentStream(solutionPageContentStream);
this.drawHorizonzalLines(labyrinth, labyrinthPageContentStream, solutionPageContentStream); this.drawHorizonzalLines(labyrinthPageContentStream, solutionPageContentStream);
this.drawVerticalLines(labyrinth, labyrinthPageContentStream, solutionPageContentStream); this.drawVerticalLines(labyrinthPageContentStream, solutionPageContentStream);
this.drawSolution(labyrinth, solutionPageContentStream); this.drawSolution(solutionPageContentStream);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -72,15 +84,14 @@ public class PDFRenderer implements Renderer<byte[]> {
pageContentStream.setNonStrokingColor(Color.BLACK); pageContentStream.setNonStrokingColor(Color.BLACK);
} }
private void drawHorizonzalLines(@NonNull final Labyrinth labyrinth, private void drawHorizonzalLines(@NonNull final PDPageContentStream... contentStreams) throws IOException {
@NonNull final PDPageContentStream... contentStreams) 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.
Coordinate coordinate = new Coordinate(0f, 0f); Coordinate coordinate = new Coordinate(0f, 0f);
for (int y = 0; y < labyrinth.getHeight(); y++) { for (int y = 0; y < this.labyrinth.getHeight(); y++) {
boolean isPainting = false; boolean isPainting = false;
coordinate = coordinate.withY(y, labyrinth); coordinate = coordinate.withY(y);
for (int x = 0; x < labyrinth.getWidth(); x++) { for (int x = 0; x < this.labyrinth.getWidth(); x++) {
final Tile currentTile = labyrinth.getTileAt(x, y); final Tile currentTile = this.labyrinth.getTileAt(x, y);
coordinate = coordinate.withX(x); coordinate = coordinate.withX(x);
if (currentTile.hasWallAt(Direction.TOP)) { if (currentTile.hasWallAt(Direction.TOP)) {
if (!isPainting) { if (!isPainting) {
@ -100,7 +111,7 @@ public class PDFRenderer implements Renderer<byte[]> {
} }
} }
if (isPainting) { if (isPainting) {
coordinate = coordinate.withX(labyrinth.getWidth()); coordinate = coordinate.withX(this.labyrinth.getWidth());
for (final PDPageContentStream contentStream : contentStreams) { for (final PDPageContentStream contentStream : contentStreams) {
contentStream.lineTo(coordinate.getX(), coordinate.getY()); contentStream.lineTo(coordinate.getX(), coordinate.getY());
contentStream.stroke(); contentStream.stroke();
@ -108,10 +119,10 @@ public class PDFRenderer implements Renderer<byte[]> {
} }
} }
boolean isPainting = false; boolean isPainting = false;
int y = labyrinth.getHeight(); int y = this.labyrinth.getHeight();
coordinate = coordinate.withY(labyrinth.getHeight(), labyrinth); coordinate = coordinate.withY(this.labyrinth.getHeight());
for (int x = 0; x < labyrinth.getWidth(); x++) { for (int x = 0; x < this.labyrinth.getWidth(); x++) {
final Tile currentTile = labyrinth.getTileAt(x, y - 1); final Tile currentTile = this.labyrinth.getTileAt(x, y - 1);
coordinate = coordinate.withX(x); coordinate = coordinate.withX(x);
if (currentTile.hasWallAt(Direction.BOTTOM)) { if (currentTile.hasWallAt(Direction.BOTTOM)) {
if (!isPainting) { if (!isPainting) {
@ -131,7 +142,7 @@ public class PDFRenderer implements Renderer<byte[]> {
} }
} }
if (isPainting) { if (isPainting) {
coordinate = coordinate.withX(labyrinth.getWidth()); coordinate = coordinate.withX(this.labyrinth.getWidth());
for (final PDPageContentStream contentStream : contentStreams) { for (final PDPageContentStream contentStream : contentStreams) {
contentStream.lineTo(coordinate.getX(), coordinate.getY()); contentStream.lineTo(coordinate.getX(), coordinate.getY());
contentStream.stroke(); contentStream.stroke();
@ -139,17 +150,15 @@ public class PDFRenderer implements Renderer<byte[]> {
} }
} }
private void drawVerticalLines(@NonNull final Labyrinth labyrinth, private void drawVerticalLines(@NonNull final PDPageContentStream... contentStreams) throws IOException {
@NonNull final PDPageContentStream... contentStreams) 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.
Coordinate coordinate = new Coordinate(0f, 0f); Coordinate coordinate = new Coordinate(0f, 0f);
final float labyrinthHeight = labyrinth.getHeight() * SCALE; for (int x = 0; x < this.labyrinth.getWidth(); x++) {
for (int x = 0; x < labyrinth.getWidth(); x++) {
boolean isPainting = false; boolean isPainting = false;
coordinate = coordinate.withX(x); coordinate = coordinate.withX(x);
for (int y = 0; y < labyrinth.getHeight(); y++) { for (int y = 0; y < this.labyrinth.getHeight(); y++) {
final Tile currentTile = labyrinth.getTileAt(x, y); final Tile currentTile = this.labyrinth.getTileAt(x, y);
coordinate = coordinate.withY(y, labyrinth); coordinate = coordinate.withY(y);
if (currentTile.hasWallAt(Direction.LEFT)) { if (currentTile.hasWallAt(Direction.LEFT)) {
if (!isPainting) { if (!isPainting) {
for (final PDPageContentStream contentStream : contentStreams) { for (final PDPageContentStream contentStream : contentStreams) {
@ -168,7 +177,7 @@ public class PDFRenderer implements Renderer<byte[]> {
} }
} }
if (isPainting) { if (isPainting) {
coordinate = coordinate.withY(labyrinth.getHeight(), labyrinth); coordinate = coordinate.withY(this.labyrinth.getHeight());
for (final PDPageContentStream contentStream : contentStreams) { for (final PDPageContentStream contentStream : contentStreams) {
contentStream.lineTo(coordinate.getX(), coordinate.getY()); contentStream.lineTo(coordinate.getX(), coordinate.getY());
contentStream.stroke(); contentStream.stroke();
@ -176,11 +185,11 @@ public class PDFRenderer implements Renderer<byte[]> {
} }
} }
boolean isPainting = false; boolean isPainting = false;
int x = labyrinth.getWidth(); int x = this.labyrinth.getWidth();
coordinate = coordinate.withX(labyrinth.getWidth()); coordinate = coordinate.withX(this.labyrinth.getWidth());
for (int y = 0; y < labyrinth.getHeight(); y++) { for (int y = 0; y < this.labyrinth.getHeight(); y++) {
final Tile currentTile = labyrinth.getTileAt(x - 1, y); final Tile currentTile = this.labyrinth.getTileAt(x - 1, y);
coordinate = coordinate.withY(y, labyrinth); coordinate = coordinate.withY(y);
if (currentTile.hasWallAt(Direction.RIGHT)) { if (currentTile.hasWallAt(Direction.RIGHT)) {
if (!isPainting) { if (!isPainting) {
for (final PDPageContentStream contentStream : contentStreams) { for (final PDPageContentStream contentStream : contentStreams) {
@ -199,7 +208,7 @@ public class PDFRenderer implements Renderer<byte[]> {
} }
} }
if (isPainting) { if (isPainting) {
coordinate = coordinate.withY(labyrinth.getHeight(), labyrinth); coordinate = coordinate.withY(this.labyrinth.getHeight());
for (final PDPageContentStream contentStream : contentStreams) { for (final PDPageContentStream contentStream : contentStreams) {
contentStream.lineTo(coordinate.getX(), coordinate.getY()); contentStream.lineTo(coordinate.getX(), coordinate.getY());
contentStream.stroke(); contentStream.stroke();
@ -207,36 +216,35 @@ public class PDFRenderer implements Renderer<byte[]> {
} }
} }
private void drawSolution(@NonNull final Labyrinth labyrinth, private void drawSolution(@NonNull final PDPageContentStream pageContentStream) throws IOException {
@NonNull final PDPageContentStream pageContentStream) throws IOException {
// Draw the solution in red // Draw the solution in red
pageContentStream.setStrokingColor(Color.RED); pageContentStream.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. // PDF has the origin in the lower left corner. We want it in the upper left corner, hence some magic is required.
final Position end = labyrinth.getEnd(); final Position end = this.labyrinth.getEnd();
Position currentPosition = labyrinth.getStart(); Position currentPosition = this.labyrinth.getStart();
Position previousPosition = null; Position previousPosition = null;
SolutionCoordinate coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY(), labyrinth); SolutionCoordinate coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY());
pageContentStream.moveTo(coordinate.getX(), coordinate.getY()); pageContentStream.moveTo(coordinate.getX(), coordinate.getY());
do { do {
Position newCurrent = this.findNextSolutionPosition(labyrinth, previousPosition, currentPosition); Position newCurrent = this.findNextSolutionPosition(previousPosition, currentPosition);
previousPosition = currentPosition; previousPosition = currentPosition;
currentPosition = newCurrent; currentPosition = newCurrent;
coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY(), labyrinth); coordinate = new SolutionCoordinate(currentPosition.getX(), currentPosition.getY());
pageContentStream.lineTo(coordinate.getX(), coordinate.getY()); pageContentStream.lineTo(coordinate.getX(), coordinate.getY());
} while (!currentPosition.equals(end)); } while (!currentPosition.equals(end));
pageContentStream.stroke(); pageContentStream.stroke();
} }
@NonNull @NonNull
private Position findNextSolutionPosition(@NonNull final Labyrinth labyrinth, @Nullable final Position previousPosition, @NonNull final Position currentPosition) { private Position findNextSolutionPosition(@Nullable final Position previousPosition, @NonNull final Position currentPosition) {
final Tile currentTile = labyrinth.getTileAt(currentPosition); final Tile currentTile = this.labyrinth.getTileAt(currentPosition);
for (final Direction direction : Direction.values()) { for (final Direction direction : Direction.values()) {
if (!currentTile.hasWallAt(direction)) { if (!currentTile.hasWallAt(direction)) {
final Position position = direction.translate(currentPosition); final Position position = direction.translate(currentPosition);
if (position.equals(previousPosition) || !isValid(position)) { if (position.equals(previousPosition) || !isValid(position)) {
continue; continue;
} }
if (labyrinth.getTileAt(position).isSolution()) { if (this.labyrinth.getTileAt(position).isSolution()) {
return position; return position;
} }
} }
@ -245,7 +253,7 @@ public class PDFRenderer implements Renderer<byte[]> {
} }
@Value @Value
private static class Coordinate { private class Coordinate {
float x; float x;
float y; float y;
@ -254,52 +262,40 @@ public class PDFRenderer implements Renderer<byte[]> {
this.y = y; this.y = y;
} }
private static float calcX(final int x) { private float calcX(final int x) {
return x * SCALE + MARGIN; return x * SCALE + MARGIN;
} }
private static float calcY(final int y, @NonNull final Labyrinth labyrinth) { private float calcY(final int y) {
return (labyrinth.getHeight() - y) * SCALE + MARGIN; return (Generator.this.labyrinth.getHeight() - y) * SCALE + MARGIN;
} }
public Coordinate withX(final int x) { public Coordinate withX(final int x) {
return new Coordinate(calcX(x), this.y); return new Coordinate(calcX(x), this.y);
} }
public Coordinate withY(final int y, @NonNull final Labyrinth labyrinth) { public Coordinate withY(final int y) {
return new Coordinate(this.x, calcY(y, labyrinth)); return new Coordinate(this.x, calcY(y));
} }
} }
@Value @Value
private static class SolutionCoordinate { private class SolutionCoordinate {
float x; float x;
float y; float y;
public SolutionCoordinate(final float x, final float y) { public SolutionCoordinate(final int x, final int y) {
this.x = x;
this.y = y;
}
public SolutionCoordinate(final int x, final int y, @NonNull final Labyrinth labyrinth) {
this.x = calcX(x); this.x = calcX(x);
this.y = calcY(y, labyrinth); this.y = calcY(y);
} }
private static float calcX(final int x) { private float calcX(final int x) {
return x * SCALE + SCALE / 2 + MARGIN; return x * SCALE + SCALE / 2 + MARGIN;
} }
private static float calcY(final int y, @NonNull final Labyrinth labyrinth) { private float calcY(final int y) {
return (labyrinth.getHeight() - y) * SCALE - SCALE / 2 + MARGIN; return (Generator.this.labyrinth.getHeight() - y) * SCALE - SCALE / 2 + MARGIN;
} }
public SolutionCoordinate withX(final int x) {
return new SolutionCoordinate(calcX(x), this.y);
}
public SolutionCoordinate withY(final int y, @NonNull final Labyrinth labyrinth) {
return new SolutionCoordinate(this.x, calcY(y, labyrinth));
} }
} }
} }