diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java index 5b0e1c36bded3b87ca70493b051e8a3c03d49a65..0b9a0ebbe0e4f3ee865fb5a7488d60230ba6d5b9 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java @@ -1,21 +1,23 @@ package de.tudresden.inf.mci.brailleplot; +import ch.qos.logback.classic.Level; import de.tudresden.inf.mci.brailleplot.configparser.Format; import de.tudresden.inf.mci.brailleplot.configparser.JavaPropertiesConfigurationParser; import de.tudresden.inf.mci.brailleplot.configparser.Printer; import de.tudresden.inf.mci.brailleplot.configparser.Representation; -import de.tudresden.inf.mci.brailleplot.layout.PlotCanvas; +import de.tudresden.inf.mci.brailleplot.csvparser.MalformedCsvException; import de.tudresden.inf.mci.brailleplot.datacontainers.PointListContainer; -import de.tudresden.inf.mci.brailleplot.diagrams.ScatterPlot; +import de.tudresden.inf.mci.brailleplot.datacontainers.SimpleCategoricalPointListContainerImpl; +import de.tudresden.inf.mci.brailleplot.diagrams.CategoricalBarChart; +import de.tudresden.inf.mci.brailleplot.diagrams.Diagram; +import de.tudresden.inf.mci.brailleplot.layout.AbstractCanvas; +import de.tudresden.inf.mci.brailleplot.layout.PlotCanvas; import de.tudresden.inf.mci.brailleplot.layout.RasterCanvas; -import de.tudresden.inf.mci.brailleplot.layout.Rectangle; -import de.tudresden.inf.mci.brailleplot.point.Point2DValued; -import de.tudresden.inf.mci.brailleplot.printabledata.FloatingPointData; +import de.tudresden.inf.mci.brailleplot.printabledata.PrintableData; import de.tudresden.inf.mci.brailleplot.printerbackend.PrintDirector; import de.tudresden.inf.mci.brailleplot.printerbackend.PrinterCapability; -import de.tudresden.inf.mci.brailleplot.printabledata.SimpleMatrixDataImpl; import de.tudresden.inf.mci.brailleplot.commandline.CommandLineParser; import de.tudresden.inf.mci.brailleplot.commandline.SettingType; @@ -25,39 +27,36 @@ import de.tudresden.inf.mci.brailleplot.commandline.SettingsWriter; import de.tudresden.inf.mci.brailleplot.csvparser.CsvOrientation; import de.tudresden.inf.mci.brailleplot.csvparser.CsvParser; import de.tudresden.inf.mci.brailleplot.csvparser.CsvType; +import de.tudresden.inf.mci.brailleplot.datacontainers.CategoricalPointListContainer; import de.tudresden.inf.mci.brailleplot.datacontainers.PointList; -import de.tudresden.inf.mci.brailleplot.rendering.BrailleText; -import de.tudresden.inf.mci.brailleplot.rendering.FunctionalRasterizer; import de.tudresden.inf.mci.brailleplot.rendering.LiblouisBrailleTextRasterizer; - import de.tudresden.inf.mci.brailleplot.rendering.MasterRenderer; import de.tudresden.inf.mci.brailleplot.svgexporter.BoolFloatingPointDataSvgExporter; import de.tudresden.inf.mci.brailleplot.svgexporter.BoolMatrixDataSvgExporter; import de.tudresden.inf.mci.brailleplot.svgexporter.SvgExporter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import tec.units.ri.quantity.Quantities; -import tec.units.ri.unit.MetricPrefix; -import javax.measure.Quantity; -import javax.measure.quantity.Length; import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.nio.file.Path; -import java.util.Optional; +import java.util.Iterator; +import java.util.Objects; import java.util.concurrent.ConcurrentLinkedDeque; -import static tec.units.ri.unit.Units.METRE; - /** * Main class. * Set up the application and run it. - * @author Georg Graßnick, Andrey Ruzhanskiy - * @version 2019.08.26 + * @author Georg Graßnick, Andrey Ruzhanskiy, Leonard Kupper + * @version 2019.10.03 */ public final class App { @@ -155,16 +154,14 @@ public final class App { mLogger.info("Application started"); // Parse command line parameters CommandLineParser cliParser = new CommandLineParser(); + if (CommandLineParser.checkForHelp(args)) { + cliParser.printHelp(); // If requested, print help and exit + return EXIT_SUCCESS; + } SettingsWriter settings = cliParser.parse(args); SettingsReader settingsReader = settings; - - // If requested, print help and exit - Optional<Boolean> printHelp = settingsReader.isTrue(SettingType.DISPLAY_HELP); - if (printHelp.isPresent() && printHelp.get()) { - cliParser.printHelp(); - return EXIT_SUCCESS; - } + setLoggingLevel(Level.valueOf(settingsReader.getSetting(SettingType.LOG_LEVEL).orElse("Info"))); // Config Parsing JavaPropertiesConfigurationParser configParser; @@ -178,81 +175,114 @@ public final class App { configParser = new JavaPropertiesConfigurationParser(configPath, defaultConfig); } - Printer indexV4Printer = configParser.getPrinter(); - Format a4Format = configParser.getFormat("A4"); + // Set up Printer, Representation & Format Configurables + Printer printer = configParser.getPrinter(); Representation representationParameters = configParser.getRepresentation(); + Format format; + if (!settingsReader.isPresent(SettingType.FORMAT)) { + format = configParser.getFormat("default"); // Default behaviour from default config (A4 portrait) + } else { + format = configParser.getFormat(settingsReader.getSetting(SettingType.FORMAT).get()); + } - // Parse csv data - ClassLoader classloader = Thread.currentThread().getContextClassLoader(); - InputStream csvStream = classloader.getResourceAsStream("examples/csv/2_line_plot.csv"); - Reader csvReader = new BufferedReader(new InputStreamReader(csvStream)); - + // Parse csv data and create diagram + InputStream csvStream; + if (!settingsReader.isPresent(SettingType.CSV_LOCATION)) { + ClassLoader classloader = Thread.currentThread().getContextClassLoader(); + csvStream = classloader.getResourceAsStream("examples/csv/0_bar_chart_categorical.csv"); + mLogger.warn("ATTENTION! Using example csv. Please remove this behavior before packaging the jar."); + } else { + csvStream = new FileInputStream(settingsReader.getSetting(SettingType.CSV_LOCATION).get()); + } + Reader csvReader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(csvStream))); CsvParser csvParser = new CsvParser(csvReader, ',', '\"'); - PointListContainer<PointList> container = csvParser.parse(CsvType.DOTS, CsvOrientation.HORIZONTAL); - mLogger.debug("Internal data representation:\n {}", container.toString()); - - ScatterPlot scatterPlot = new ScatterPlot(container); - scatterPlot.setTitle("Scatter Plot"); - scatterPlot.setXAxisName("X-Achse foo"); - scatterPlot.setYAxisName("Y-Achse bar"); + Diagram diagram; + CsvOrientation csvOrientation; + if (settingsReader.isTrue(SettingType.VERTICAL_CSV).orElse(false)) { + csvOrientation = CsvOrientation.VERTICAL; + } else { + csvOrientation = CsvOrientation.HORIZONTAL; + } + switch (settingsReader.getSetting(SettingType.DIAGRAM_TYPE).orElse("")) { + case "ScatterPlot": + PointListContainer<PointList> scatterPlotContainer = csvParser.parse(CsvType.DOTS, csvOrientation); + throw new UnsupportedOperationException("Scatter Plots coming soon."); // TODO: integrate scatter plots + case "LineChart": + PointListContainer<PointList> lineChartContainer = csvParser.parse(CsvType.DOTS, csvOrientation); + throw new UnsupportedOperationException("Line Charts coming soon."); // TODO: integrate line charts + case "BarChart": + CategoricalPointListContainer<PointList> barChartContainer; + try { // first try to parse as regular bar chart and convert to single category bar cart. + barChartContainer = new SimpleCategoricalPointListContainerImpl(csvParser.parse(CsvType.X_ALIGNED, csvOrientation)); + } catch (MalformedCsvException e) { // else parse as categorical bar chart + barChartContainer = csvParser.parse(CsvType.X_ALIGNED_CATEGORIES, csvOrientation); + } + diagram = new CategoricalBarChart(barChartContainer); + break; + default: throw new IllegalStateException("Unknown diagram type: " + settingsReader.getSetting(SettingType.DIAGRAM_TYPE).orElse("<none>")); + } + diagram.setTitle(settingsReader.getSetting(SettingType.DIAGRAM_TITLE).orElse("")); + diagram.setXAxisName(settingsReader.getSetting(SettingType.X_AXIS_LABEL).orElse("")); + diagram.setYAxisName(settingsReader.getSetting(SettingType.Y_AXIS_LABEL).orElse("")); // Render diagram LiblouisBrailleTextRasterizer.initModule(); - MasterRenderer renderer = new MasterRenderer(indexV4Printer, representationParameters, a4Format); - RasterCanvas canvas = renderer.rasterize(scatterPlot); - SimpleMatrixDataImpl<Boolean> mat = (SimpleMatrixDataImpl<Boolean>) canvas.getCurrentPage(); - mLogger.debug("Render preview:\n" + mat.toBoolString()); - - // SVG exporting - SvgExporter<RasterCanvas> svgExporter = new BoolMatrixDataSvgExporter(canvas); - svgExporter.render(); - svgExporter.dump("boolMat"); - - // FloatingPointData SVG exporting example - PlotCanvas floatCanvas = new PlotCanvas(indexV4Printer, representationParameters, a4Format); - FloatingPointData<Boolean> points = floatCanvas.getNewPage(); - - final int blockX = 230; - final int blockY = 400; - for (int y = 0; y < blockY; y += 2) { - for (int x = 0; x < blockX; x += 2) { - Point2DValued<Quantity<Length>, Boolean> point = new Point2DValued<>(Quantities.getQuantity(x, MetricPrefix.MILLI(METRE)), Quantities.getQuantity(y, MetricPrefix.MILLI(METRE)), true); - points.addPoint(point); - } + MasterRenderer renderer = new MasterRenderer(printer, representationParameters, format); + PrinterCapability mode = PrinterCapability.valueOf(printer.getProperty("mode").toString().toUpperCase()); + Iterator<? extends PrintableData> outputPages; + SvgExporter<? extends AbstractCanvas> svgExporter; + switch (mode) { // Decide on correct rendering mode to apply + case NORMALPRINTER: + RasterCanvas rasterCanvas = renderer.rasterize(diagram); + svgExporter = new BoolMatrixDataSvgExporter(rasterCanvas); + outputPages = rasterCanvas.getPageIterator(); + break; + case INDEX_EVEREST_D_V4_FLOATINGDOT_PRINTER: + PlotCanvas plotCanvas = new PlotCanvas(printer, representationParameters, format); // TODO: call renderer.plot() + svgExporter = new BoolFloatingPointDataSvgExporter(plotCanvas); + outputPages = plotCanvas.getPageIterator(); + break; + default: throw new UnsupportedOperationException("Mode not supported: " + mode); } - SvgExporter<PlotCanvas> floatSvgExporter = new BoolFloatingPointDataSvgExporter(floatCanvas); - floatSvgExporter.render(); - floatSvgExporter.dump("floatingPointData"); - LiblouisBrailleTextRasterizer textRasterizer = new LiblouisBrailleTextRasterizer(indexV4Printer); - renderer.getRenderingBase().registerRasterizer(new FunctionalRasterizer<BrailleText>(BrailleText.class, textRasterizer)); - RasterCanvas refCanvas = renderer.rasterize(new BrailleText(" ", new Rectangle(0, 0, 0, 0))); - mat = (SimpleMatrixDataImpl<Boolean>) canvas.getCurrentPage(); - mLogger.debug("Render preview:\n" + mat.toBoolString()); + // Action switches + boolean doPrint = !settingsReader.isTrue(SettingType.INHIBIT_PRINT).orElse(false); + boolean doSvgExport = settingsReader.isPresent(SettingType.SVG_EXPORT); + boolean doByteDump = settingsReader.isPresent(SettingType.BYTE_DUMP); - - - // Check if some SpoolerService/Printservice exists - if (!PrintDirector.isPrintServiceOn()) { - throw new Exception("Can't find any Printservices on this System."); + // SVG exporting + if (doSvgExport) { + File svgBaseFile = new File(settingsReader.getSetting(SettingType.SVG_EXPORT).get()); + svgExporter.render(); + svgExporter.dump(svgBaseFile.getAbsolutePath()); } - /* - We do not want to actually print on each run. - Until CLI parsing is fully integrated, you will have to disable this check by hand if you actually do - want to print. - Please do not commit changes to this. - */ - if (true) { - return EXIT_SUCCESS; + // Printing and Byte Dumping + PrintDirector printD = new PrintDirector(mode, printer); + if (doPrint && !PrintDirector.isPrintServiceOn()) { // Check for running spooler or print service + throw new Exception("Can't find any running print services on this system."); + } + File dumpBaseFile = null; // Setup dump base file if required + if (doByteDump) { + dumpBaseFile = new File(settingsReader.getSetting(SettingType.BYTE_DUMP).get()); + } + int pageNumber = 0; + while (outputPages.hasNext()) { // Iterate pages + PrintableData page = outputPages.next(); + if (doByteDump) { // Byte dump + try (FileOutputStream outputStream = new FileOutputStream(dumpBaseFile.getAbsolutePath() + String.format("_%03d.bin", pageNumber))) { + outputStream.write(printD.byteDump(page)); + } catch (IOException ex) { + // Inform user, but do not stop execution + mLogger.error("An error occured while creating byte dump", ex); + throw new RuntimeException(); + } + } + if (doPrint) { // Print page + printD.print(page); + } + pageNumber++; } - - // Last Step: Printing - @SuppressWarnings("checkstyle:MagicNumber") - String printerConfigUpperCase = indexV4Printer.getProperty("mode").toString().toUpperCase(); - PrintDirector printD = new PrintDirector(PrinterCapability.valueOf(printerConfigUpperCase), indexV4Printer); - printD.print(mat); - } catch (final Exception e) { terminateWithException(e); } @@ -261,4 +291,9 @@ public final class App { return EXIT_SUCCESS; } + + public static void setLoggingLevel(final Level level) { + ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) org.slf4j.LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME); + root.setLevel(level); + } } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java index 81e77493f2815a72fe27a5da3ea8de51adb7a686..2ad445dea44aca67cafed64cecb46eb065d20e68 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParser.java @@ -3,28 +3,49 @@ package de.tudresden.inf.mci.brailleplot.commandline; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import java.util.Arrays; +import java.util.Objects; + + /** * Performs command line parsing and creates a {@link Settings} object. - * @author Georg Graßnick - * @version 2019.05.31 + * @author Georg Graßnick, Andrey Ruzhanskiy, Leonard Kupper + * @version 2019.10.03 */ public class CommandLineParser { private Options mOptions; + public static Option helpOption = Option.builder("h") + .longOpt("help") + .required(false) + .hasArg(false) + .desc("Print help and exit") + .build(); + public CommandLineParser() { setupOptions(); } private void setupOptions() { mOptions = new Options(); - mOptions.addOption("h", SettingType.DISPLAY_HELP.toString(), false, "Print help and exit") - .addOption("c", SettingType.CSV_LOCATION.toString(), true, "Path to CSV") - .addOption("s", SettingType.SEMANTIC_MAPPING.toString(), true, "Literal for semantic mapping") - .addOption("p", SettingType.PRINTER_CONFIG_PATH.toString(), true, "Path to printer configuration file"); + mOptions.addOption(helpOption) + .addRequiredOption("c", SettingType.CSV_LOCATION.toString(), true, "Path to CSV") + .addRequiredOption("p", SettingType.PRINTER_CONFIG_PATH.toString(), true, "Path to printer configuration file") + .addRequiredOption("t", SettingType.DIAGRAM_TITLE.toString(), true, "Title of the diagram") + .addRequiredOption("d", SettingType.DIAGRAM_TYPE.toString(), true, "Type of the diagram. Possible Values: [ScatterPlot, LineChart, BarChart]") + .addOption("x", SettingType.X_AXIS_LABEL.toString(), true, "Label of X-axis including unit") + .addOption("y", SettingType.Y_AXIS_LABEL.toString(), true, "Label of Y-axis including unit") + .addOption("f", SettingType.FORMAT.toString(), true, "Name of predefined format from configuration (A4 portrait if not specified)") + .addOption("v", SettingType.VERTICAL_CSV.toString(), false, "Parse CSV in vertical instead of horizontal orientation (Only applicable for BarChart)") + .addOption("i", SettingType.INHIBIT_PRINT.toString(), false, "Inhibit the printing process") + .addOption("s", SettingType.SVG_EXPORT.toString(), true, "Base file path for export of svg file(s) (Omit '.svg' suffix)") + .addOption("b", SettingType.BYTE_DUMP.toString(), true, "Base file path for print data byte dump file(s) (Omit '.bin' suffix)") + .addOption("l", SettingType.LOG_LEVEL.toString(), true, "Logging output level. Possible Values: [All, Trace, Debug, Info, Warn, Error, Off] Defaults to 'Info'"); } /** @@ -45,13 +66,35 @@ public class CommandLineParser { return new Settings(cmdLine); } + public static boolean checkForHelp(final String[] args) { + + boolean hasHelp = false; + Options options = new Options(); + options.addOption(helpOption); + org.apache.commons.cli.CommandLineParser parser = new DefaultParser(); + CommandLine cmd = null; + try { + cmd = parser.parse(options, args); + } catch (ParseException e) { + // Will occur if any other option than "help" is encountered + // For this case we can safely ignore it. + } + if ((Objects.nonNull(cmd) && cmd.hasOption(helpOption.getOpt())) + || (args.length == 0) + || (Arrays.asList(args).contains("-" + helpOption.getOpt()) + || (Arrays.asList(args).contains("--" + helpOption.getLongOpt())))) { + hasHelp = true; + } + return hasHelp; + } + /** * Print usage information to the command line. */ public final void printHelp() { HelpFormatter formatter = new HelpFormatter(); String headerForOptions = "Convert csv into braille"; - String footerForOptions = "Report Issues to Leonard Kupper"; + String footerForOptions = ""; // TODO: Add footer with version and official contact. formatter.printHelp("braillegraphics", headerForOptions, mOptions, footerForOptions, true); } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java index 010c9c528891ffdb81549f715eac363625469e76..c8154bc15e62744a5db2a6e715e760565682e844 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/commandline/SettingType.java @@ -2,15 +2,25 @@ package de.tudresden.inf.mci.brailleplot.commandline; /** * Represents all possible parsed options parsed from the command line. - * @author Georg Graßnick - * @version 2019.05.31 + * @author Georg Graßnick, Andrey Ruzhanskiy, Leonard Kupper + * @version 2019.10.03 */ public enum SettingType { DISPLAY_HELP("help"), CSV_LOCATION("csv-path"), PRINTER_CONFIG_PATH("printer-config-path"), - SEMANTIC_MAPPING("semantic-mapping"); + DIAGRAM_TITLE("title"), + X_AXIS_LABEL("xLabel"), + Y_AXIS_LABEL("yLabel"), + DIAGRAM_TYPE("diagram-type"), + FORMAT("format"), + VERTICAL_CSV("vertical-csv"), + INHIBIT_PRINT("inhibit-print"), + SVG_EXPORT("svg-export"), + BYTE_DUMP("byte-dump"), + LOG_LEVEL("log-level"); + private final String mName; @@ -26,8 +36,26 @@ public enum SettingType { return CSV_LOCATION; case "printer-config-path": return PRINTER_CONFIG_PATH; - case "semantic-mapping": - return SEMANTIC_MAPPING; + case "title": + return DIAGRAM_TITLE; + case "xLabel": + return X_AXIS_LABEL; + case "yLabel": + return Y_AXIS_LABEL; + case "diagram-type": + return DIAGRAM_TYPE; + case "format": + return FORMAT; + case "vertical-csv": + return VERTICAL_CSV; + case "inhibit-print": + return INHIBIT_PRINT; + case "svg-export": + return SVG_EXPORT; + case "byte-dump": + return BYTE_DUMP; + case "log-level": + return LOG_LEVEL; default: throw new IllegalArgumentException("Setting not available"); } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationValidator.java b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationValidator.java index ccfcf8f8bccdbf26f84730890e4ef2855a2e3d45..5f4f4dc62d17d6c2cf3b366a7d5f880726365915 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationValidator.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/configparser/JavaPropertiesConfigurationValidator.java @@ -1,6 +1,7 @@ package de.tudresden.inf.mci.brailleplot.configparser; +import de.tudresden.inf.mci.brailleplot.rendering.language.BrailleLanguage; import de.tudresden.inf.mci.brailleplot.util.GeneralResource; import de.tudresden.inf.mci.brailleplot.printerbackend.PrinterCapability; import org.slf4j.Logger; @@ -76,6 +77,8 @@ public final class JavaPropertiesConfigurationValidator implements Configuration // Definition of valid representation properties defineRepresentationProperty("general.nonexistentDataText", requireNotEmpty); + defineRepresentationProperty("general.brailleLanguage", requireNotEmpty); + defineRepresentationProperty("general.legendKeyword", requireNotEmpty); defineRepresentationProperty("general.maxTitleHeight", requireInteger.and(requirePositive).and(requireNonZero)); defineRepresentationProperty("rasterize.barChart.maxBarThickness", requireInteger.and(requirePositive)); defineRepresentationProperty("rasterize.barChart.minBarThickness", requireInteger.and(requirePositive)); @@ -98,6 +101,9 @@ public final class JavaPropertiesConfigurationValidator implements Configuration case "brailletable": case "semantictable": return new GeneralResource(value, mSearchPath).getResourcePath(); + case "general.brailleLanguage": + BrailleLanguage.Language language = BrailleLanguage.Language.valueOf(value.toUpperCase()); + return language.toString(); default: return value; } } catch (Exception e) { diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/PointList.java b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/PointList.java index 9d41811040a8de1dde43be7ac983d8af6517ab18..668c5698fc8186bc2b0e20e1c738952016d15e0b 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/PointList.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/PointList.java @@ -4,12 +4,13 @@ import de.tudresden.inf.mci.brailleplot.point.Point2DDouble; import java.util.ListIterator; + /** * Interface for a list-like data structure that holds a set of {@link Point2DDouble}. * Implementing classes can be instantiated by data parser classes and are used as a data representation * for use of the rasterizer. - * @author Georg Graßnick - * @version 2019.07.29 + * @author Georg Graßnick, Andrey Ruzhanskiy + * @version 2019.09.24 */ public interface PointList extends PointContainer<Point2DDouble>, Named { @@ -19,4 +20,10 @@ public interface PointList extends PointContainer<Point2DDouble>, Named { */ ListIterator<Point2DDouble> getListIterator(); + /** + * Returns a sorted list copy in ascending fashion for the x-values. + * @return An {@link PointList} which is sorted by the x-values in ascending fashion. + */ + + PointList sortXAscend(); } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimplePointListImpl.java b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimplePointListImpl.java index 278faef9e14388a1f41c95da276661c321ced7a9..5b6dd2faab37bd00c31098a3a7793efbfcd92459 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimplePointListImpl.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimplePointListImpl.java @@ -6,11 +6,12 @@ import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Objects; +import java.util.stream.Collectors; /** * A low effort implementation of {@link PointList}. - * @author Georg Graßnick - * @version 2019.07.29 + * @author Georg Graßnick, Andrey Ruzhanskiy + * @version 2019.09.24 */ public class SimplePointListImpl extends AbstractPointContainer<Point2DDouble> implements PointList { @@ -36,6 +37,21 @@ public class SimplePointListImpl extends AbstractPointContainer<Point2DDouble> i return mElements.listIterator(); } + @Override + public PointList sortXAscend() { + PointList list = this; + List<Point2DDouble> temp = list.stream().sorted((o1, o2) -> { + if (o1.getX() < o2.getX()) { + return -1; + } else { + return 1; + } + }).collect(Collectors.toList()); + SimplePointListImpl result = new SimplePointListImpl(list.getName(), temp); + result.calculateExtrema(); + return result; + } + @Override public String getName() { return mName; diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/diagrams/LineChart.java b/src/main/java/de/tudresden/inf/mci/brailleplot/diagrams/LineChart.java new file mode 100644 index 0000000000000000000000000000000000000000..d5fcf6c24d1d3376bc584c2cdff2268682dfea46 --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/diagrams/LineChart.java @@ -0,0 +1,66 @@ +package de.tudresden.inf.mci.brailleplot.diagrams; + +import de.tudresden.inf.mci.brailleplot.datacontainers.PointList; +import de.tudresden.inf.mci.brailleplot.datacontainers.PointListContainer; + +import java.util.Objects; + +/** + * Representation of a line chart with basic data functions. Implements Renderable. + * @author Andrey Ruzhanskiy + * @version 2019.08.17 + */ +public class LineChart extends Diagram { + + private PointListContainer<PointList> mData; + + /** + * Constructor for a line chart. + * @param data The container, which holds the information about the datapoints. + */ + public LineChart(final PointListContainer<PointList> data) { + Objects.requireNonNull(data); + mData = data; + } + + /** + * Getter for the underlying Pointlistcontainer. + * @return Pointlistcontainer. + */ + public PointListContainer<PointList> getData() { + return mData; + } + + /** + * Getter for the minimum y-value. + * @return minimum y-value. + */ + public double getMinY() { + return mData.getMinY(); + } + + /** + * Getter for the maximum y-value. + * @return maximum y-value. + */ + public double getMaxY() { + return mData.getMaxY(); + } + + /** + * Getter for the minimum x-value. + * @return minimum x-value. + */ + public double getMinX() { + return mData.getMinX(); + } + + /** + * Getter for the maximum x-value. + * @return maximum x-value. + */ + public double getMaxX() { + return mData.getMaxX(); + } + +} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/AbstractDocumentBuilder.java b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/AbstractDocumentBuilder.java index 690f112e977b7c7af386e3d0d9dfbd6ae4257cc3..8aff7615c903f5422b2963b34abf9ef074366e79 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/AbstractDocumentBuilder.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/AbstractDocumentBuilder.java @@ -1,20 +1,21 @@ package de.tudresden.inf.mci.brailleplot.printerbackend; + import de.tudresden.inf.mci.brailleplot.brailleparser.AbstractBrailleTableParser; -import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData; +import de.tudresden.inf.mci.brailleplot.printabledata.PrintableData; /** * This class provides an extension point for further implementation * and protocol building for documents that need to be send to the printer. * The common Interface is the getDocument() and assemble() method. * Its usable for all braille printers. - * @param <T> Type of MatrixData. + * @param <T> Type of Data. * @author Andrey Ruzhanskiy * @version 28.05.2019 */ -abstract class AbstractDocumentBuilder<T> { +abstract class AbstractDocumentBuilder<T extends PrintableData> { - MatrixData<T> mData; + T mData; AbstractBrailleTableParser mParser; @@ -24,6 +25,6 @@ abstract class AbstractDocumentBuilder<T> { * @param data Raw data to be printed without any escapes equences * @return Fully build document as byte[] */ - abstract byte[] assemble(MatrixData<T> data); + abstract byte[] assemble(T data); } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/AbstractIndexV4Builder.java b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/AbstractIndexV4Builder.java index b79ba6a7bd627ac0db6f1adb17ecf564ad7b1c7b..dd89611885c8b025529cfa9c8a31e0bd47b2319a 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/AbstractIndexV4Builder.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/AbstractIndexV4Builder.java @@ -1,17 +1,20 @@ package de.tudresden.inf.mci.brailleplot.printerbackend; +import de.tudresden.inf.mci.brailleplot.printabledata.PrintableData; + /** * Abstract class for Documents, that need special escape sequences for the Index Everest-V4. * All special documents (i.e. Floating Dot Area) should implement this class. All information taken * from the Index PrinterCapability Interface Protocol V5_V4 2ß16-05-13. All the variables with the respective values have no * particular order (except mStartTemporaryDoc, which must be at the beginning). All the variable names are set to * final, these are PrinterCapability specific values that should not be changed. + * @param <T> Type of Data, must extend PrintableData. * @author Andrey Ruzhanskiy * @version 31.05.2019 */ @SuppressWarnings("checkstyle:MagicNumber") -abstract class AbstractIndexV4Builder extends AbstractDocumentBuilder { +abstract class AbstractIndexV4Builder<T extends PrintableData> extends AbstractDocumentBuilder<T> { @@ -22,6 +25,10 @@ abstract class AbstractIndexV4Builder extends AbstractDocumentBuilder { final byte[] mStartTemporaryDoc = new byte[] {0x1B, 0x44}; + final byte[] mStartFloatingMode = new byte[] {0x1B, 0x46}; + + final byte[] mNewLine = new byte[] {0x0A}; + /** * Standard variable name for binding margin (BI). */ diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/FloatingDotAreaBuilder.java b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/FloatingDotAreaBuilder.java index b16826be2e92e2ace441eafc47927548f932a5f1..032d0c750a7227187f03f748948cbef11ad732be 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/FloatingDotAreaBuilder.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/FloatingDotAreaBuilder.java @@ -1,16 +1,28 @@ package de.tudresden.inf.mci.brailleplot.printerbackend; -import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData; +import de.tudresden.inf.mci.brailleplot.point.Point2DValued; +import de.tudresden.inf.mci.brailleplot.printabledata.SimpleFloatingPointDataImpl; +import tec.units.ri.unit.MetricPrefix; + +import javax.measure.Quantity; +import javax.measure.quantity.Length; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Locale; +import java.util.Objects; + +import static tec.units.ri.unit.Units.METRE; /** - * Class representing the FloatingDotAre protocol for the braille Index Everest V4 for printing + * Class representing the FloatingDotArea protocol for the braille Index Everest V4 for printing * variable areas on paper via coordinates. * @author Andrey Ruzhanskiy, Leonard Kupper * @version 29.05.2019 */ -class FloatingDotAreaBuilder extends AbstractIndexV4Builder { +class FloatingDotAreaBuilder extends AbstractIndexV4Builder<SimpleFloatingPointDataImpl<Boolean>> { /** * Constructor. Does not have any functionality. Should only be used in {@link PrintDirector} @@ -19,10 +31,38 @@ class FloatingDotAreaBuilder extends AbstractIndexV4Builder { /** * Currently not implemented. - * @param data Raw data to be printed without any escape sequences + * @param data Raw data to be printed via the FloatingDotArea * @return Exception. */ - byte[] assemble(final MatrixData data) { - throw new UnsupportedOperationException(); + @Override + byte[] assemble(final SimpleFloatingPointDataImpl<Boolean> data) { + mData = Objects.requireNonNull(data); + Iterator<Point2DValued<Quantity<Length>, Boolean>> iter = mData.getIterator(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + stream.write(mStartFloatingMode); + stream.write(mSemicolon); + stream.write(mNewLine); + // Start iteration over values + while (iter.hasNext()) { + Point2DValued<Quantity<Length>, Boolean> current = iter.next(); + Quantity<Length> x = current.getX().to(MetricPrefix.MILLI(METRE)); + Quantity<Length> y = current.getY().to(MetricPrefix.MILLI(METRE)); + String xFormated = String.format(Locale.ENGLISH, "%.2f", x.getValue().doubleValue()); + String yFormated = String.format(Locale.ENGLISH, "%.2f", x.getValue().doubleValue()); + stream.write(xFormated.getBytes()); + stream.write(mColon); + stream.write(yFormated.getBytes()); + if (iter.hasNext()) { + stream.write(mNewLine); + } + } + // End with ; + stream.write(mSemicolon); + } catch (IOException e) { + e.printStackTrace(); + } + return stream.toByteArray(); + //return null; } } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/GraphicPrintBuilder.java b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/GraphicPrintBuilder.java index a51107b5ca0fa00cee02ff52dc7ef6038d187afd..ad72df45ec0a0d0c249308aa4f315f49aee95b9e 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/GraphicPrintBuilder.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/GraphicPrintBuilder.java @@ -7,7 +7,7 @@ import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData; * Class representing the graphic mode protocol from braille Index Everest D4. * @author Andrey Ruzhanskiy */ -class GraphicPrintBuilder extends AbstractIndexV4Builder { +class GraphicPrintBuilder extends AbstractIndexV4Builder<MatrixData> { /** * Constructor. Does not have any functionality. Should only be used in {@link PrintDirector} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/NormalBuilder.java b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/NormalBuilder.java index 39cee042a55d6c3fec38ea239205db5265fec999..27919f292b0f18a563170d459265a0f3c93fa3a4 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/NormalBuilder.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/NormalBuilder.java @@ -16,7 +16,7 @@ import java.util.Objects; * @version 12.07.2019 */ @SuppressWarnings("checkstyle:MagicNumber") -class NormalBuilder extends AbstractDocumentBuilder<Boolean> { +class NormalBuilder extends AbstractDocumentBuilder<MatrixData<Boolean>> { /** diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/PrintDirector.java b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/PrintDirector.java index 1ac2ba4a64afa11f20d56f63b2f90ec277c5fa7d..0868ad0387fdd4d8b0092af226f160636cd30ed2 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/PrintDirector.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/printerbackend/PrintDirector.java @@ -2,19 +2,23 @@ package de.tudresden.inf.mci.brailleplot.printerbackend; import de.tudresden.inf.mci.brailleplot.configparser.Printer; -import de.tudresden.inf.mci.brailleplot.printabledata.MatrixData; +import de.tudresden.inf.mci.brailleplot.printabledata.PrintableData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.print.Doc; import javax.print.DocFlavor; import javax.print.DocPrintJob; import javax.print.PrintException; import javax.print.PrintService; import javax.print.PrintServiceLookup; -import javax.print.Doc; - import javax.print.SimpleDoc; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.standard.JobName; +import javax.print.attribute.standard.PrinterState; +import javax.print.event.PrintJobEvent; import java.util.Objects; /** * Implements a variation of the GoF design pattern Builder. This class is used for setting the printer configuration and @@ -29,6 +33,7 @@ public class PrintDirector { private PrintService mService; private String mPrinterName; private DocFlavor mDocflavor; + private final Logger mLogger = LoggerFactory.getLogger(PrintDirector.class); private DocPrintJob mPrintJob; @@ -44,14 +49,20 @@ public class PrintDirector { Objects.requireNonNull(printerConfig); this.mPrinter = printerCap; mPrinterName = printerConfig.getProperty("name").toString(); + mLogger.trace("Using following printercapability {}", printerCap.toString(), " loaded"); + mLogger.info("Using the following printer: {}", mPrinterName); switch (mPrinter) { case NORMALPRINTER: - mBuilder = new NormalBuilder(); break; + mBuilder = new NormalBuilder(); + mLogger.trace("Using NormalBuilder as protocol"); + break; case INDEX_EVEREST_D_V4_GRAPHIC_PRINTER: mBuilder = new GraphicPrintBuilder(); + mLogger.trace("Using Index Everest-D V4 graphic print as protocol"); break; case INDEX_EVEREST_D_V4_FLOATINGDOT_PRINTER: mBuilder = new FloatingDotAreaBuilder(); + mLogger.trace("Using Index Everest-D V4 floatingdot as protocol"); break; default: throw new IllegalArgumentException(); } @@ -61,26 +72,48 @@ public class PrintDirector { /** * Public method for printing the given document with the given data. * @param data {@link de.tudresden.inf.mci.brailleplot.printabledata.MatrixData} to be printed. - * @param <T> The type of {@link MatrixData}. */ // Needed if someone tries to use a normal builder with something that is not a boolean. @SuppressWarnings("unchecked") - public <T> void print(final MatrixData<T> data) { + public void print(final PrintableData data) { + mLogger.info("Starting with print process"); Objects.requireNonNull(data); + mLogger.info("Setting up docflavour and service"); setUpDoc(); setUpService(); byte[] result; + mLogger.info("Finished setting up doc and service"); try { + mLogger.trace("Assembling the data according to protocol: {}", mBuilder.getClass().getCanonicalName()); result = mBuilder.assemble(data); } catch (ClassCastException e) { throw new IllegalArgumentException(e.getMessage(), e); } + mLogger.info("Finished assembling data"); print(result); } + /** + * Method for creating the byte array form the printing process. + * @param data Data to be later dumped. + * @return Byte array containing the sequence for the printer. + */ + @SuppressWarnings("unchecked") + public byte[] byteDump(final PrintableData data) { + mLogger.info("Starting with textdump process"); + byte[] result; + try { + mLogger.trace("Assembling the data according to protocol: {}", mBuilder.getClass().getCanonicalName()); + result = mBuilder.assemble(data); + } catch (ClassCastException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + mLogger.info("Finished with creating textdump"); + return result; + } /** * Method for setting up the DocFlavor for printing. Currently, not parameterised because the printer can * (hopefully) understand raw bytes with an octet stream. @@ -117,12 +150,24 @@ public class PrintDirector { Objects.requireNonNull(data); Objects.requireNonNull(mService); Objects.requireNonNull(mDocflavor); + mLogger.info("Setting up doc, asset and job"); Doc doc = new SimpleDoc(data, mDocflavor, null); PrintRequestAttributeSet asset = new HashPrintRequestAttributeSet(); DocPrintJob job = mService.createPrintJob(); + mLogger.trace("Finished setting up doc, asset and job"); + PrinterState state; asset.add(new JobName("Braille Printing", null)); try { + mLogger.trace("Adding job to the PrintJobListener"); + PrintJobListener listener = new PrintJobListener(); + job.addPrintJobListener(listener); + mLogger.trace("Starting printing"); + //PrinterIsAcceptingJobs set = mService.getAttribute(PrinterIsAcceptingJobs.class); + //PrinterStateReasons reasons = mService.getAttribute(PrinterStateReasons.class); job.print(doc, asset); + //set = mService.getAttribute(PrinterIsAcceptingJobs.class); + //reasons = mService.getAttribute(PrinterStateReasons.class); + listener.waitForDone(); mPrintJob = job; } catch (PrintException pe) { throw new RuntimeException(pe); @@ -141,4 +186,69 @@ public class PrintDirector { } return true; } + + /** + * Eventlistener which receives updates regarding printing. + * Because of enormous shortcoming in the implementation of printing in java, some events are never received. + */ + private class PrintJobListener implements javax.print.event.PrintJobListener { + boolean done = false; + + @Override + public void printDataTransferCompleted(final PrintJobEvent pje) { + mLogger.info("Data transfer to printer complete"); + } + + @Override + public void printJobCompleted(final PrintJobEvent pje) { + mLogger.info("Printjob completed"); + synchronized (PrintJobListener.this) { + done = true; + PrintJobListener.this.notify(); + } + } + + @Override + public void printJobFailed(final PrintJobEvent pje) { + mLogger.info("Printjob failed"); + synchronized (PrintJobListener.this) { + done = true; + PrintJobListener.this.notify(); + } + } + + @Override + public void printJobCanceled(final PrintJobEvent pje) { + mLogger.info("Printjob was canceled"); + synchronized (PrintJobListener.this) { + done = true; + PrintJobListener.this.notify(); + } + } + + @Override + public void printJobNoMoreEvents(final PrintJobEvent pje) { + mLogger.info("Printjob has no more events"); + synchronized (PrintJobListener.this) { + done = true; + } + } + + @Override + public void printJobRequiresAttention(final PrintJobEvent pje) { + mLogger.info("Printjob requires attention"); + PrintJobListener.this.notify(); + } + public synchronized void waitForDone() { + try { + // Not busy waiting, sleeping as long as not notified. + while (!done) { + wait(); + } + } catch (InterruptedException e) { + return; + } + } + } + } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Axis.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Axis.java index 2406f03bfd54140e03172cf95387152317f4a03f..0f282f0bbcb4cd472ba5bf6ee1c5dcbf437a60f9 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Axis.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Axis.java @@ -26,7 +26,7 @@ public class Axis implements Renderable { * @param type Just changes the orientation of the axis. Can be either {@link Axis.Type#X_AXIS} or {@link Axis.Type#Y_AXIS}. * @param originX The x coordinate of the position where the axis line and the tickmark and label corresponding to the value '0' is placed. * @param originY The y coordinate of the position where the axis line and the tickmark and label corresponding to the value '0' is placed. - * @param stepWidth The distance between two tickmarks on the axis. + * @param stepWidth The distance between two tickmarks on the axis in dots. * @param tickSize The size and orientation of the tickmarks. The absolute value controls the length, the sign controls on which side they are displayed. */ public Axis(final Type type, final double originX, final double originY, final double stepWidth, diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BarChartRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BarChartRasterizer.java index e9a1c3eaaeddcd943793bccce0570c9dbef7431b..5aab47f95fd240c13888b42d04498df8aae895f6 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BarChartRasterizer.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BarChartRasterizer.java @@ -54,6 +54,7 @@ public class BarChartRasterizer implements Rasterizer<CategoricalBarChart> { private int mGroupPaddingCells; // the padding size between two neighbouring bar groups in cells private int mBarPaddingCells; // the padding size between two bars inside the same group in cells private String nonexistentDataText; // the text which is displayed for a missing datapoint + private BrailleLanguage.Language mBrailleLanguage; // the preferred language & level for titles, captions, ... // Texture Management private List<Texture<Boolean>> mTextures = new ArrayList<>(); // A list of textures to differentiate bars inside a group @@ -98,6 +99,8 @@ public class BarChartRasterizer implements Rasterizer<CategoricalBarChart> { nonexistentDataText = canvas.getRepresentation().getProperty("general.nonexistentDataText").toString(); + mBrailleLanguage = BrailleLanguage.Language.valueOf(canvas.getRepresentation().getProperty("general.brailleLanguage").toString()); + // Load textures double[] rotate90 = {0, 0, 0, 1, 1, 0}; registerTexture(new Texture<>(TexturedArea.BOTTOM_T_PATTERN).applyAffineTransformation(rotate90), 0, 0); @@ -139,7 +142,7 @@ public class BarChartRasterizer implements Rasterizer<CategoricalBarChart> { // PHASE 1 - LAYOUT: The following calculations will divide the canvas area to create the basic chart layout. // Diagram Title String title = diagram.getTitle(); - int titleLength = mTextRasterizer.getBrailleStringLength(title); + int titleLength = mTextRasterizer.getBrailleStringLength(title, mBrailleLanguage); int titleHeight = (int) Math.ceil(titleLength / referenceCellArea.getWidth()); if (titleHeight > mMaximumTitleHeightCells) { throw new InsufficientRenderingAreaException("Title is too long. (Exceeds maximum height)"); @@ -198,20 +201,20 @@ public class BarChartRasterizer implements Rasterizer<CategoricalBarChart> { // PHASE 2 - RASTERIZING: Now, every element of the chart will be drawn onto the according area. // Diagram Title - mTextRasterizer.rasterize(new BrailleText(title, titleDotArea), canvas); + mTextRasterizer.rasterize(new BrailleText(title, titleDotArea, mBrailleLanguage), canvas); // Y-Axis: no units, no tickmarks Axis yAxis = new Axis(Axis.Type.Y_AXIS, originXDotCoordinate, originYDotCoordinate, 1, 0); yAxis.setBoundary(yAxisDotArea); mAxisRasterizer.rasterize(yAxis, canvas); // Y-Axis name - mTextRasterizer.rasterize(new BrailleText(diagram.getYAxisName(), yAxisNameDotArea), canvas); + mTextRasterizer.rasterize(new BrailleText(diagram.getYAxisName(), yAxisNameDotArea, mBrailleLanguage), canvas); // X-Axis: units and labels Axis xAxis = new Axis(Axis.Type.X_AXIS, originXDotCoordinate, originYDotCoordinate, X_AXIS_UNIT_SIZE_DOTS, X_AXIS_TICK_SIZE_DOTS); xAxis.setBoundary(xAxisDotArea); xAxis.setLabels(generateNumericAxisLabels(xAxisScaling, xAxisScalingMagnitude, negativeAvailableUnits, positiveAvailableUnits)); mAxisRasterizer.rasterize(xAxis, canvas); // X-Axis name - mTextRasterizer.rasterize(new BrailleText(diagram.getXAxisName(), xAxisNameDotArea), canvas); + mTextRasterizer.rasterize(new BrailleText(diagram.getXAxisName(), xAxisNameDotArea, mBrailleLanguage), canvas); // The actual groups and bars: // This is done by iterating through the diagram data set and drawing borders with the respective padding based on whether switched // from one bar to another or a group to another. In between, the bars are rasterized as textured areas, with a line on the bars top. @@ -270,7 +273,7 @@ public class BarChartRasterizer implements Rasterizer<CategoricalBarChart> { } // PHASE 3 - DIAGRAM LEGEND: Symbols and textures are explained in the legend which will be created by the LegendRasterizer - Legend diagramLegend = new Legend(title); // Create a legend container + Legend diagramLegend = new Legend(title, mBrailleLanguage); // Create a legend container diagramLegend.addSymbolExplanation("Achsenskalierung:", "X-Achse", "Faktor " + xAxisScalingMagnitude); // Explain axis scaling diagramLegend.addSymbolExplanationGroup("Kategorien:", groupNameExplanations); // Explain bar group single character captions if (textureExplanations.size() > 1) { // Explain textures (if multiple of them were used) diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleText.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleText.java index 786a34ff1bbd019f63bbc3f01bb0e7ad2a023295..12be5aa30abae24fc2e7b747744e6baa57e88173 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleText.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleText.java @@ -15,6 +15,10 @@ public class BrailleText implements Renderable { private String mContent; private Rectangle mArea; + /** + * Getter for the associated language used in this braille text. + * @return String containing the language + */ public String getLanguage() { return mLanguage; } @@ -32,7 +36,7 @@ public class BrailleText implements Renderable { mLanguage = "de-g0.utb"; } - public BrailleText(final String content, final Rectangle area, BrailleLanguage.Language language) { + public BrailleText(final String content, final Rectangle area, final BrailleLanguage.Language language) { setText(content); setArea(area); mLanguage = BrailleLanguage.getCorrectLanguage(language); diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleTextRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleTextRasterizer.java index c0e89d1131bfe096a1e63fab871f3637c4f527e1..2cb6cf2b4db756b8d06ea6fe56f8cc9f752a0a3f 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleTextRasterizer.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/BrailleTextRasterizer.java @@ -11,7 +11,6 @@ import de.tudresden.inf.mci.brailleplot.printerbackend.NotSupportedFileExtension /** * A rasterizer for text on braille grids. This class is still a stub and must be implemented! -<<<<<<< HEAD * @version 2019.08.17 * @author Leonard Kupper, Andrey Ruzhanskiy */ diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Legend.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Legend.java index 1f4b13c4d97c55a9971da6323b5e6b23c3f4f762..feb7567a30891b594a71ebcfa90739af0ed532fe 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Legend.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Legend.java @@ -1,29 +1,45 @@ package de.tudresden.inf.mci.brailleplot.rendering; +import de.tudresden.inf.mci.brailleplot.rendering.language.BrailleLanguage; + import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; /** * Simple representation of a legend. - * @author Leonard Kupper - * @version 2019.08.29 + * @author Leonard Kupper, Andrey Ruzhanskiy + * @version 2019.09.25 */ public class Legend implements Renderable { private String mTitle; + private BrailleLanguage.Language mLanguage; 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<>(); private int mTextureExampleWidthCells = 1; private int mTextureExampleHeightCells = 1; /** - * Constructor. Creates a legend. + * Constructor. Creates a legend with default language (DE_BASISSCHRIFT). * @param title The title of the legend. */ public Legend(final String title) { setTitle(title); + setLanguage(BrailleLanguage.Language.DE_BASISSCHRIFT); + } + + /** + * Constructor. Creates a legend with a defined language. + * @param title The title of the legend. + * @param language A {@link BrailleLanguage.Language}. + */ + public Legend(final String title, final BrailleLanguage.Language language) { + setTitle(title); + setLanguage(language); } /** @@ -34,6 +50,14 @@ public class Legend implements Renderable { mTitle = Objects.requireNonNull(title); } + /** + * Sets the braille language and level. + * @param language The new language. + */ + public void setLanguage(final BrailleLanguage.Language language) { + mLanguage = Objects.requireNonNull(language); + } + /** * Gets the current title of the legend. * @return A {@link String} containing the title. @@ -42,6 +66,14 @@ public class Legend implements Renderable { return mTitle; } + /** + * Gets the current braille language and level. + * @return A {@link BrailleLanguage.Language} determining the language and braille level. + */ + public BrailleLanguage.Language getLanguage() { + return mLanguage; + } + /** * 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") @@ -55,6 +87,40 @@ public class Legend implements Renderable { 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") @@ -119,4 +185,5 @@ public class Legend implements Renderable { final int getTextureExampleHeightCells() { return mTextureExampleHeightCells; } + } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LegendRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LegendRasterizer.java index 76509b4e26c9e24254127709d508b82387810283..68d31c85f541132713dad209f1c7526d00086481 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LegendRasterizer.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LegendRasterizer.java @@ -4,13 +4,15 @@ import de.tudresden.inf.mci.brailleplot.layout.InsufficientRenderingAreaExceptio 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 de.tudresden.inf.mci.brailleplot.rendering.language.BrailleLanguage; 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 - * @version 2019.08.28 + * @author Leonard Kupper, Andrey Ruzhanskiy + * @version 2019.09.25 */ public class LegendRasterizer implements Rasterizer<Legend> { @@ -19,7 +21,9 @@ public class LegendRasterizer implements Rasterizer<Legend> { 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 + private BrailleLanguage.Language mLanguage; + private String mLegendKeyword; // title for the legend + private static final BrailleLanguage.Language EXPLANATION_LIST_LANGUAGE = BrailleLanguage.Language.DE_BASISSCHRIFT; // Sub rasterizers private LiblouisBrailleTextRasterizer mTextRasterizer; @@ -36,44 +40,86 @@ public class LegendRasterizer implements Rasterizer<Legend> { mTextRasterizer = new LiblouisBrailleTextRasterizer(canvas.getPrinter()); mCanvas = canvas; mLegend = legend; + mLegendKeyword = mCanvas.getRepresentation().getProperty("general.legendKeyword").toString(); // Create a fresh page on the canvas. - MatrixData<Boolean> page = canvas.getNewPage(); + canvas.getNewPage(); Rectangle referenceCellArea = canvas.getCellRectangle(); try { // Write "Legend" keyword + title - writeLine(LEGEND_KEYWORD + " " + legend.getTitle(), referenceCellArea); + setLanguage(legend.getLanguage()); + writeLine(mLegendKeyword + " " + legend.getTitle(), referenceCellArea); - // String explanation lists - for (Map.Entry<String, Map<String, String>> list : legend.getSymbolExplanationGroups().entrySet()) { + // Texture explanation lists + for (Map.Entry<String, Map<Texture<Boolean>, String>> list : legend.getTextureExplanationGroups().entrySet()) { String groupName = list.getKey(); + setLanguage(legend.getLanguage()); writeLine("", referenceCellArea); // Leave space of one empty line - writeLine(groupName + ":", referenceCellArea); + writeLine(groupName, referenceCellArea); moveIndentation(referenceCellArea, EXPLANATION_TEXT_INDENTATION_CELLS); // set indentation - for (Map.Entry<String, String> explanation : list.getValue().entrySet()) { - String symbol = explanation.getKey(); + setLanguage(EXPLANATION_LIST_LANGUAGE); + for (Map.Entry<Texture<Boolean>, String> explanation : list.getValue().entrySet()) { + Texture<Boolean> texture = explanation.getKey(); String description = explanation.getValue(); - writeLine(symbol + " - " + description, referenceCellArea); + drawTextureExample(referenceCellArea, texture, description); } 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 explanation lists + for (Map.Entry<String, Map<String, String>> list : legend.getSymbolExplanationGroups().entrySet()) { String groupName = list.getKey(); + setLanguage(legend.getLanguage()); writeLine("", referenceCellArea); // Leave space of one empty line - writeLine(groupName + ":", referenceCellArea); + 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(); + setLanguage(EXPLANATION_LIST_LANGUAGE); + for (Map.Entry<String, String> explanation : list.getValue().entrySet()) { + String symbol = explanation.getKey(); String description = explanation.getValue(); - drawTextureExample(referenceCellArea, texture, description); + writeLine(symbol + " " + description, referenceCellArea); } moveIndentation(referenceCellArea, -1 * EXPLANATION_TEXT_INDENTATION_CELLS); // reset indentation } + // Columnview + if (legend.getColumnView().size() > 0) { + setLanguage(legend.getLanguage()); + writeLine(legend.getColumnViewTitle(), referenceCellArea); + for (Map.Entry<String, Map<String, String>> list : legend.getColumnView().entrySet()) { + Rectangle columnCellArea = new Rectangle(referenceCellArea); + setLanguage(legend.getLanguage()); + 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; + + setLanguage(EXPLANATION_LIST_LANGUAGE); + 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); + } + } + } catch (Rectangle.OutOfSpaceException e) { throw new InsufficientRenderingAreaException("The amount of data in the legend does not fit on the format.", e); } @@ -131,15 +177,23 @@ public class LegendRasterizer implements Rasterizer<Legend> { cellArea.setWidth(cellArea.getWidth() - indent); } - private void writeLine(final String text, final Rectangle cellArea) throws InsufficientRenderingAreaException, Rectangle.OutOfSpaceException { + 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 textLength = mTextRasterizer.getBrailleStringLength(text, getLanguage()); int textHeight = max(1, (int) Math.ceil(textLength / cellArea.getWidth())); Rectangle textLineDotArea = mCanvas.toDotRectangle(cellArea.removeFromTop(textHeight)); - mTextRasterizer.rasterize(new BrailleText(text, textLineDotArea), mCanvas); + mTextRasterizer.rasterize(new BrailleText(text, textLineDotArea, getLanguage()), mCanvas); + return min(textLength, cellArea.intWrapper().getWidth()); } + private void setLanguage(final BrailleLanguage.Language language) { + mLanguage = language; + } + + private BrailleLanguage.Language getLanguage() { + return mLanguage; + } } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java index e214755c5ff66fbe0a4b534b26d63eb1aa081b3b..08201f0a21e60d78e40478073803bdbb92582d7d 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LiblouisBrailleTextRasterizer.java @@ -85,9 +85,7 @@ public class LiblouisBrailleTextRasterizer implements Rasterizer<BrailleText> { TranslationResult result = null; try { result = mTranslator.translate(data.getText(), null, null, null, DisplayTable.StandardDisplayTables.DEFAULT); - } catch (TranslationException e) { - e.printStackTrace(); - } catch (DisplayException e) { + } catch (TranslationException | DisplayException e) { e.printStackTrace(); } String[] resultAsArray = result.getBraille().split(""); diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LineChartRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LineChartRasterizer.java new file mode 100644 index 0000000000000000000000000000000000000000..b7c44f2d795f4b0db3ede022a72bada6c1f96967 --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LineChartRasterizer.java @@ -0,0 +1,565 @@ +package de.tudresden.inf.mci.brailleplot.rendering; + +import de.tudresden.inf.mci.brailleplot.datacontainers.PointList; +import de.tudresden.inf.mci.brailleplot.datacontainers.SimplePointListImpl; +import de.tudresden.inf.mci.brailleplot.diagrams.LineChart; +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.point.Point2DDouble; +import de.tudresden.inf.mci.brailleplot.rendering.language.BrailleLanguage; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + +import static java.lang.Math.abs; +import static java.lang.Math.ceil; +import static java.lang.Math.log10; +import static java.lang.Math.pow; +import static java.lang.StrictMath.floor; +import static java.lang.StrictMath.round; + +/** + * Class representing a line chart rasterizer. + * @author Andrey Ruzhanskiy + * @version 2019.09.24 + */ +public class LineChartRasterizer implements Rasterizer<LineChart> { + private LineChart mDiagram; + private RasterCanvas mCanvas; + + private LiblouisBrailleTextRasterizer mTextRasterizer; + private LinearMappingAxisRasterizer mAxisRasterizer; + private Legend mLegend; + private final double tenth = 0.1, fifth = 0.2, quarter = 0.25, half = 0.5; + private final double[] mUnitScalings = new double[]{tenth, fifth, quarter, half, 1.0}; + + private int mXStepWidth; + private int mYStepWidth; + private double mDpiX; + private double mDpiY; + private final int mPaddingBetweenAxisTextAndDiagram = 3; + private final int mPaddingXandYText = 1; + private Rectangle mCellLineArea; + private boolean mPrintOnSamePaper = false; // If you want to print on the same paper, change this variable to true. + private BrailleLanguage.Language mLanguage; + private int mTitleHeight; + private int mPaddingToTitle = 1; + + + LineChartRasterizer() { + mAxisRasterizer = new LinearMappingAxisRasterizer(); + } + + /** + * Method for rasterizing a {@link LineChart}-diagram. + * This approach of an algorithm is minimaly adjustable by design, the algorithm for itself tries to find the best fitting + * for the given dataset. + * @param data The renderable representation. + * @param canvas An instance of {@link RasterCanvas} representing the target for the rasterizer output. + * @throws InsufficientRenderingAreaException If the data can not be rasterized due to shortcomings of the algorithm + * or because the data was too big. + */ + @Override + public void rasterize(final LineChart data, final RasterCanvas canvas) throws InsufficientRenderingAreaException { + if (data.equals(null)) { + throw new NullPointerException("The given data for the LineChartRasterizer was null!"); + } + if (canvas.equals(null)) { + throw new NullPointerException("The given canvas for the LineChartRasterizer was null!"); + } + mLanguage = BrailleLanguage.Language.valueOf(canvas.getRepresentation().getProperty("general.brailleLanguage").toString()); + mTitleHeight = canvas.getRepresentation().getProperty("general.maxTitleHeight").toInt(); + mTextRasterizer = new LiblouisBrailleTextRasterizer(canvas.getPrinter()); + mLegend = new Legend(data.getTitle(), mLanguage); // + + mCanvas = canvas; + mDiagram = data; + // Important: Its a cell rectangle, not a dot rectangle. + mCellLineArea = mCanvas.getCellRectangle(); + + // ITS CALCULATION TIME // + + // Step one: Calculate area needed for the title. + Rectangle titleArea = calculateTitle(); + Rectangle yAxisText; + Rectangle xAxisText; + try { + yAxisText = canvas.toDotRectangle(mCellLineArea.removeFromTop(mPaddingXandYText)); + xAxisText = canvas.toDotRectangle(mCellLineArea.removeFromBottom(mPaddingXandYText)); + } catch (Rectangle.OutOfSpaceException e) { + throw new InsufficientRenderingAreaException("The axis text cant fit to the layout.", e); + } + + // Step two: Calculate area needed for the x/y axis. + Rectangle xAxisArea = calculateXAxis(); + Rectangle yAxisArea = calculateYAxis(); + + // Step three: Calculate various things needed for computing the most simple approach for the x axis. + double rangeOfXValues = valueRangeOfXAxis(); + int xUnitsAvailable = calculateUnitsWidthInCells(xAxisArea); + mDpiX = calculateDPI(rangeOfXValues, xUnitsAvailable); + mXStepWidth = (int) findXAxisStepWidth(rangeOfXValues, xUnitsAvailable); + int xNumberOfTicks = (int) getNumberOfTicks(xUnitsAvailable); + Rectangle xAxisBound = xAxisArea.scaledBy(mCanvas.getCellWidth(), mCanvas.getCellHeight()); // Change to canvas convert toDotRectangle + int originY = xAxisBound.intWrapper().getY(); + int originX = xAxisBound.intWrapper().getX(); + + // Step four: Same thing for the y axis. + Rectangle yAxisBound = yAxisArea.scaledBy(mCanvas.getCellWidth(), mCanvas.getCellHeight()); + int yOriginY = originY - 1; // Drawing the diagram so that the y = 0 lies not on the x axis + int yOriginX = yAxisBound.intWrapper().getRight(); + double rangeOfYValues = valueRangeOfYAxis(); + int yUnitsAvailable = calculateUnitsHeightInCells(yAxisArea); + mYStepWidth = findYAxisStepWidth(rangeOfYValues, yUnitsAvailable); + mDpiY = calculateDPI(rangeOfYValues, yUnitsAvailable); + int yNumberOfTicks = (int) getNumberOfTicks(yUnitsAvailable); + + // Step five: Setting correct labels for x and y axis. + Map<String, String> xLabelsForLegend = new TreeMap<>(); + Map<String, String> yLabelsForLegend = new TreeMap<>(); + Map<Integer, String> xLabels = setCorrectLabelsforX(rangeOfXValues, xNumberOfTicks, mDpiX, xLabelsForLegend); + Map<Integer, String> yLabels = setCorrectLabelsforY(rangeOfYValues, yNumberOfTicks, mDpiY, yLabelsForLegend); + + // Step six: Filling the legend. + mLegend.addSymbolExplanation("Achsenskalierung:", "X-Achse", "Faktor " + mDpiX); + mLegend.addSymbolExplanation("Achsenskalierung:", "Y-Achse", "Faktor " + mDpiY); + mLegend.setColumnViewTitle("Werte der Tickmarks"); + setLabelsXForLegend(xLabelsForLegend); + setLabelsYForLegend(yLabelsForLegend); + + // Step seven: Iterate through the lines, rasterize the axis for each paper. + LegendRasterizer mLegendRasterizer = new LegendRasterizer(); + Iterator<PointList> iter = mDiagram.getData().iterator(); + while (iter.hasNext()) { + rasterizeTitle(data.getTitle(), titleArea); + rasterizeXAxis(originY, originX, mXStepWidth, xAxisBound, xLabels); + rasterizeYAxis(yOriginY, yOriginX, mYStepWidth, yAxisBound, yLabels); + mTextRasterizer.rasterize(new BrailleText(data.getYAxisName(), yAxisText, BrailleLanguage.Language.GERMAN_BASISSCHRIFT), mCanvas); + mTextRasterizer.rasterize(new BrailleText(data.getXAxisName(), xAxisText, BrailleLanguage.Language.GERMAN_BASISSCHRIFT), mCanvas); + rasterizeData(mDiagram.getMinX(), mDiagram.getMinY(), iter.next()); + if (iter.hasNext() && !mPrintOnSamePaper) { + mCanvas.getNewPage(); + } + } + // Last Step eight: Rasterize the legend (only needed one time). + mLegendRasterizer.rasterize(mLegend, mCanvas); + } + + // Various helper methods // + + /** + * Method for setting the correct x-labels to the {@link Legend}. + * Places first the value of the map and then the corresponding key. + * @param labelsForLegend A map containing the values and the letters which will be put on the legend. + */ + private void setLabelsXForLegend(final Map<String, String> labelsForLegend) { + mLegend.addColumn("X-Achse", labelsForLegend); + } + + /** + * Method for setting the correct y-labels to the {@link Legend}. + * Places first the value of the map and then the corresponding key. + * @param labelsForLegend A map containing the values and the letters which will be put on the legend. + */ + private void setLabelsYForLegend(final Map<String, String> labelsForLegend) { + mLegend.addColumn("Y-Achse", labelsForLegend); + } + + /** + * Method for rasterizing the data inside a {@link LineChart}. + * @param globalMinX The global minimum of the x values in the {@link LineChart}. + * @param globalMinY The global minimum of the y values in the {@link LineChart}. + * @param next The {@link PointList} containing the data for rasterization. + */ + private void rasterizeData(final double globalMinX, final double globalMinY, final PointList next) { + PointList sorted = next.sortXAscend(); + SimplePointListImpl points = rasterizePoints(sorted, globalMinX, globalMinY); + Iterator<Point2DDouble> iter = points.getListIterator(); + Point2DDouble previous = null; + while (iter.hasNext()) { + Point2DDouble current = iter.next(); + if (previous == null) { + previous = current; + continue; + } + // Here you can swap bresenham to a new linerasterizing algorithm + bresenham(previous.getX(), previous.getY(), current.getX(), current.getY()); + previous = current; + } + } + + /** + * Bresenham algorithm for rasterizing lines. + * Important: It translates the y coordinates to a normal coordinate-system. Currently, the Y-coordinate of the + * {@link RasterCanvas} lies on the left upper corner, representing 0. But Bresenham assumes the Y-coordinate lies + * in the left buttom corner. The difference is that the Y-Coordinate grows in a normal coordinate system as it lies + * further and further above, but in a {@link RasterCanvas} it actually decreases as it goes further up. + * Before setting the point on to the {@link RasterCanvas} it translates it back to the {@link RasterCanvas}-coordinate + * system. + * @param xStart X-coordinate of the startpoint. + * @param yStart Y-coordinate of the startpoint. + * @param xEnd X-coordinate of the endpoint. + * @param yEnd Y-coordinate of the endpoint. + */ + @SuppressWarnings("avoidinlineconditionals") + private void bresenham(final Double xStart, final Double yStart, final Double xEnd, final Double yEnd) { + int y0 = (int) (mCanvas.toDotRectangle(mCellLineArea).intWrapper().getHeight() - yStart); + int y1 = (int) (mCanvas.toDotRectangle(mCellLineArea).intWrapper().getHeight() - yEnd); + int x0 = (int) (xStart.doubleValue()); + int x1 = (int) (xEnd.doubleValue()); + int dx = abs(x1 - x0); + int dy = -abs(y1 - y0); + int sx = x0 < x1 ? 1 : -1; + int sy = y0 < y1 ? 1 : -1; + int err = dx + dy; + int e2; + while (true) { + mCanvas.getCurrentPage().setValue((int) (mCanvas.toDotRectangle(mCellLineArea).getHeight() - y0), (int) x0, true); + if (x0 == x1 && y0 == y1) { + break; + } + e2 = 2 * err; + if (e2 > dy) { + err += dy; + x0 += sx; + } + if (e2 < dx) { + err += dx; + y0 += sy; + } + } + } + + /** + * Method for rasterizing the points. + * @param list A {@link PointList} containing points which will be rasterized. + * @param globalMinX The global minimum of the x values in the {@link LineChart}. + * @param globalMinY The global minimum of the y values in the {@link LineChart}. + * @return The {@link SimplePointListImpl} containing the converted coordinates of the points. + */ + private SimplePointListImpl rasterizePoints(final PointList list, final double globalMinX, final double globalMinY) { + Objects.requireNonNull(list, "The given PointList for the rasterization of points was null!"); + double xMin = globalMinX; + double yMin = globalMinY; + Iterator<Point2DDouble> iter = list.getListIterator(); + Rectangle canvas = mCanvas.toDotRectangle(mCellLineArea); + double canvasStartX = canvas.intWrapper().getX(); + double canvasStartY = canvas.intWrapper().getBottom(); + SimplePointListImpl result = new SimplePointListImpl(); + while (iter.hasNext()) { + Point2DDouble current = iter.next(); + double currentValueX = current.getX() - xMin; + double currentValueY = current.getY() - yMin; + double stepX = currentValueX / mDpiX; + double stepY = currentValueY / mDpiY; + result.pushBack(new Point2DDouble(round(canvasStartX + mXStepWidth * mCanvas.getCellWidth() * stepX), round(canvasStartY - mYStepWidth * mCanvas.getCellHeight() * stepY))); + mCanvas.getCurrentPage().setValue((int) round(canvasStartY - mYStepWidth * mCanvas.getCellHeight() * stepY), (int) round(canvasStartX + mXStepWidth * mCanvas.getCellWidth() * stepX), true); + } + result.calculateExtrema(); + return result; + } + + + /** + * Method for creating a map containing the key-value pair for the datapoints for the y-axis. The key represents an integer, which has + * no special meaning (but is needed for the {@link LinearMappingAxisRasterizer}, the value is a letter which will be + * drawn on to the diagram. This same letter will appear on the legend with its representational value + * (for example: 'a' -> 0.5 ). + * @param rangeOfYValues The value range for the y datapoints. + * @param numberOfTicks The number of ticks wich will be drawn on to the diagram. + * @param dpi The resolution, or in other words, what one step for the tickmark along the axis means for the datapoints (for example 0.5 means + * for each tickmark the coordinatesystem where the datapoints lies is increased by 0.5) + * @param yLabelsForLegend The map (can be empty, but must be initialized) in which the representation of the letters will be stored. + * For example: 2.5 -> a, 3.0 -> b and so on. + * @return A map containing the correct number of labels which will be needed to address all datapoints in {@link LineChart}. + */ + @SuppressWarnings({"finalparameters", "magicnumber"}) + private Map<Integer, String> setCorrectLabelsforY(final double rangeOfYValues, final int numberOfTicks, double dpi, Map<String, String> yLabelsForLegend) { + Objects.requireNonNull(yLabelsForLegend, "The given map for setting the correct labels for the y-axis was null!"); + double min = mDiagram.getData().getMinY(); + Map<Integer, String> result = new HashMap<>(); + double tmpDpi = dpi; + + // According to a not representative study the y axis should start with 'a' on the highest value, not the lowest. + // So we need to calculate an offset and decrement the letter + // Works currently only with letters represented in ASCII + int datapoints = (int) ceil(rangeOfYValues / dpi); + int range = 25; // Number of letters in the ASCII alphabet + int offset = range - datapoints; + byte z = 0x7A; + byte letterAsByte = (byte) (z - offset); + char letter = (char) letterAsByte; + + + for (int i = 0; i < numberOfTicks; i++) { + result.put(i, String.valueOf(letter)); + if (i == 0) { + yLabelsForLegend.put(String.valueOf(letter), String.valueOf(min)); + } else { + yLabelsForLegend.put(String.valueOf(letter), String.valueOf((dpi + min))); + dpi = dpi + tmpDpi; + } + letter--; + if (i >= datapoints) { + break; + } + } + return result; + } + + /** + * Calculates the resolution (meaning how much in the datapoint we go if we do one tickmark-step). + * @param rangeOfValues The range of values in the {@link LineChart}. + * @param unitsAvailable How many units (Braillecells) are available on the axis. + * @return Double representing the resolution. + */ + @SuppressWarnings("magicnumber") + private double calculateDPI(final double rangeOfValues, final int unitsAvailable) { + if (unitsAvailable < 0) { + throw new RuntimeException("The units available were less then zero!"); + } + double minRangePerUnit = rangeOfValues / unitsAvailable; // this range must fit into one 'axis step' + double orderOfMagnitude = pow(10, ceil(log10(minRangePerUnit))); + double scaledRangePerUnit = 0; + for (double scaling : mUnitScalings) { + scaledRangePerUnit = (scaling * orderOfMagnitude); + if (scaledRangePerUnit >= minRangePerUnit) { + break; + } + } + return scaledRangePerUnit; + } + + /** + * Method for creating a map containing the key-value pair for the datapoints for the x-axis. The key represents an integer, which has + * no special meaning (but is needed for the {@link LinearMappingAxisRasterizer}, the value is a letter which will be + * drawn on to the diagram. This same letter will appear on the legend with its representational value + * (for example: 'a' -> 0.5 ). + * @param rangeOfXValues The value range for the y datapoints. + * @param numberOfTicks The number of ticks wich will be drawn on to the diagram. + * @param dpi The resolution, or in other words, what one step for the tickmark along the axis means for the datapoints (for example 0.5 means + * for each tickmark the coordinatesystem where the datapoints lies is increased by 0.5) + * @param xLabelsForLegend The map (can be empty, but must be initialized) in which the representation of the letters will be stored. + * For example: 2.5 -> a, 3.0 -> b and so on. + * @return A map containing the correct number of labels which will be needed to address all datapoints in {@link LineChart}. + */ + @SuppressWarnings("finalparameters") + private Map<Integer, String> setCorrectLabelsforX(final double rangeOfXValues, final int numberOfTicks, double dpi, Map<String, String> xLabelsForLegend) { + Objects.requireNonNull(xLabelsForLegend, "The given map to set the correct labels for the x-axis was null!"); + double min = mDiagram.getMinX(); + Map<Integer, String> result = new HashMap<>(); + double tmpDpi = dpi; + char letter = 'a'; + double datapoints = rangeOfXValues / dpi; + for (int i = 0; i < numberOfTicks; i++) { + result.put(i, String.valueOf(letter)); + if (i == 0) { + xLabelsForLegend.put(String.valueOf(letter), String.valueOf(min)); + } else { + xLabelsForLegend.put(String.valueOf(letter), String.valueOf((dpi + min))); + dpi = dpi + tmpDpi; + } + letter++; + if (i >= datapoints) { + break; + } + } + return result; + } + + /** + * Method for the calculation of the stepwidth for the y-axis on the canvas. + * Important: Not meant in the datapoints, but on the canvas. Currently returning 1. + * @param rangeOfYValues The range of values along the y-axis. + * @param yUnitsAvailable The number of available units along the y-axis, measured in braillecells. + * @return Integer representing the number of braillecells between two tickmarks. + */ + private int findYAxisStepWidth(final double rangeOfYValues, final int yUnitsAvailable) { + // You can change the following step width to cater your needs. + // The minimum int taken by the y-axis rasterizer is 1 + return 1; + } + + /** + * Method for calculating the height for a given {@link Rectangle} in braillecells. + * @param rectangle The rectangle for which the height is computed. + * @return Integer, representing the height in braillecells. + */ + private int calculateUnitsHeightInCells(final Rectangle rectangle) { + // Needed because one can get a height that encapsulates a fraction of a braillecell, so we need to ensure that + // we work on whole cells. + return (int) floor((rectangle.getHeight() * mCanvas.getCellHeight()) / mCanvas.getCellHeight()); + } + + /** + * Method for rasterizing the title of the diagram. + * @param title String which contains the title of the diagram. + * @param titleArea The {@link Rectangle} on which the the text will be rasterized. + */ + private void rasterizeTitle(final String title, final Rectangle titleArea) { + BrailleText diagramTitle = new BrailleText(title, titleArea); + try { + mTextRasterizer.rasterize(diagramTitle, mCanvas); + } catch (InsufficientRenderingAreaException e) { + e.printStackTrace(); + } + } + + /** + * Wrapper method for creating an X-{@link Axis} and rasterize it. Delegates to the {@link LinearMappingAxisRasterizer} for rasterizing. + * @param originY The y coordinate of the position where the axis line and the tickmark and label corresponding to the value '0' is placed. + * @param originX The x coordinate of the position where the axis line and the tickmark and label corresponding to the value '0' is placed. + * @param stepWidthX The distance between two tickmarks on the axis in cells. This will be automatically converted in dots for the {@link Axis}. + * @param xAxisBound The x-axis bound so that the borders are considered. + * @param labels Map containing the labels (letters). + */ + private void rasterizeXAxis(final int originY, final int originX, final int stepWidthX, final Rectangle xAxisBound, final Map<Integer, String> labels) { + Axis xAxis = new Axis(Axis.Type.X_AXIS, originX, originY, stepWidthX * mCanvas.getCellWidth(), 2); + xAxis.setBoundary(xAxisBound); + xAxis.setLabels(labels); + Rectangle test = xAxis.getBoundary(); + try { + mAxisRasterizer.rasterize(xAxis, mCanvas); + } catch (InsufficientRenderingAreaException e) { + e.printStackTrace(); + } + } + + /** + * Wrapper method for creating an Y-{@link Axis} and rasterize it. Delegates to the {@link LinearMappingAxisRasterizer} for rasterizing. + * @param originY The y coordinate of the position where the axis line and the tickmark and label corresponding to the value '0' is placed. + * @param originX The x coordinate of the position where the axis line and the tickmark and label corresponding to the value '0' is placed. + * @param stepWidthY The distance between two tickmarks on the axis in cells. This will be automatically converted in dots for the {@link Axis}. + * @param yAxisBound The x-axis bound so that the borders are considered. + * @param labels {@link Map} containing the labels (letters). + */ + @SuppressWarnings("magicnumber") + private void rasterizeYAxis(final int originY, final int originX, final int stepWidthY, final Rectangle yAxisBound, final Map<Integer, String> labels) { + Axis yAxis = new Axis(Axis.Type.Y_AXIS, originX, originY, stepWidthY * mCanvas.getCellHeight(), -2); + yAxis.setBoundary(yAxisBound); + yAxis.setLabels(labels); + try { + mAxisRasterizer.rasterize(yAxis, mCanvas); + } catch (InsufficientRenderingAreaException e) { + e.printStackTrace(); + } + } + + /** + * Method for cutting off the right {@link Rectangle} from the whole {@link RasterCanvas}. + * Internally it uses the mDiagram variable. + * @return {@link Rectangle} with the correct length and width so that the diagramtitle can be rasterized on it. + * @throws InsufficientRenderingAreaException If the text is too big to fit on the {@link RasterCanvas}. + */ + private Rectangle calculateTitle() throws InsufficientRenderingAreaException { + if (mDiagram.getTitle().isEmpty()) { + throw new IllegalArgumentException("The title in LineChartRasterizer was empty!"); + } + int widthOfCompleteArea = mCellLineArea.intWrapper().getWidth(); + int titleBarHeight = mTextRasterizer.calculateRequiredHeight(mDiagram.getTitle(), widthOfCompleteArea, mCanvas, BrailleLanguage.Language.GERMAN_BASISSCHRIFT); + try { + return mCellLineArea.removeFromTop(mCanvas.getCellYFromDotY(mTitleHeight) + mPaddingToTitle); + } catch (Rectangle.OutOfSpaceException e) { + throw new InsufficientRenderingAreaException("Not enough space to build the title area for the line chart!"); + } + } + + /** + * Method for cutting off the right {@link Rectangle} from the whole {@link RasterCanvas}. + * Currently it cuts of from the bottom and left by the amount of the offset variable + * @return {@link Rectangle} for the x-axis. + * @throws InsufficientRenderingAreaException If the offset amount cant be cut off the mCellLineArea. + */ + private Rectangle calculateXAxis() throws InsufficientRenderingAreaException { + Objects.requireNonNull(mCellLineArea, "The given Rectangle for the x axis to be removed from was null!"); + try { + Rectangle result = mCellLineArea.removeFromBottom(mPaddingBetweenAxisTextAndDiagram); + result.removeFromLeft(mPaddingBetweenAxisTextAndDiagram); + return result; + } catch (Rectangle.OutOfSpaceException e) { + throw new InsufficientRenderingAreaException("Not enough space to build the X-Axis for the line chart!"); + } + } + + /** + * Method for cutting off the right {@link Rectangle} from the whole {@link RasterCanvas}. + * Currently it cuts of from the left by the amount of the offset variable + * @return {@link Rectangle} for the y-axis. + * @throws InsufficientRenderingAreaException If the offset amount cant be cut off the mCellLineArea. + */ + private Rectangle calculateYAxis() throws InsufficientRenderingAreaException { + Objects.requireNonNull(mCellLineArea, "The given Rectangle for the y axis to be removed from was null!"); + try { + return mCellLineArea.removeFromLeft(mPaddingBetweenAxisTextAndDiagram); + } catch (Rectangle.OutOfSpaceException e) { + throw new InsufficientRenderingAreaException("Not enough space to build the Y-Axis for the line chart!"); + } + } + + /** + * Method for calculating the valuerange of the x-axis. + * @return {@link Double} representing the value range of the x-axis. + */ + private double valueRangeOfYAxis() { + Objects.requireNonNull(mDiagram, "The given linechart for the calculation of the value range of the y-axis was null!"); + double minY = mDiagram.getMinY(); + double maxY = mDiagram.getMaxY(); + double valueRangeOfYAxis; + if (minY >= 0) { + valueRangeOfYAxis = maxY - minY; + } else { + valueRangeOfYAxis = abs(maxY) + abs(minY); + } + return valueRangeOfYAxis; + } + + /** + * Calculate width, measured in cells. Important: it divides by two and floors the result. The current axis rasterizer + * does not support a width of 1. + * @param rectangle The cell rectangle which you want to know the width. + * @return Width in cells divided by two and floored. + */ + private int calculateUnitsWidthInCells(final Rectangle rectangle) { + Objects.requireNonNull(rectangle, "The given rectangle for the calculation of its width was null!"); + return (int) floor((rectangle.getWidth() - 1) / 2); + } + + /** + * Method for calculating the value range of the x axis. + * @return {@link Double} representing the value range. + */ + private double valueRangeOfXAxis() { + Objects.requireNonNull(mDiagram, "The given linechart for the calculation of the value range of the x-axis was null!"); + double minX = mDiagram.getMinX(); + double maxX = mDiagram.getMaxX(); + return maxX - minX; + } + + /** + * Returns the number of ticks. Currently, it adds one to the given parameter. + * @param unitsAvailable How many units are available on the x axis. + * @return {@link Double} representing ticks available. + */ + private double getNumberOfTicks(final int unitsAvailable) { + if (unitsAvailable < 0) { + throw new RuntimeException("The units available was less then zero!"); + } + return unitsAvailable + 1; + } + + /** + * Method for finding the x axis step width. + * @param rangeOfXValues Representing the range of values. + * @param xUnitsAvailable Representing the availabe units on the xAxis. + * @return Currently always 2; + */ + private double findXAxisStepWidth(final double rangeOfXValues, final int xUnitsAvailable) { + // Most simple approach: always take the minimum stepwidth, which the x-axis rasterizer can handle + // The signature is not adjusted so that someone can change the calculation if he needs it + return 2; + } +} diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LinearMappingAxisRasterizer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LinearMappingAxisRasterizer.java index f20e2e41121d5339009e78f45d9248f02e7f54b6..5fc0baaed683beeb392fb3675a140e294bc2ae2b 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LinearMappingAxisRasterizer.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/LinearMappingAxisRasterizer.java @@ -127,7 +127,8 @@ public class LinearMappingAxisRasterizer implements Rasterizer<Axis> { labelCellX = mCanvas.getCellXFromDotX(dotX + (mTickSize + labelOffset)) + labelOffset; labelCellY = mCanvas.getCellYFromDotY(dotY); if (mTickSize < 0) { - labelCellArea = new Rectangle(labelCellX - (stringLength), labelCellY, stringLength, 1); + // Calculate x position of first character in right aligned text + labelCellArea = new Rectangle(labelCellX - (stringLength - 1), labelCellY, stringLength, 1); } else { labelCellArea = new Rectangle(labelCellX, labelCellY, stringLength, 1); } diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRenderer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRenderer.java index 7f41d757c56680a42633f8ca3397fdd9e8e53752..78b721d7175247763a63f116805f16239538ac5b 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRenderer.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/MasterRenderer.java @@ -6,6 +6,9 @@ import de.tudresden.inf.mci.brailleplot.diagrams.CategoricalBarChart; import de.tudresden.inf.mci.brailleplot.layout.InsufficientRenderingAreaException; import de.tudresden.inf.mci.brailleplot.layout.RasterCanvas; import de.tudresden.inf.mci.brailleplot.layout.SixDotBrailleRasterCanvas; + +import de.tudresden.inf.mci.brailleplot.diagrams.LineChart; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,11 +47,14 @@ public final class MasterRenderer { Rasterizer<CategoricalBarChart> barChartRasterizer = new BarChartRasterizer(); Rasterizer<Image> linearImageMapping = new ImageRasterizer(); Rasterizer<ScatterPlot> scatter = new ScatterPlotRasterizer(); + Rasterizer<LineChart> lineChart = new LineChartRasterizer(); mLogger.trace("Registering default rasterizers"); renderingBase.registerRasterizer(new FunctionalRasterizer<CategoricalBarChart>(CategoricalBarChart.class, barChartRasterizer)); renderingBase.registerRasterizer(new FunctionalRasterizer<Image>(Image.class, linearImageMapping)); renderingBase.registerRasterizer(new FunctionalRasterizer<ScatterPlot>(ScatterPlot.class, scatter)); + renderingBase.registerRasterizer(new FunctionalRasterizer<LineChart>(LineChart.class, lineChart)); + //renderingBase.registerRasterizer(new FunctionalRasterizer<ScatterPlot>(ScatterPlot.class, ScatterPlotRasterizing::fooRasterizing)); //... setRenderingContext(printer, representation, format, renderingBase); diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Texture.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Texture.java index f4f85f46ad028d3f1fc6d91c38f98ebf375e4742..8fdacb2273ac662d488e49ded640224d3724c63b 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Texture.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/Texture.java @@ -43,6 +43,7 @@ public class Texture<T> { * (x, y) is a vector describing the translation. * | a b | * | c d | is a linear transformation matrix. + * @return Returns a reference to this {@link Texture}. */ public Texture<T> setAffineTransformation(final double[] transformation) { if ((transformation.length != TRANSLATION_SIZE) && (transformation.length != TRANSFORMATION_SIZE)) { @@ -61,6 +62,7 @@ public class Texture<T> { * (x, y) is a vector describing the translation. * | a b | * | c d | is a linear transformation matrix. + * @return Returns a reference to this {@link Texture}. */ @SuppressWarnings("checkstyle:MagicNumber") public Texture<T> applyAffineTransformation(final double[] transformation) { diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/language/BrailleLanguage.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/language/BrailleLanguage.java index 724691c67c43636a23ce9fffa01e873ad1aa53f2..fd633ab8787bb48a2382c753c5f9b8980701e9f3 100644 --- a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/language/BrailleLanguage.java +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/language/BrailleLanguage.java @@ -1,13 +1,20 @@ package de.tudresden.inf.mci.brailleplot.rendering.language; /** - * Helper class for braillelanguage + * Helper class for braillelanguage. * @author Andrey Ruzhanskiy * @version 27.09.2019 */ + +@SuppressWarnings("HideUtilityClassConstructor") public class BrailleLanguage { - public static String getCorrectLanguage(Language language){ + /** + * Method to get the correct name of the table for the given enum. + * @param language Enum, for which the table is to be known. + * @return String containing the name of the table. + */ + public static String getCorrectLanguage(final Language language) { switch (language) { case GERMAN_VOLLSCHRIFT: case DE_VOLLSCHRIFT: @@ -18,11 +25,14 @@ public class BrailleLanguage { case GERMAN_KURZSCHRIFT: case DE_KURZSCHRIFT: return "de-g2.ctb"; + default: + throw new RuntimeException("Unsupported language given as braillelanguage! \"" + language.toString() + "\""); } - throw new RuntimeException("Unsupported language given as braillelanguage! \"" + language.toString() + "\""); } - + /** + * Enum describing the current supported braille languages and grammars. + */ public enum Language { DE_KURZSCHRIFT, DE_BASISSCHRIFT, diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/language/package-info.java b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/language/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..61d15ce437090456f96c52d9827fac4e2be7c3fd --- /dev/null +++ b/src/main/java/de/tudresden/inf/mci/brailleplot/rendering/language/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains the language helper class and the braille language declaration. + */ +package de.tudresden.inf.mci.brailleplot.rendering.language; \ No newline at end of file diff --git a/src/main/resources/config/default.properties b/src/main/resources/config/default.properties index 90039e37e7cf88ab4cc97bc4c9cc44245a26bdc7..74da5a7ca6688fbec357278ba89697217519bfbd 100644 --- a/src/main/resources/config/default.properties +++ b/src/main/resources/config/default.properties @@ -50,14 +50,16 @@ printer.raster.dotDiameter=1.5 format.default.page.width=210 format.default.page.height=297 format.default.margin.top=0 -format.default.margin.left=0 +format.default.margin.left=5 format.default.margin.bottom=0 format.default.margin.right=0 ### Diagram Formatting ### ================== +representation.general.brailleLanguage=DE_BASISSCHRIFT representation.general.nonexistentDataText=n/a +representation.general.legendKeyword=Legende: representation.general.maxTitleHeight=2 representation.rasterize.barChart.maxBarThickness=3 representation.rasterize.barChart.minBarThickness=1 diff --git a/src/main/resources/config/index_basic_d.properties b/src/main/resources/config/index_basic_d.properties index 0d83722d223c7941fa8953e7ca3a0164e17dee49..a403fe0b95f708fa625bfd40829235555bee2d74 100644 --- a/src/main/resources/config/index_basic_d.properties +++ b/src/main/resources/config/index_basic_d.properties @@ -1,7 +1,7 @@ # JProperties Printer & Format Configuration # # Embosser: Index Basic-D -# Version 1 Rev. 4 (19-08-21) +# Version 1 Rev. 5 (19-10-01) # # Description: # This is the main configuration file for use with the braille plot application @@ -17,7 +17,6 @@ printer.name=Index Basic-D V3 printer.mode=normalprinter -printer.brailletable=src/main/resources/mapping/eurobraille.properties printer.floatingDot.support=false # The following values represent the fixed indentation and maximum technical printing area of the embosser. diff --git a/src/main/resources/config/standard_formats.properties b/src/main/resources/config/standard_formats.properties index c3aee460f7adb21daafa6f86885548069edbd01f..502ca9e5465090f2e850f98e4e411a256e00cdb0 100644 --- a/src/main/resources/config/standard_formats.properties +++ b/src/main/resources/config/standard_formats.properties @@ -17,6 +17,16 @@ format.A4.margin.left=10 format.A4.margin.bottom=0 format.A4.margin.right=0 + +### A4 Landscape Format No Margin +### =================== +format.A4L.page.width=297 +format.A4L.page.height=210 +format.A4L.margin.top=0 +format.A4L.margin.left=5 +format.A4L.margin.bottom=0 +format.A4L.margin.right=0 + ### A5 Format Standard ### =================== format.A5.page.width=148 diff --git a/src/main/resources/examples/csv/1_scatter_plot.csv b/src/main/resources/examples/csv/1_scatter_plot.csv index 904d14586cc75f52b04e835a7a1802ba6894570c..c90c77fe69fa65fe7f492f6859dea5a2614b8031 100644 --- a/src/main/resources/examples/csv/1_scatter_plot.csv +++ b/src/main/resources/examples/csv/1_scatter_plot.csv @@ -1,5 +1,6 @@ -Linie1, ,1,7,9,2,10 - ,1,2,5,4,10 -Linie2, ,0,2,7,9,1,4 - ,3,9,4,2,5,7 - +Erste Gruppe,"2,14577671155962","3,83957088149638","2,74381418592686","4,44040259535766","3,05835335915677","3,51347910532925","4,50752129340682","3,10684847076423" +,"1,86189289220467","0,845650622246947","1,62491581811196","0,716873892169058","1,07960644510134","1,25420442725184","0,868213570630901","0,3131242131342" +Zweite Gruppe,"2,13687514878477","1,0900468273911","0,202664216808808","-0,117994978447507","1,91197730783847","0,472235461102831","1,01858439429409","1,54912208686837" +,"0,983807817453276","2,34845526278731","0,785524371519466","0,220854402806683","1,77117365732932","3,01226487117459","-0,327141232914824","-0,210388758733043" +Dritte Gruppe,"4,11271848986168","4,99244908180646","5,98316851481515","4,95765061866595","4,00636404848764","5,49799175180235","4,34309770180236","5,36675690932105" +,"5,09261289722065","4,92838187744164","4,47540951345638","3,45104975190378","5,64998407245105","4,88914941630864","5,92876353824873","6,67452703834106" diff --git a/src/main/resources/examples/csv/2_1_line_plot_equal_distance.csv b/src/main/resources/examples/csv/2_1_line_plot_equal_distance.csv new file mode 100644 index 0000000000000000000000000000000000000000..f7982779a1fd188735db9af69f5a7eeb7ff494d4 --- /dev/null +++ b/src/main/resources/examples/csv/2_1_line_plot_equal_distance.csv @@ -0,0 +1,5 @@ +Linie1, ,1,5,7,3,9 + ,1,2,5,4,10 +Linie2, ,0,2,4,6,8,10 + ,3,9,4,2,5,7 + diff --git a/src/main/resources/examples/csv/2_line_plot - small.csv b/src/main/resources/examples/csv/2_line_plot - small.csv new file mode 100644 index 0000000000000000000000000000000000000000..c715ad8052b2455f8f1971bbf70fc89cdf82f249 --- /dev/null +++ b/src/main/resources/examples/csv/2_line_plot - small.csv @@ -0,0 +1,4 @@ +Linie1, ,"0,1","0,7","0,9","0,2",1 + ,"0,1","0,2","0,5","0,4",1 + + diff --git a/src/main/resources/examples/csv/2_line_plot.csv b/src/main/resources/examples/csv/2_line_plot.csv index c90c77fe69fa65fe7f492f6859dea5a2614b8031..904d14586cc75f52b04e835a7a1802ba6894570c 100644 --- a/src/main/resources/examples/csv/2_line_plot.csv +++ b/src/main/resources/examples/csv/2_line_plot.csv @@ -1,6 +1,5 @@ -Erste Gruppe,"2,14577671155962","3,83957088149638","2,74381418592686","4,44040259535766","3,05835335915677","3,51347910532925","4,50752129340682","3,10684847076423" -,"1,86189289220467","0,845650622246947","1,62491581811196","0,716873892169058","1,07960644510134","1,25420442725184","0,868213570630901","0,3131242131342" -Zweite Gruppe,"2,13687514878477","1,0900468273911","0,202664216808808","-0,117994978447507","1,91197730783847","0,472235461102831","1,01858439429409","1,54912208686837" -,"0,983807817453276","2,34845526278731","0,785524371519466","0,220854402806683","1,77117365732932","3,01226487117459","-0,327141232914824","-0,210388758733043" -Dritte Gruppe,"4,11271848986168","4,99244908180646","5,98316851481515","4,95765061866595","4,00636404848764","5,49799175180235","4,34309770180236","5,36675690932105" -,"5,09261289722065","4,92838187744164","4,47540951345638","3,45104975190378","5,64998407245105","4,88914941630864","5,92876353824873","6,67452703834106" +Linie1, ,1,7,9,2,10 + ,1,2,5,4,10 +Linie2, ,0,2,7,9,1,4 + ,3,9,4,2,5,7 + diff --git a/src/test/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParserTest.java b/src/test/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParserTest.java index 436ef4d2be887c6df4d423c42497c91b68df58df..1749fa2d8e003b32f5f8e5fbfa30dd4a00e00e35 100644 --- a/src/test/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParserTest.java +++ b/src/test/java/de/tudresden/inf/mci/brailleplot/commandline/CommandLineParserTest.java @@ -1,5 +1,7 @@ package de.tudresden.inf.mci.brailleplot.commandline; +import org.apache.commons.cli.MissingOptionException; +import org.apache.commons.cli.ParseException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -16,7 +18,7 @@ class CommandLineParserTest { @Test void testParseLegalArgs() { - String[] args = {"-h", "--csv-path", "foobar"}; + String[] args = {"-h", "--csv-path", "foobar", "-p", "test", "-t", "title", "-x", "x-axis", "-y", "y-axis", "-d", "BarChart"}; Assertions.assertDoesNotThrow(() -> {commandLineParser.parse(args);}); } @@ -29,12 +31,13 @@ class CommandLineParserTest { @Test void testEmptyArgs() { String[] args = {""}; - Assertions.assertDoesNotThrow(() -> {commandLineParser.parse(args);}); + Assertions.assertThrows(ParsingException.class, () -> {commandLineParser.parse(args);}); } @Test void testBoolFlagRecognized() { - String[] args = {"-h"}; + final String param = "xyz"; + String[] args = {"--csv-path", param, "-p", param, "-t", param, "-x", param, "-y", param, "-d", param, "-i"}; SettingsReader settings; try { settings = commandLineParser.parse(args); @@ -42,14 +45,15 @@ class CommandLineParserTest { fail(); return; // Never executed, satisfy compiler } - Optional<Boolean> flag = settings.isTrue(SettingType.DISPLAY_HELP); + Optional<Boolean> flag = settings.isTrue(SettingType.INHIBIT_PRINT); Assertions.assertTrue(flag.isPresent()); Assertions.assertTrue(flag.get()); } @Test void testBoolFlagCorrectlyNotRecognized() { - String[] args = {""}; + final String param = "xyz"; + String[] args = {"--csv-path", param, "-p", param, "-t", param, "-x", param, "-y", param, "-d", param}; SettingsReader settings; try { settings = commandLineParser.parse(args); @@ -57,14 +61,14 @@ class CommandLineParserTest { fail(); return; // Never executed, satisfy compiler } - Optional<Boolean> flag = settings.isTrue(SettingType.DISPLAY_HELP); + Optional<Boolean> flag = settings.isTrue(SettingType.INHIBIT_PRINT); Assertions.assertFalse(flag.isPresent()); } @Test void testParameterRecognized() { final String param = "xyz"; - String[] args = {"--csv-path", param}; + String[] args = {"--csv-path", param, "-p", param, "-t", param, "-x", param, "-y", param, "-d", param}; SettingsReader settings; try { settings = commandLineParser.parse(args); @@ -72,14 +76,21 @@ class CommandLineParserTest { fail(); return; // Never executed, satisfy compiler } - Optional<String> flag = settings.getSetting(SettingType.CSV_LOCATION); - Assertions.assertTrue(flag.isPresent()); - Assertions.assertTrue(flag.get().equals(param)); + Optional<String> flag; + for (SettingType setting : new SettingType[]{ + SettingType.CSV_LOCATION, SettingType.PRINTER_CONFIG_PATH, SettingType.DIAGRAM_TITLE, + SettingType.X_AXIS_LABEL, SettingType.X_AXIS_LABEL, SettingType.DIAGRAM_TYPE} + ) { + flag = settings.getSetting(setting); + Assertions.assertTrue(flag.isPresent()); + Assertions.assertTrue(flag.get().equals(param)); + } } @Test void testParameterCorrectlyNotRecognized() { - String[] args = {""}; + final String param = "xyz"; + String[] args = {"--csv-path", param, "-p", param, "-t", param, "-x", param, "-y", param, "-d", param}; SettingsReader settings; try { settings = commandLineParser.parse(args); @@ -87,7 +98,7 @@ class CommandLineParserTest { fail(); return; // Never executed, satisfy compiler } - Optional<String> flag = settings.getSetting(SettingType.CSV_LOCATION); + Optional<String> flag = settings.getSetting(SettingType.SVG_EXPORT); Assertions.assertFalse(flag.isPresent()); } } diff --git a/src/test/resources/config/default.properties b/src/test/resources/config/default.properties index 3ca645921f4865c0ec0c504fac4afa47330978fc..fdc3d1a1a49da21cf66dc7944493ede218aeb594 100644 --- a/src/test/resources/config/default.properties +++ b/src/test/resources/config/default.properties @@ -56,7 +56,9 @@ format.default.margin.right=10 ### Diagram Formatting ### ================== +representation.general.brailleLanguage=DE_KURZSCHRIFT representation.general.nonexistentDataText=n/a +representation.general.legendKeyword=Legende: representation.general.maxTitleHeight=2 representation.rasterize.barChart.maxBarThickness=3 representation.rasterize.barChart.minBarThickness=1 diff --git a/src/test/resources/config/rasterizer_test_default.properties b/src/test/resources/config/rasterizer_test_default.properties index 0a78cfee948ef6ede757dedc4e102b1a4676a3ea..033ae6d9479c71427f0ad9c8a33ab815ce60ff48 100644 --- a/src/test/resources/config/rasterizer_test_default.properties +++ b/src/test/resources/config/rasterizer_test_default.properties @@ -45,7 +45,9 @@ format.default.margin.right=0 ### Diagram Formatting ### ================== +representation.general.brailleLanguage=DE_KURZSCHRIFT representation.general.nonexistentDataText=n/a +representation.general.legendKeyword=Legende: representation.general.maxTitleHeight=2 representation.rasterize.barChart.maxBarThickness=3 representation.rasterize.barChart.minBarThickness=1 diff --git a/third_party/liblouis b/third_party/liblouis index 67ab8e96cc2f4db70f220f71cbdee0903e8abac6..364cab426fd86f417a675073914a2f3f0d233194 160000 --- a/third_party/liblouis +++ b/third_party/liblouis @@ -1 +1 @@ -Subproject commit 67ab8e96cc2f4db70f220f71cbdee0903e8abac6 +Subproject commit 364cab426fd86f417a675073914a2f3f0d233194 diff --git a/third_party/liblouis.dll b/third_party/liblouis.dll deleted file mode 100644 index 452b2f1747e1d6c26355a9972265f0514b46233b..0000000000000000000000000000000000000000 Binary files a/third_party/liblouis.dll and /dev/null differ