package com.baselet.element.elementnew.plot; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.baselet.control.Matrix; import com.baselet.control.basics.geom.Dimension; import com.baselet.control.enums.AlignHorizontal; import com.baselet.control.enums.ElementId; import com.baselet.diagram.draw.DrawHandler; import com.baselet.diagram.draw.helper.ColorOwn; import com.baselet.element.NewGridElement; import com.baselet.element.elementnew.plot.drawer.PlotGridDrawConfig; import com.baselet.element.elementnew.plot.elements.AbstractPlot; import com.baselet.element.elementnew.plot.elements.BarPlot; import com.baselet.element.elementnew.plot.elements.LinePlot; import com.baselet.element.elementnew.plot.elements.PiePlot; import com.baselet.element.elementnew.plot.elements.ScatterPlot; import com.baselet.element.elementnew.plot.parser.Parser; import com.baselet.element.elementnew.plot.parser.ParserException; import com.baselet.element.elementnew.plot.parser.ParserResult; import com.baselet.element.elementnew.plot.parser.PlotConstants; import com.baselet.element.elementnew.plot.parser.PlotConstants.PlotType; import com.baselet.element.elementnew.plot.parser.PlotState; import com.baselet.element.facet.Facet; import com.baselet.element.facet.PropertiesParserState; import com.baselet.element.facet.Settings; import com.baselet.element.settings.SettingsManualresizeCenter; import com.baselet.gui.AutocompletionText; public class PlotGrid extends NewGridElement { private static final Logger log = LoggerFactory.getLogger(PlotGrid.class); private Matrix<List<AbstractPlot>> matrix; private Integer gridWidth; private Double minValue; private Double maxValue; /** * this facet is only here to show autocompletion and include PlotGrid in the new parser logic which uses facets */ public static final Facet PSEUDO_PLOT_FACET = new Facet() { @Override public void handleLine(String line, PropertiesParserState propConfig) { // do nothing } @Override public List<AutocompletionText> getAutocompletionStrings() { return PlotConstants.AUTOCOMPLETION_LIST; } @Override public boolean checkStart(String line, PropertiesParserState propConfig) { return true; } }; private void fillWithPlots(ParserResult parserState) { ArrayList<PlotState> plotStateList = parserState.getPlotStateList(); DrawHandler drawer = parserState.getDrawer(); setOverallMinMaxValue(plotStateList); for (PlotState plotState : plotStateList) { Integer xPos = plotState.getValueAsInt(PlotConstants.KEY_INT_X_POSITION, null); Integer yPos = plotState.getValueAsInt(PlotConstants.KEY_INT_Y_POSITION, null); // 1 is subtracted from the values because the user counts from 1 to x; java counts from 0 to x-1 if (xPos != null) { xPos -= 1; } if (yPos != null) { yPos -= 1; } // case1: x and y are specified if (xPos != null && yPos != null) { setMatrixHeightMinimum(yPos); List<List<AbstractPlot>> xCoordinateList = matrix.row(yPos); setMinimumListSize(xPos, xCoordinateList); xCoordinateList.set(xPos, createPlots(drawer, plotState, xPos, yPos, "x and y are specified")); } // case2: only x is specified else if (xPos != null) { putPlotInFirstFreeVerticalSpaceOrAddPlot(drawer, xPos, plotState, "only x is specified -> space replaced"); } // case3: only y is specified else if (yPos != null) { setMatrixHeightMinimum(yPos); List<List<AbstractPlot>> xCoordinateList = matrix.row(yPos); putPlotInFirstFreeHorizontalSpaceOrAddPlot(drawer, xCoordinateList, yPos, plotState, "only y specified -> "); } // case4: no coordinate is specified else { putPlotInFirstFreeMatrixSpace(drawer, plotState); } } gridWidth = matrix.cols(); // Recalculate grid width log.debug("\n" + toString() + "\n"); } private void setOverallMinMaxValue(List<PlotState> plotStateList) { minValue = Double.MAX_VALUE; maxValue = Double.MIN_VALUE; Double[][] data; for (PlotState state : plotStateList) { data = state.getDataSet().data(); for (Double[] dArray : data) { for (Double d : dArray) { if (d > maxValue) { maxValue = d; } if (d < minValue) { minValue = d; } } } } } private void setMatrixHeightMinimum(Integer minHeight) { while (minHeight > matrix.rows() - 1) { matrix.addLine(new ArrayList<List<AbstractPlot>>()); } } private void setMinimumListSize(Integer minWidth, List<List<AbstractPlot>> lineToSet) { while (minWidth > lineToSet.size() - 1) { lineToSet.add(null); } } private void putPlotInFirstFreeVerticalSpaceOrAddPlot(DrawHandler drawer, Integer xFix, PlotState plotState, String info) { boolean plotFilledInFreeSpace = false; for (int ySeq = 0; ySeq < matrix.rows(); ySeq++) { List<List<AbstractPlot>> xCoordinateList = matrix.row(ySeq); if (xFix >= xCoordinateList.size()) { setMinimumListSize(xFix, xCoordinateList); } if (xCoordinateList.get(xFix) == null) { xCoordinateList.set(xFix, createPlots(drawer, plotState, xFix, ySeq, info)); plotFilledInFreeSpace = true; break; } } // If there is no free space available for the plot, a new line must be added if (!plotFilledInFreeSpace) { ArrayList<List<AbstractPlot>> newColumn = new ArrayList<List<AbstractPlot>>(); setMinimumListSize(xFix, newColumn); newColumn.set(xFix, createPlots(drawer, plotState, xFix, matrix.rows(), "only x is specified -> expanded y-list")); matrix.addLine(newColumn); } } private void putPlotInFirstFreeHorizontalSpaceOrAddPlot(DrawHandler drawer, List<List<AbstractPlot>> xCoordinateList, Integer yFix, PlotState plotState, String info) { for (int xSeq = 0; true; xSeq++) { if (xSeq == xCoordinateList.size()) { xCoordinateList.add(createPlots(drawer, plotState, xSeq, yFix, info + "added new x-entry")); return; } if (xCoordinateList.get(xSeq) == null) { xCoordinateList.set(xSeq, createPlots(drawer, plotState, xSeq, yFix, info + "replaced x-entry")); return; } } } private void putPlotInFirstFreeMatrixSpace(DrawHandler drawer, PlotState plotState) { // Go through all lines and all values in each line for (int ySeq = 0; ySeq < matrix.rows(); ySeq++) { List<List<AbstractPlot>> oneLine = matrix.row(ySeq); for (int xSeq = 0; xSeq < oneLine.size(); xSeq++) { List<AbstractPlot> oneValue = oneLine.get(xSeq); // If a free space is found use it if (oneValue == null) { oneLine.set(xSeq, createPlots(drawer, plotState, xSeq, ySeq, "no coordinate specified -> free space found")); return; } } // If the actual x-coordinates line is < than the default grid width add a new value if (oneLine.size() < gridWidth) { oneLine.add(createPlots(drawer, plotState, oneLine.size(), ySeq, "no coordinate specified -> expanded x-list")); return; } } // If every space in the matrix is occupied and the position is still not found add a new line and fill its first place List<List<AbstractPlot>> newLine = new ArrayList<List<AbstractPlot>>(); newLine.add(createPlots(drawer, plotState, 0, matrix.rows(), "no coordinate specified -> every matrix space occupied, expanded y-list")); matrix.addLine(newLine); } private List<AbstractPlot> createPlots(DrawHandler drawer, PlotState plotState, Integer xPos, Integer yPos, String info) { List<AbstractPlot> plotList = new ArrayList<AbstractPlot>(); // create and add base plot plotList.add(createPlot(drawer, plotState, xPos, yPos, info)); // create and add sub plots for (PlotState subPlotState : plotState.getSubplots()) { plotList.add(createPlot(drawer, subPlotState, xPos, yPos, info)); } return plotList; } private AbstractPlot createPlot(DrawHandler drawer, PlotState plotState, int xPos, int yPos, String info) { String type = plotState.getValueValidated(PlotType.getKey(), PlotType.Bar.getValue(), PlotConstants.toStringList(PlotType.values())); log.debug("PlotGrid insert : " + type + " (" + xPos + ";" + yPos + ") " + info); PlotGridDrawConfig plotDrawConfig = new PlotGridDrawConfig(getRealSize(), new Dimension(getRectangle().width, getRectangle().height), minValue, maxValue); if (PlotType.Pie.getValue().equals(type)) { return new PiePlot(drawer, plotDrawConfig, plotState, xPos, yPos); } else if (PlotType.Line.getValue().equals(type)) { return new LinePlot(drawer, plotDrawConfig, plotState, xPos, yPos); } else if (PlotType.Scatter.getValue().equals(type)) { return new ScatterPlot(drawer, plotDrawConfig, plotState, xPos, yPos); } else { return new BarPlot(drawer, plotDrawConfig, plotState, xPos, yPos); } } public void drawPlots() { for (int row = 0; row < matrix.rows(); row++) { for (int col = 0; col < matrix.row(row).size(); col++) { List<AbstractPlot> oneCell = matrix.cell(row, col); for (AbstractPlot onePlot : oneCell) { if (onePlot != null) { if (col != onePlot.getXPosition()) { log.error("Plot contains wrong coordinates: " + col + " != " + onePlot.getXPosition()); } if (row != onePlot.getYPosition()) { log.error("Plot contains wrong coordinates: " + row + " != " + onePlot.getYPosition()); } onePlot.plot(matrix.cols(), matrix.rows()); } } } } } @Override public String toString() { StringBuilder sb = new StringBuilder("------------------------------\n"); for (int i = 0; i < matrix.rows(); i++) { List<List<AbstractPlot>> row = matrix.row(i); for (List<AbstractPlot> oneCell : row) { for (AbstractPlot onePlot : oneCell) { if (onePlot == null) { sb.append("null\t"); } else { sb.append(onePlot.getPlotLineNr()).append("\t"); } } } sb.append("\n"); } return sb.append("------------------------------").toString(); } @Override protected void drawCommonContent(PropertiesParserState state) { DrawHandler drawer = state.getDrawer(); try { matrix = new Matrix<List<AbstractPlot>>(); ParserResult parserState = new Parser().parse(getPanelAttributes()); parserState.setDrawer(drawer); log.debug(parserState.toString()); gridWidth = Integer.parseInt(parserState.getPlotGridValue(PlotConstants.KEY_INT_GRID_WIDTH, PlotConstants.GRID_WIDTH_DEFAULT)); fillWithPlots(parserState); drawPlots(); } catch (ParserException e) { drawer.setForegroundColor(ColorOwn.RED); drawer.setBackgroundColor(ColorOwn.WHITE); drawer.drawRectangle(0, 0, getRectangle().width - 1, getRectangle().height - 1); float x = getRectangle().getWidth() / 2.0f; drawer.print(e.getMessage(), x, getRealSize().height / 2.0, AlignHorizontal.CENTER); } } @Override protected Settings createSettings() { return new SettingsManualresizeCenter() { @Override protected List<Facet> createFacets() { return listOf(PSEUDO_PLOT_FACET); // no real facets should be used } }; } @Override public ElementId getId() { return ElementId.PlotGrid; } }