Skip to content
Snippets Groups Projects
Commit 6acf56f7 authored by Leonard Kupper's avatar Leonard Kupper
Browse files

Checkout new approach as applied in line rasterizer.

parent 0e93800e
No related branches found
No related tags found
1 merge request!42Legend rasterizer column views
package de.tudresden.inf.mci.brailleplot.rendering;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
/**
* Simple representation of a legend.
* @author Leonard Kupper, Andrey Ruzhanskiy
* @version 2019.09.25
*/
public class Legend implements Renderable {
private String mTitle;
private Map<String, Map<String, String>> mStringExplanationLists = new LinkedHashMap<>();
private Map<String, Map<Texture<Boolean>, String>> mTextureExplanationLists = new LinkedHashMap<>();
private String mColumnViewTitle; //
private Map<String, Map<String, String>> mColumnView = new LinkedHashMap<>();
//
// column title (x-axis e.g.)
// symbol
// explanation
private int mTextureExampleWidthCells = 1;
private int mTextureExampleHeightCells = 1;
/**
* Constructor. Creates a legend.
* @param title The title of the legend.
*/
public Legend(final String title) {
setTitle(title);
}
/**
* Sets a new title.
* @param title The new title for the legend.
*/
public void setTitle(final String title) {
mTitle = Objects.requireNonNull(title);
}
/**
* Gets the current title of the legend.
* @return A {@link String} containing the title.
*/
public String getTitle() {
return mTitle;
}
/**
* Add a text symbol and the associated description text to the legend.
* @param groupName The name of the header under which explanations of this group will be placed. (e.g. "Categories")
* @param symbol The actual symbol to be explained, which appears in the diagram. (e.g. "A")
* @param descriptionText A text describing the meaning of the symbol/abbreviation. (e.g. a long name of a category)
*/
public void addSymbolExplanation(final String groupName, final String symbol, final String descriptionText) {
if (!mStringExplanationLists.containsKey(groupName)) {
mStringExplanationLists.put(groupName, new LinkedHashMap<>());
}
mStringExplanationLists.get(groupName).put(symbol, descriptionText);
}
/**
* Add a column to the columnview.
* @param columnName Name of column.
* @param explanations {@link Map} of symbols and descriptions inside the column.
*/
public void addColumn(final String columnName, final Map<String, String> explanations) {
mColumnView.put(columnName, explanations);
}
/**
* Set the columnview title.
* @param columnViewTitle The title for the columnview.
*/
public void setColumnViewTitle(final String columnViewTitle) {
this.mColumnViewTitle = columnViewTitle;
}
/**
* Getter for the column-view.
* @return {@link Map} representing the columnview.
*/
public Map<String, Map<String, String>> getColumnView() {
return mColumnView;
}
/**
* Getter for the columnview-title.
* @return {@link String} representing the columnview-title.
*/
public String getColumnViewTitle() {
return mColumnViewTitle;
}
/**
* Add a texture and the associated description text to the legend.
* @param groupName The name of the header under which explanations of this group will be placed. (e.g. "Series")
* @param texture The actual texture to be explained, which is used in the diagram.
* @param descriptionText A text describing the meaning of the texture. (e.g. "Series 1")
*/
public void addTextureExplanation(final String groupName, final Texture<Boolean> texture, final String descriptionText) {
if (!mTextureExplanationLists.containsKey(groupName)) {
mTextureExplanationLists.put(groupName, new LinkedHashMap<>());
}
mTextureExplanationLists.get(groupName).put(texture, descriptionText);
}
/**
* Add a complete group of text symbol explanations to the legend.
* @param groupName The name of the header under which explanations of this group will be placed. (e.g. "Categories")
* @param explanationGroup A map listing the text symbols and their associated description texts.
*/
public void addSymbolExplanationGroup(final String groupName, final Map<String, String> explanationGroup) {
mStringExplanationLists.put(groupName, explanationGroup);
}
/**
* Add a complete group of texture explanations to the legend.
* @param groupName The name of the header under which explanations of this group will be placed. (e.g. "Categories")
* @param explanationGroup A map listing the textures and their associated description texts.
*/
public void addTextureExplanationGroup(final String groupName, final Map<Texture<Boolean>, String> explanationGroup) {
mTextureExplanationLists.put(groupName, explanationGroup);
}
/**
* Set the size (in cells) of the example rectangles displaying textures from the texture explanation groups.
* @param widthCells The new width in cells.
* @param heightCells The new height in cells.
*/
public void setTextureExampleSize(final int widthCells, final int heightCells) {
mTextureExampleWidthCells = widthCells;
mTextureExampleHeightCells = heightCells;
}
/**
* Get all text symbol explanation groups from the legend.
* @return A map associating every group name with a map listing the text symbols and their associated description texts.
*/
final Map<String, Map<String, String>> getSymbolExplanationGroups() {
return mStringExplanationLists;
}
/**
* Get all texture explanation groups from the legend.
* @return A map associating every group name with a map listing the textures and their associated description texts.
*/
final Map<String, Map<Texture<Boolean>, String>> getTextureExplanationGroups() {
return mTextureExplanationLists;
}
final int getTextureExampleWidthCells() {
return mTextureExampleWidthCells;
}
final int getTextureExampleHeightCells() {
return mTextureExampleHeightCells;
}
}
package de.tudresden.inf.mci.brailleplot.rendering;
import de.tudresden.inf.mci.brailleplot.layout.InsufficientRenderingAreaException;
import de.tudresden.inf.mci.brailleplot.layout.RasterCanvas;
import de.tudresden.inf.mci.brailleplot.layout.Rectangle;
import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData;
import java.util.Map;
import static java.lang.Integer.max;
import static java.lang.StrictMath.min;
/**
* A rasterizer that is able to draw a legend on a new page.
* @author Leonard Kupper, Andrey Ruzhanskiy
* @version 2019.09.25
*/
public class LegendRasterizer implements Rasterizer<Legend> {
private RasterCanvas mCanvas;
private Legend mLegend;
private static final int MIN_TEXT_WIDTH_CELLS = 10; // how much space should be available for an explanation text at least. (To avoid excessive line breaking)
private static final int EXPLANATION_TEXT_INDENTATION_CELLS = 1; // indentation for explanation texts.
private static final String LEGEND_KEYWORD = "Legende:"; // title for the legend
// Sub rasterizers
private LiblouisBrailleTextRasterizer mTextRasterizer;
private TextureRasterizer mTextureRasterizer = new TextureRasterizer();
/**
* Rasterizes a {@link Legend} instance onto a {@link RasterCanvas}. Important: This creates a new page on the canvas!
* @param legend An instance of {@link Legend} containing the legend contents.
* @param canvas An instance of {@link RasterCanvas} representing the target for the rasterizer output.
*/
@Override
public void rasterize(final Legend legend, final RasterCanvas canvas) throws InsufficientRenderingAreaException {
mTextRasterizer = new LiblouisBrailleTextRasterizer(canvas.getPrinter());
mCanvas = canvas;
mLegend = legend;
// Create a fresh page on the canvas.
canvas.getNewPage();
Rectangle referenceCellArea = canvas.getCellRectangle();
try {
// Write "Legend" keyword + title
writeLine(LEGEND_KEYWORD + " " + legend.getTitle(), referenceCellArea);
// String explanation lists
for (Map.Entry<String, Map<String, String>> list : legend.getSymbolExplanationGroups().entrySet()) {
String groupName = list.getKey();
writeLine(groupName + ":", referenceCellArea);
moveIndentation(referenceCellArea, EXPLANATION_TEXT_INDENTATION_CELLS); // set indentation
for (Map.Entry<String, String> explanation : list.getValue().entrySet()) {
String symbol = explanation.getKey();
String description = explanation.getValue();
writeLine(symbol + " " + description, referenceCellArea);
}
moveIndentation(referenceCellArea, -1 * EXPLANATION_TEXT_INDENTATION_CELLS); // reset indentation
}
// Texture explanation lists
for (Map.Entry<String, Map<Texture<Boolean>, String>> list : legend.getTextureExplanationGroups().entrySet()) {
String groupName = list.getKey();
writeLine(groupName + ":", referenceCellArea);
moveIndentation(referenceCellArea, EXPLANATION_TEXT_INDENTATION_CELLS); // set indentation
for (Map.Entry<Texture<Boolean>, String> explanation : list.getValue().entrySet()) {
Texture<Boolean> texture = explanation.getKey();
String description = explanation.getValue();
drawTextureExample(referenceCellArea, texture, description);
}
moveIndentation(referenceCellArea, -1 * EXPLANATION_TEXT_INDENTATION_CELLS); // reset indentation
}
// Columnview
if (legend.getColumnView().size() > 0) {
writeLine(legend.getColumnViewTitle(), referenceCellArea);
// int columnWidthCells = referenceCellArea.intWrapper().getWidth() / legend.getColumnView().size();
for (Map.Entry<String, Map<String, String>> list : legend.getColumnView().entrySet()) {
//Rectangle columnCellArea = referenceCellArea.removeFromLeft(columnWidthCells);
Rectangle columnCellArea = new Rectangle(referenceCellArea);
//moveIndentation(columnCellArea, EXPLANATION_TEXT_INDENTATION_CELLS);
writeLine(list.getKey(), columnCellArea);
int maxWidth = 0;
for (Map.Entry<String, String> explanation : list.getValue().entrySet()) {
String symbol = explanation.getKey();
String description = explanation.getValue();
String textToWrite = symbol + " " + description;
try {
int usedWidth = writeLine(textToWrite, columnCellArea);
if (usedWidth > maxWidth) {
maxWidth = usedWidth;
}
} catch (Rectangle.OutOfSpaceException e) {
referenceCellArea.removeFromLeft(maxWidth + 1);
maxWidth = 0;
columnCellArea = new Rectangle(referenceCellArea);
columnCellArea.removeFromTop(1);
int usedWidth = writeLine(textToWrite, columnCellArea);
if (usedWidth > maxWidth) {
maxWidth = usedWidth;
}
}
}
referenceCellArea.removeFromLeft(maxWidth + 1 + EXPLANATION_TEXT_INDENTATION_CELLS);
//moveIndentation(columnCellArea, -1 * EXPLANATION_TEXT_INDENTATION_CELLS); // reset indentation
}
}
} catch (Rectangle.OutOfSpaceException e) {
throw new InsufficientRenderingAreaException("The amount of data in the legend does not fit on the format.", e);
}
}
private void drawTextureExample(
final Rectangle referenceCellArea,
final Texture<Boolean> texture,
final String description
) throws Rectangle.OutOfSpaceException, InsufficientRenderingAreaException {
MatrixData<Boolean> page = mCanvas.getCurrentPage();
// add padding between previous content and example
referenceCellArea.removeFromTop(1);
// reserve the overall area for the inner texture and description text
int textureExampleHeightCells = mLegend.getTextureExampleHeightCells();
int textureExampleWidthCells = mLegend.getTextureExampleWidthCells();
Rectangle exampleCellArea = referenceCellArea.removeFromTop(textureExampleHeightCells);
exampleCellArea.removeFromLeft(1); // space for left border
Rectangle texturedDotArea = mCanvas.toDotRectangle(exampleCellArea.removeFromLeft(textureExampleWidthCells));
referenceCellArea.fromTop(1); // just to make sure there is enough space for the bottom border
// create surrounding rectangle for border of textured area
Rectangle textureDotBorder = texturedDotArea.translatedBy(-1, -1);
textureDotBorder.setHeight(textureDotBorder.getHeight() + 2);
textureDotBorder.setWidth(textureDotBorder.getWidth() + 1);
// reserve space for the texture description text
Rectangle textCellArea = exampleCellArea;
textCellArea.removeFromLeft(1); // padding between textured area and text
// check if description height will fit
int descriptionHeightCells = (int) Math.ceil(mTextRasterizer.getBrailleStringLength(description) / textCellArea.getWidth());
if (descriptionHeightCells > textureExampleHeightCells) {
int missingLines = descriptionHeightCells - textureExampleHeightCells;
// in this case, description text needs more lines than texture, so we need to reserve the missing space
referenceCellArea.removeFromTop(missingLines);
textCellArea.setHeight(textCellArea.getHeight() + missingLines); // expand height
}
// draw textured area
mTextureRasterizer.rasterize(new TexturedArea(texture, texturedDotArea), mCanvas);
// draw border
Rasterizer.rectangle(textureDotBorder, mCanvas.getCurrentPage(), true);
// draw text
mTextRasterizer.rasterize(new BrailleText(description, mCanvas.toDotRectangle(textCellArea)), mCanvas);
}
private void moveIndentation(final Rectangle cellArea, final int indent) {
cellArea.setX(cellArea.getX() + indent);
cellArea.setWidth(cellArea.getWidth() - indent);
}
private int writeLine(final String text, final Rectangle cellArea) throws InsufficientRenderingAreaException, Rectangle.OutOfSpaceException {
if (cellArea.getWidth() < MIN_TEXT_WIDTH_CELLS) {
throw new InsufficientRenderingAreaException("Not enough space for legend text.");
}
// write text lines
int textLength = mTextRasterizer.getBrailleStringLength(text);
int textHeight = max(1, (int) Math.ceil(textLength / cellArea.getWidth()));
Rectangle textLineDotArea = mCanvas.toDotRectangle(cellArea.removeFromTop(textHeight));
mTextRasterizer.rasterize(new BrailleText(text, textLineDotArea), mCanvas);
return min(textLength, cellArea.intWrapper().getWidth());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment