All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.stathissideris.ascii2image.graphics.Diagram Maven / Gradle / Ivy

// THIS FILE HAS BEEN GENERATED BY A PREPROCESSOR.
/* +=======================================================================
 * |
 * |      PlantUML : a free UML diagram generator
 * |
 * +=======================================================================
 *
 * (C) Copyright 2009-2024, Arnaud Roques
 *
 * Project Info:  https://plantuml.com
 *
 * If you like this project or if you find it useful, you can support us at:
 *
 * https://plantuml.com/patreon (only 1$ per month!)
 * https://plantuml.com/liberapay (only 1€ per month!)
 * https://plantuml.com/paypal
 *
 *
 * PlantUML is free software; you can redistribute it and/or modify it
 * under the terms of the Revised BSD License.
 *
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * * Neither the name of the University of California, Berkeley nor the
 *   names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * PlantUML can occasionally display sponsored or advertising messages. Those
 * messages are usually generated on welcome or error images and never on
 * functional diagrams.
 * See https://plantuml.com/professional if you want to remove them
 *
 * Images (whatever their format : PNG, SVG, EPS...) generated by running PlantUML
 * are owned by the author of their corresponding sources code (that is, their
 * textual description in PlantUML language). Those images are not covered by
 * this BSD license.
 *
 * The generated images can then be used without any reference to the BSD license.
 * It is not even necessary to stipulate that they have been generated with PlantUML,
 * although this will be appreciated by the PlantUML team.
 *
 * There is an exception : if the textual description in PlantUML language is also covered
 * by any license, then the generated images are logically covered
 * by the very same license.
 *
 * This is the IGY distribution (Install GraphViz by Yourself).
 * You have to install GraphViz and to setup the GRAPHVIZ_DOT environment variable
 * (see https://plantuml.com/graphviz-dot )
 *
 * Icons provided by OpenIconic :  https://useiconic.com/open
 * Archimate sprites provided by Archi :  http://www.archimatetool.com
 * Stdlib AWS provided by https://github.com/milo-minderbinder/AWS-PlantUML
 * Stdlib Icons provided https://github.com/tupadr3/plantuml-icon-font-sprites
 * ASCIIMathML (c) Peter Jipsen http://www.chapman.edu/~jipsen
 * ASCIIMathML (c) David Lippman http://www.pierce.ctc.edu/dlippman
 * CafeUndZopfli ported by Eugene Klyuchnikov https://github.com/eustas/CafeUndZopfli
 * Brotli (c) by the Brotli Authors https://github.com/google/brotli
 * Themes (c) by Brett Schwarz https://github.com/bschwarz/puml-themes
 * Twemoji (c) by Twitter at https://twemoji.twitter.com/
 *
 */
package org.stathissideris.ascii2image.graphics;

import java.awt.Color;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Iterator;

import org.stathissideris.ascii2image.core.ConversionOptions;
import org.stathissideris.ascii2image.core.Pair;
import org.stathissideris.ascii2image.core.ProcessingOptions;
import org.stathissideris.ascii2image.text.AbstractionGrid;
import org.stathissideris.ascii2image.text.CellSet;
import org.stathissideris.ascii2image.text.TextGrid;

/**
 *
 * @author Efstathios Sideris
 */
public class Diagram {
    // ::remove folder when __HAXE__

	private static final boolean DEBUG = false;
	private static final boolean VERBOSE_DEBUG = false;

	private ArrayList shapes = new ArrayList();
	private ArrayList compositeShapes = new ArrayList();
	private ArrayList textObjects = new ArrayList();

	private int width, height;
	private int cellWidth, cellHeight;


	/**
	 *
	 * 

An outline of the inner workings of this very important (and monstrous) * constructor is presented here. Boundary processing is the first step * of the process:

* *
    *
  1. Copy the grid into a work grid and remove all type-on-line * and point markers from the work grid
  2. *
  3. Split grid into distinct shapes by plotting the grid * onto an AbstractionGrid and its getDistinctShapes() method.
  4. *
  5. Find all the possible boundary sets of each of the * distinct shapes. This can produce duplicate shapes (if the boundaries * are the same when filling from the inside and the outside).
  6. *
  7. Remove duplicate boundaries.
  8. *
  9. Remove obsolete boundaries. Obsolete boundaries are the ones that are * the sum of their parts when plotted as filled shapes. (see method * removeObsoleteShapes())
  10. *
  11. Seperate the found boundary sets to open, closed or mixed * (See CellSet class on how its done).
  12. *
  13. Are there any closed boundaries? *
      *
    • YES. Subtract all the closed boundaries from each of the * open ones. That should convert the mixed shapes into open.
    • *
    • NO. In this (harder) case, we use the method * breakTrulyMixedBoundaries() of CellSet to break boundaries * into open and closed shapes (would work in any case, but it's * probably slower than the other method). This method is based * on tracing from the lines' ends and splitting when we get to * an intersection.
    • *
    *
  14. *
  15. If we had to eliminate any mixed shapes, we seperate the found * boundary sets again to open, closed or mixed.
  16. *
* *

At this stage, the boundary processing is all complete and we * proceed with using those boundaries to create the shapes:

* *
    *
  1. Create closed shapes.
  2. *
  3. Create open shapes. That's when the line end corrections are * also applied, concerning the positioning of the ends of lines * see methods connectEndsToAnchors() and moveEndsToCellEdges() of * DiagramShape.
  4. *
  5. Assign color codes to closed shapes.
  6. *
  7. Assing extended markup tags to closed shapes.

    *
  8. Create arrowheads.

    *
  9. Create point markers.

    *
* *

Finally, the text processing occurs: [pending]

* * @param grid * @param options * @param processingOptions */ public Diagram(TextGrid grid, ConversionOptions options, ProcessingOptions processingOptions) { this.cellWidth = options.renderingOptions.getCellWidth(); this.cellHeight = options.renderingOptions.getCellHeight(); FontMeasurer fontMeasurer = new FontMeasurer( options.renderingOptions.getFont(), options.renderingOptions.getForceFontSize()); width = grid.getWidth() * cellWidth; height = grid.getHeight() * cellHeight; TextGrid workGrid = new TextGrid(grid); workGrid.replaceTypeOnLine(); workGrid.replacePointMarkersOnLine(); if(DEBUG) workGrid.printDebug(); int width = grid.getWidth(); int height = grid.getHeight(); //split distinct shapes using AbstractionGrid AbstractionGrid temp = new AbstractionGrid(workGrid, workGrid.getAllBoundaries()); ArrayList boundarySetsStep1 = temp.getDistinctShapes(); if(DEBUG){ System.out.println("******* Distinct shapes found using AbstractionGrid *******"); Iterator dit = boundarySetsStep1.iterator(); while (dit.hasNext()) { CellSet set = (CellSet) dit.next(); set.printAsGrid(); } System.out.println("******* Same set of shapes after processing them by filling *******"); } //Find all the boundaries by using the special version of the filling method //(fills in a different buffer than the buffer it reads from) ArrayList boundarySetsStep2 = new ArrayList(); Iterator boundarySetIt = boundarySetsStep1.iterator(); while (boundarySetIt.hasNext()) { CellSet set = (CellSet) boundarySetIt.next(); //the fill buffer keeps track of which cells have been //filled already TextGrid fillBuffer = new TextGrid(width * 3, height * 3); for(int yi = 0; yi < height * 3; yi++){ for(int xi = 0; xi < width * 3; xi++){ if(fillBuffer.isBlank(xi, yi)){ TextGrid copyGrid = new AbstractionGrid(workGrid, set).getCopyOfInternalBuffer(); CellSet boundaries = copyGrid .findBoundariesExpandingFrom(copyGrid.new Cell(xi, yi)); if(boundaries.size() == 0) continue; //i'm not sure why these occur boundarySetsStep2.add(boundaries.makeScaledOneThirdEquivalent()); copyGrid = new AbstractionGrid(workGrid, set).getCopyOfInternalBuffer(); CellSet filled = copyGrid .fillContinuousArea(copyGrid.new Cell(xi, yi), '*'); fillBuffer.fillCellsWith(filled, '*'); fillBuffer.fillCellsWith(boundaries, '-'); if(DEBUG){ //System.out.println("Fill buffer:"); //fillBuffer.printDebug(); boundaries.makeScaledOneThirdEquivalent().printAsGrid(); System.out.println("-----------------------------------"); } } } } } if (DEBUG) System.out.println("******* Removed duplicates *******"); boundarySetsStep2 = CellSet.removeDuplicateSets(boundarySetsStep2); if(DEBUG){ Iterator dit = boundarySetsStep2.iterator(); while (dit.hasNext()) { CellSet set = (CellSet) dit.next(); set.printAsGrid(); } } int originalSize = boundarySetsStep2.size(); boundarySetsStep2 = CellSet.removeDuplicateSets(boundarySetsStep2); if(DEBUG) { System.out.println( "******* Removed duplicates: there were " +originalSize +" shapes and now there are " +boundarySetsStep2.size()); } //split boundaries to open, closed and mixed if (DEBUG) System.out.println("******* First evaluation of openess *******"); ArrayList open = new ArrayList(); ArrayList closed = new ArrayList(); ArrayList mixed = new ArrayList(); Iterator sets = boundarySetsStep2.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); int type = set.getType(workGrid); if(type == CellSet.TYPE_CLOSED) closed.add(set); else if(type == CellSet.TYPE_OPEN) open.add(set); else if(type == CellSet.TYPE_MIXED) mixed.add(set); if(DEBUG){ if(type == CellSet.TYPE_CLOSED) System.out.println("Closed boundaries:"); else if(type == CellSet.TYPE_OPEN) System.out.println("Open boundaries:"); else if(type == CellSet.TYPE_MIXED) System.out.println("Mixed boundaries:"); set.printAsGrid(); } } boolean hadToEliminateMixed = false; if(mixed.size() > 0 && closed.size() > 0) { // mixed shapes can be eliminated by // subtracting all the closed shapes from them if (DEBUG) System.out.println("******* Eliminating mixed shapes (basic algorithm) *******"); hadToEliminateMixed = true; //subtract from each of the mixed sets all the closed sets sets = mixed.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); Iterator closedSets = closed.iterator(); while(closedSets.hasNext()){ CellSet closedSet = (CellSet) closedSets.next(); set.subtractSet(closedSet); } // this is necessary because some mixed sets produce // several distinct open sets after you subtract the // closed sets from them if(set.getType(workGrid) == CellSet.TYPE_OPEN) { boundarySetsStep2.remove(set); boundarySetsStep2.addAll(set.breakIntoDistinctBoundaries(workGrid)); } } } else if(mixed.size() > 0 && closed.size() == 0) { // no closed shape exists, will have to // handle mixed shape on its own // an example of this case is the following: // +-----+ // | A |C B // + ---+------------------- // | | // +-----+ hadToEliminateMixed = true; if (DEBUG) System.out.println("******* Eliminating mixed shapes (advanced algorithm for truly mixed shapes) *******"); sets = mixed.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); boundarySetsStep2.remove(set); boundarySetsStep2.addAll(set.breakTrulyMixedBoundaries(workGrid)); } } else { if (DEBUG) System.out.println("No mixed shapes found. Skipped mixed shape elimination step"); } if(hadToEliminateMixed){ if (DEBUG) System.out.println("******* Second evaluation of openess *******"); //split boundaries again to open, closed and mixed open = new ArrayList(); closed = new ArrayList(); mixed = new ArrayList(); sets = boundarySetsStep2.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); int type = set.getType(workGrid); if(type == CellSet.TYPE_CLOSED) closed.add(set); else if(type == CellSet.TYPE_OPEN) open.add(set); else if(type == CellSet.TYPE_MIXED) mixed.add(set); if(DEBUG){ if(type == CellSet.TYPE_CLOSED) System.out.println("Closed boundaries:"); else if(type == CellSet.TYPE_OPEN) System.out.println("Open boundaries:"); else if(type == CellSet.TYPE_MIXED) System.out.println("Mixed boundaries:"); set.printAsGrid(); } } } boolean removedAnyObsolete = removeObsoleteShapes(workGrid, closed); boolean allCornersRound = false; if(processingOptions.areAllCornersRound()) allCornersRound = true; //make shapes from the boundary sets //make closed shapes ArrayList closedShapes = new ArrayList(); sets = closed.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); DiagramComponent shape = DiagramComponent.createClosedFromBoundaryCells(workGrid, set, cellWidth, cellHeight, allCornersRound); if(shape != null){ if(shape instanceof DiagramShape){ addToShapes((DiagramShape) shape); closedShapes.add(shape); } else if(shape instanceof CompositeDiagramShape) addToCompositeShapes((CompositeDiagramShape) shape); } } if(processingOptions.performSeparationOfCommonEdges()) separateCommonEdges(closedShapes); //make open shapes sets = open.iterator(); while(sets.hasNext()){ CellSet set = (CellSet) sets.next(); if(set.size() == 1){ //single cell "shape" TextGrid.Cell cell = (TextGrid.Cell) set.getFirst(); if(!grid.cellContainsDashedLineChar(cell)) { DiagramShape shape = DiagramShape.createSmallLine(workGrid, cell, cellWidth, cellHeight); if(shape != null) { addToShapes(shape); shape.connectEndsToAnchors(workGrid, this); } } } else { //normal shape DiagramComponent shape = CompositeDiagramShape .createOpenFromBoundaryCells( workGrid, set, cellWidth, cellHeight, allCornersRound); if(shape != null){ if(shape instanceof CompositeDiagramShape){ addToCompositeShapes((CompositeDiagramShape) shape); ((CompositeDiagramShape) shape).connectEndsToAnchors(workGrid, this); } else if(shape instanceof DiagramShape) { addToShapes((DiagramShape) shape); ((DiagramShape) shape).connectEndsToAnchors(workGrid, this); ((DiagramShape) shape).moveEndsToCellEdges(grid, this); } } } } //assign color codes to shapes //TODO: text on line should not change its color //TODO: each color tag should be assigned to the smallest containing shape (like shape tags) Iterator cellColorPairs = grid.findColorCodes().iterator(); while(cellColorPairs.hasNext()){ TextGrid.CellColorPair pair = (TextGrid.CellColorPair) cellColorPairs.next(); ShapePoint point = new ShapePoint(getCellMidX(pair.cell), getCellMidY(pair.cell)); Iterator shapes = getShapes().iterator(); while(shapes.hasNext()){ DiagramShape shape = (DiagramShape) shapes.next(); if(shape.contains(point)) shape.setFillColor(pair.color); } } //assign markup to shapes Iterator cellTagPairs = grid.findMarkupTags().iterator(); while(cellTagPairs.hasNext()){ TextGrid.CellTagPair pair = (TextGrid.CellTagPair) cellTagPairs.next(); ShapePoint point = new ShapePoint(getCellMidX(pair.cell), getCellMidY(pair.cell)); //find the smallest shape that contains the tag DiagramShape containingShape = null; Iterator shapes = getShapes().iterator(); while(shapes.hasNext()){ DiagramShape shape = (DiagramShape) shapes.next(); if(shape.contains(point)){ if(containingShape == null){ containingShape = shape; } else { if(shape.isSmallerThan(containingShape)){ containingShape = shape; } } } } //this tag is not within a shape, skip if(containingShape == null) continue; //TODO: the code below could be a lot more concise if(pair.tag.equals("d")){ CustomShapeDefinition def = processingOptions.getFromCustomShapes("d"); if(def == null) containingShape.setType(DiagramShape.TYPE_DOCUMENT); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("s")){ CustomShapeDefinition def = processingOptions.getFromCustomShapes("s"); if(def == null) containingShape.setType(DiagramShape.TYPE_STORAGE); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("io")){ CustomShapeDefinition def = processingOptions.getFromCustomShapes("io"); if(def == null) containingShape.setType(DiagramShape.TYPE_IO); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("c")){ CustomShapeDefinition def = processingOptions.getFromCustomShapes("c"); if(def == null) containingShape.setType(DiagramShape.TYPE_DECISION); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("mo")){ CustomShapeDefinition def = processingOptions.getFromCustomShapes("mo"); if(def == null) containingShape.setType(DiagramShape.TYPE_MANUAL_OPERATION); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("tr")){ CustomShapeDefinition def = processingOptions.getFromCustomShapes("tr"); if(def == null) containingShape.setType(DiagramShape.TYPE_TRAPEZOID); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else if(pair.tag.equals("o")){ CustomShapeDefinition def = processingOptions.getFromCustomShapes("o"); if(def == null) containingShape.setType(DiagramShape.TYPE_ELLIPSE); else { containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } else { CustomShapeDefinition def = processingOptions.getFromCustomShapes(pair.tag); containingShape.setType(DiagramShape.TYPE_CUSTOM); containingShape.setDefinition(def); } } //make arrowheads Iterator arrowheadCells = workGrid.findArrowheads().iterator(); while(arrowheadCells.hasNext()){ TextGrid.Cell cell = (TextGrid.Cell) arrowheadCells.next(); DiagramShape arrowhead = DiagramShape.createArrowhead(workGrid, cell, cellWidth, cellHeight); if(arrowhead != null) addToShapes(arrowhead); else System.err.println("Could not create arrowhead shape. Unexpected error."); } //make point markers Iterator markersIt = grid.getPointMarkersOnLine().iterator(); while (markersIt.hasNext()) { TextGrid.Cell cell = (TextGrid.Cell) markersIt.next(); DiagramShape mark = new DiagramShape(); mark.addToPoints(new ShapePoint( getCellMidX(cell), getCellMidY(cell) )); mark.setType(DiagramShape.TYPE_POINT_MARKER); mark.setFillColor(Color.white); shapes.add(mark); } removeDuplicateShapes(); if(DEBUG) System.out.println("Shape count: "+shapes.size()); if(DEBUG) System.out.println("Composite shape count: "+compositeShapes.size()); //copy again workGrid = new TextGrid(grid); workGrid.removeNonText(); // ****** handle text ******* //break up text into groups TextGrid textGroupGrid = new TextGrid(workGrid); CellSet gaps = textGroupGrid.getAllBlanksBetweenCharacters(); //kludge textGroupGrid.fillCellsWith(gaps, '|'); CellSet nonBlank = textGroupGrid.getAllNonBlank(); ArrayList textGroups = nonBlank.breakIntoDistinctBoundaries(); if(DEBUG) System.out.println(textGroups.size()+" text groups found"); Font font = fontMeasurer.getFontFor(cellHeight); Iterator textGroupIt = textGroups.iterator(); while(textGroupIt.hasNext()){ CellSet textGroupCellSet = (CellSet) textGroupIt.next(); TextGrid isolationGrid = new TextGrid(width, height); workGrid.copyCellsTo(textGroupCellSet, isolationGrid); ArrayList strings = isolationGrid.findStrings(); Iterator it = strings.iterator(); while(it.hasNext()){ TextGrid.CellStringPair pair = (TextGrid.CellStringPair) it.next(); TextGrid.Cell cell = pair.cell; String string = pair.string; if (DEBUG) System.out.println("Found string "+string); TextGrid.Cell lastCell = isolationGrid.new Cell(cell.x + string.length() - 1, cell.y); int minX = getCellMinX(cell); int y = getCellMaxY(cell); int maxX = getCellMaxX(lastCell); DiagramText textObject; if(fontMeasurer.getWidthFor(string, font) > maxX - minX){ //does not fit horizontally Font lessWideFont = fontMeasurer.getFontFor(maxX - minX, string); textObject = new DiagramText(minX, y, string, lessWideFont, fontMeasurer); } else textObject = new DiagramText(minX, y, string, font, fontMeasurer); textObject.centerVerticallyBetween(getCellMinY(cell), getCellMaxY(cell)); //TODO: if the strings start with bullets they should be aligned to the left //position text correctly int otherStart = isolationGrid.otherStringsStartInTheSameColumn(cell); int otherEnd = isolationGrid.otherStringsEndInTheSameColumn(lastCell); if(0 == otherStart && 0 == otherEnd) { textObject.centerHorizontallyBetween(minX, maxX); } else if(otherEnd > 0 && otherStart == 0) { textObject.alignRightEdgeTo(maxX); } else if(otherEnd > 0 && otherStart > 0){ if(otherEnd > otherStart){ textObject.alignRightEdgeTo(maxX); } else if(otherEnd == otherStart){ textObject.centerHorizontallyBetween(minX, maxX); } } addToTextObjects(textObject); } } if (DEBUG) System.out.println("Positioned text"); //correct the color of the text objects according //to the underlying color Iterator shapes = this.getAllDiagramShapes().iterator(); while(shapes.hasNext()){ DiagramShape shape = (DiagramShape) shapes.next(); Color fillColor = shape.getFillColor(); if(shape.isClosed() && shape.getType() != DiagramShape.TYPE_ARROWHEAD && fillColor != null && BitmapRenderer.isColorDark(fillColor)){ Iterator textObjects = getTextObjects().iterator(); while(textObjects.hasNext()){ DiagramText textObject = (DiagramText) textObjects.next(); if(shape.intersects(textObject.getBounds())){ textObject.setColor(Color.white); } } } } //set outline to true for test within custom shapes shapes = this.getAllDiagramShapes().iterator(); while(shapes.hasNext()){ DiagramShape shape = (DiagramShape) shapes.next(); if(shape.getType() == DiagramShape.TYPE_CUSTOM){ Iterator textObjects = getTextObjects().iterator(); while(textObjects.hasNext()){ DiagramText textObject = (DiagramText) textObjects.next(); textObject.setHasOutline(true); textObject.setColor(DiagramText.DEFAULT_COLOR); } } } if (DEBUG) System.out.println("Corrected color of text according to underlying color"); } /** * Returns a list of all DiagramShapes in the Diagram, including * the ones within CompositeDiagramShapes * * @return */ public ArrayList getAllDiagramShapes(){ ArrayList shapes = new ArrayList(); shapes.addAll(this.getShapes()); Iterator shapesIt = this.getCompositeShapes().iterator(); while(shapesIt.hasNext()){ CompositeDiagramShape compShape = (CompositeDiagramShape) shapesIt.next(); shapes.addAll(compShape.getShapes()); } return shapes; } /** * Removes the sets from setsthat are the sum of their parts * when plotted as filled shapes. * * @return true if it removed any obsolete. * */ private boolean removeObsoleteShapes(TextGrid grid, ArrayList sets){ if (DEBUG) System.out.println("******* Removing obsolete shapes *******"); boolean removedAny = false; ArrayList filledSets = new ArrayList(); Iterator it; if(VERBOSE_DEBUG) { System.out.println("******* Sets before *******"); it = sets.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); set.printAsGrid(); } } //make filled versions of all the boundary sets it = sets.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); set = set.getFilledEquivalent(grid); if(set == null){ return false; } else filledSets.add(set); } ArrayList toBeRemovedIndices = new ArrayList(); it = filledSets.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); if(VERBOSE_DEBUG){ System.out.println("*** Deciding if the following should be removed:"); set.printAsGrid(); } //find the other sets that have common cells with set ArrayList common = new ArrayList(); common.add(set); Iterator it2 = filledSets.iterator(); while(it2.hasNext()){ CellSet set2 = (CellSet) it2.next(); if(set != set2 && set.hasCommonCells(set2)){ common.add(set2); } } //it only makes sense for more than 2 sets if(common.size() == 2) continue; //find largest set CellSet largest = set; it2 = common.iterator(); while(it2.hasNext()){ CellSet set2 = (CellSet) it2.next(); if(set2.size() > largest.size()){ largest = set2; } } if(VERBOSE_DEBUG){ System.out.println("Largest:"); largest.printAsGrid(); } //see if largest is sum of others common.remove(largest); //make the sum set of the small sets on a grid TextGrid gridOfSmalls = new TextGrid(largest.getMaxX() + 2, largest.getMaxY() + 2); CellSet sumOfSmall = new CellSet(); it2 = common.iterator(); while(it2.hasNext()){ CellSet set2 = (CellSet) it2.next(); if(VERBOSE_DEBUG){ System.out.println("One of smalls:"); set2.printAsGrid(); } gridOfSmalls.fillCellsWith(set2, '*'); } if(VERBOSE_DEBUG){ System.out.println("Sum of smalls:"); gridOfSmalls.printDebug(); } TextGrid gridLargest = new TextGrid(largest.getMaxX() + 2, largest.getMaxY() + 2); gridLargest.fillCellsWith(largest, '*'); int index = filledSets.indexOf(largest); if(gridLargest.equals(gridOfSmalls) && !toBeRemovedIndices.contains(index)) { toBeRemovedIndices.add(index); if (DEBUG){ System.out.println("Decided to remove set:"); largest.printAsGrid(); } } else if (DEBUG){ System.out.println("This set WILL NOT be removed:"); largest.printAsGrid(); } //if(gridLargest.equals(gridOfSmalls)) toBeRemovedIndices.add(new Integer(index)); } ArrayList setsToBeRemoved = new ArrayList(); it = toBeRemovedIndices.iterator(); while(it.hasNext()){ int i = ((Integer) it.next()).intValue(); setsToBeRemoved.add(sets.get(i)); } it = setsToBeRemoved.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); removedAny = true; sets.remove(set); } if(VERBOSE_DEBUG) { System.out.println("******* Sets after *******"); it = sets.iterator(); while(it.hasNext()){ CellSet set = (CellSet) it.next(); set.printAsGrid(); } } return removedAny; } public float getMinimumOfCellDimension(){ return Math.min(getCellWidth(), getCellHeight()); } private void separateCommonEdges(ArrayList shapes){ float offset = getMinimumOfCellDimension() / 5; ArrayList edges = new ArrayList(); //get all adges Iterator it = shapes.iterator(); while (it.hasNext()) { DiagramShape shape = (DiagramShape) it.next(); edges.addAll(shape.getEdges()); } //group edges into pairs of touching edges ArrayList> listOfPairs = new ArrayList>(); it = edges.iterator(); //all-against-all touching test for the edges int startIndex = 1; //skip some to avoid duplicate comparisons and self-to-self comparisons while(it.hasNext()){ ShapeEdge edge1 = (ShapeEdge) it.next(); for(int k = startIndex; k < edges.size(); k++) { ShapeEdge edge2 = edges.get(k); if(edge1.touchesWith(edge2)) { listOfPairs.add(new Pair(edge1, edge2)); } } startIndex++; } ArrayList movedEdges = new ArrayList(); //move equivalent edges inwards it = listOfPairs.iterator(); while(it.hasNext()){ Pair pair = (Pair) it.next(); if(!movedEdges.contains(pair.first)) { pair.first.moveInwardsBy(offset); movedEdges.add(pair.first); } if(!movedEdges.contains(pair.second)) { pair.second.moveInwardsBy(offset); movedEdges.add(pair.second); } } } //TODO: removes more than it should private void removeDuplicateShapes() { ArrayList originalShapes = new ArrayList(); Iterator shapesIt = getShapesIterator(); while(shapesIt.hasNext()){ DiagramShape shape = (DiagramShape) shapesIt.next(); boolean isOriginal = true; Iterator originals = originalShapes.iterator(); while(originals.hasNext()){ DiagramShape originalShape = (DiagramShape) originals.next(); if(shape.equals(originalShape)){ isOriginal = false; } } if(isOriginal) originalShapes.add(shape); } shapes.clear(); shapes.addAll(originalShapes); } private void addToTextObjects(DiagramText shape){ textObjects.add(shape); } private void addToCompositeShapes(CompositeDiagramShape shape){ compositeShapes.add(shape); } private void addToShapes(DiagramShape shape){ shapes.add(shape); } public Iterator getShapesIterator(){ return shapes.iterator(); } /** * @return */ public int getHeight() { return height; } /** * @return */ public int getWidth() { return width; } /** * @return */ public int getCellWidth() { return cellWidth; } /** * @return */ public int getCellHeight() { return cellHeight; } /** * @return */ public ArrayList getCompositeShapes() { return compositeShapes; } /** * @return */ public ArrayList getShapes() { return shapes; } public int getCellMinX(TextGrid.Cell cell){ return getCellMinX(cell, cellWidth); } public static int getCellMinX(TextGrid.Cell cell, int cellXSize){ return cell.x * cellXSize; } public int getCellMidX(TextGrid.Cell cell){ return getCellMidX(cell, cellWidth); } public static int getCellMidX(TextGrid.Cell cell, int cellXSize){ return cell.x * cellXSize + cellXSize / 2; } public int getCellMaxX(TextGrid.Cell cell){ return getCellMaxX(cell, cellWidth); } public static int getCellMaxX(TextGrid.Cell cell, int cellXSize){ return cell.x * cellXSize + cellXSize; } public int getCellMinY(TextGrid.Cell cell){ return getCellMinY(cell, cellHeight); } public static int getCellMinY(TextGrid.Cell cell, int cellYSize){ return cell.y * cellYSize; } public int getCellMidY(TextGrid.Cell cell){ return getCellMidY(cell, cellHeight); } public static int getCellMidY(TextGrid.Cell cell, int cellYSize){ return cell.y * cellYSize + cellYSize / 2; } public int getCellMaxY(TextGrid.Cell cell){ return getCellMaxY(cell, cellHeight); } public static int getCellMaxY(TextGrid.Cell cell, int cellYSize){ return cell.y * cellYSize + cellYSize; } public TextGrid.Cell getCellFor(ShapePoint point){ if(point == null) throw new IllegalArgumentException("ShapePoint cannot be null"); //TODO: the fake grid is a problem TextGrid g = new TextGrid(); return g.new Cell((int) point.x / cellWidth, (int) point.y / cellHeight); } /** * @return */ public ArrayList getTextObjects() { return textObjects; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy