org.stathissideris.ascii2image.text.CellSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plantuml Show documentation
Show all versions of plantuml Show documentation
PlantUML is a component that allows to quickly write :
* sequence diagram,
* use case diagram,
* class diagram,
* activity diagram,
* component diagram,
* state diagram
* object diagram
/*
* DiTAA - Diagrams Through Ascii Art
*
* Copyright (C) 2004 Efstathios Sideris
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package org.stathissideris.ascii2image.text;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
*
* @author Efstathios Sideris
*/
public class CellSet implements Iterable {
private static final boolean DEBUG = false;
private static final boolean VERBOSE_DEBUG = false;
public static final int TYPE_CLOSED = 0;
public static final int TYPE_OPEN = 1;
public static final int TYPE_MIXED = 2;
public static final int TYPE_HAS_CLOSED_AREA = 3;
public static final int TYPE_UNDETERMINED = 4;
Set internalSet = new HashSet();
private int type = TYPE_UNDETERMINED;
private boolean typeIsValid = false;
private static final Object FAKE = new Object();
public CellSet(){
}
public CellSet(CellSet other){
addAll(other);
}
public Iterator iterator(){
return internalSet.iterator();
}
public Object add(TextGrid.Cell cell){
return internalSet.add(cell);
}
public void addAll(CellSet set){
internalSet.addAll(set.internalSet);
}
void clear(){
internalSet.clear();
}
public int size() {
return internalSet.size();
}
public TextGrid.Cell getFirst(){
//return internalSet.get(0);
return (TextGrid.Cell) internalSet.iterator().next();
}
public void printAsGrid(){
TextGrid grid = new TextGrid(getMaxX()+2, getMaxY()+2);
grid.fillCellsWith(this, '*');
grid.printDebug();
}
public void printDebug(){
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cell = it.next();
System.out.print(cell);
if(it.hasNext()) System.out.print(" ");
}
System.out.println();
}
public String getCellsAsString(){
StringBuilder str = new StringBuilder();
Iterator it = iterator();
while(it.hasNext()){
str.append(it.next().toString());
if(it.hasNext()) str.append("/");
}
return str.toString();
}
public String toString(){
TextGrid grid = new TextGrid(getMaxX()+2, getMaxY()+2);
grid.fillCellsWith(this, '*');
return grid.getDebugString();
}
/**
* Deep copy
*
* @param set
* @return
*/
public static CellSet copyCellSet(CellSet set) {
TextGrid grid = new TextGrid();
CellSet newSet = new CellSet();
Iterator it = set.iterator();
while (it.hasNext()) {
TextGrid.Cell cell = (TextGrid.Cell) it.next();
TextGrid.Cell newCell = grid.new Cell(cell);
newSet.add(newCell);
}
return newSet;
}
/*public BoundarySet(BoundarySet set) {
Iterator it = set.iterator();
while(it.hasNext()){
TextGrid.Cell cell = (TextGrid.Cell) it.next();
add(new TextGrid.Cell(cell));
}
}*/
public int getType(TextGrid grid) {
if(typeIsValid) return type;
typeIsValid = true;
if(size() == 1) {
type = TYPE_OPEN;
return TYPE_OPEN;
}
int typeTrace = getTypeAccordingToTraceMethod(grid);
if(DEBUG){
System.out.println("trace: "+typeTrace);
}
if(typeTrace == TYPE_OPEN) {
type = TYPE_OPEN;
return TYPE_OPEN;
}
if(typeTrace == TYPE_CLOSED) {
type = TYPE_CLOSED;
return TYPE_CLOSED;
}
if(typeTrace == TYPE_UNDETERMINED) {
int typeFill = getTypeAccordingToFillMethod(grid);
if(typeFill == TYPE_HAS_CLOSED_AREA){
type = TYPE_MIXED;
return TYPE_MIXED;
} else if(typeFill == TYPE_OPEN){
type = TYPE_OPEN;
return TYPE_OPEN;
}
}
//in the case that both return undetermined:
type = TYPE_UNDETERMINED;
return TYPE_UNDETERMINED;
}
private int getTypeAccordingToTraceMethod(TextGrid grid) {
if(size() < 2) return TYPE_OPEN;
TextGrid workGrid = TextGrid.makeSameSizeAs(grid);
grid.copyCellsTo(this, workGrid);
//start with a line end if it exists or with a "random" cell if not
TextGrid.Cell start = null;
for(TextGrid.Cell cell : this)
if(workGrid.isLinesEnd(cell))
start = cell;
if(start == null) start = (TextGrid.Cell) getFirst();
if (DEBUG)
System.out.println("Tracing:\nStarting at "+start+" ("+grid.getCellTypeAsString(start)+")");
TextGrid.Cell previous = start;
TextGrid.Cell cell = null;
CellSet nextCells = workGrid.followCell(previous);
if(nextCells.size() == 0) return TYPE_OPEN;
cell = (TextGrid.Cell) nextCells.getFirst();
if (DEBUG)
System.out.println("\tat cell "+cell+" ("+grid.getCellTypeAsString(cell)+")");
while(!cell.equals(start)){
nextCells = workGrid.followCell(cell, previous);
if(nextCells.size() == 0) {
if (DEBUG)
System.out.println("-> Found dead-end, shape is open");
return TYPE_OPEN;
} if(nextCells.size() == 1) {
previous = cell;
cell = (TextGrid.Cell) nextCells.getFirst();
if (DEBUG)
System.out.println("\tat cell "+cell+" ("+grid.getCellTypeAsString(cell)+")");
} else if(nextCells.size() > 1) {
if (DEBUG)
System.out.println("-> Found intersection at cell "+cell);
return TYPE_UNDETERMINED;
}
}
if (DEBUG)
System.out.println("-> Arrived back to start, shape is closed");
return TYPE_CLOSED;
// boolean hasMoved = false;
//
// CellSet workSet;
// workSet = new CellSet(this);
//
// TextGrid.Cell start = (TextGrid.Cell) get(0);
//
// workSet.remove(start);
// TextGrid.Cell cell = workSet.findCellNextTo(start);
//
// while(true && cell != null){
//
// hasMoved = true;
// workSet.remove(cell);
//
// CellSet setOfNeighbours = workSet.findCellsNextTo(cell);
//
// if(setOfNeighbours.isEmpty()) break;
//
// TextGrid.Cell c = null;
// if(setOfNeighbours.size() == 1) c = (TextGrid.Cell) setOfNeighbours.get(0);
// if(setOfNeighbours.size() > 1) return TYPE_UNDETERMINED;
// if(c == null) break;
// else cell = c;
// }
// if(cell != null && start.isNextTo(cell) && hasMoved) return TYPE_CLOSED;
// else return TYPE_OPEN;
}
private int getTypeAccordingToFillMethod(TextGrid grid){
if(size() == 0) return TYPE_OPEN;
CellSet tempSet = copyCellSet(this);
tempSet.translate( -this.getMinX() + 1, -this.getMinY() + 1);
TextGrid subGrid = grid.getSubGrid(getMinX() - 1, getMinY() - 1, getWidth() + 3, getHeight() + 3);
AbstractionGrid abstraction = new AbstractionGrid(subGrid, tempSet);
TextGrid temp = abstraction.getCopyOfInternalBuffer();
TextGrid.Cell cell1 = null;
TextGrid.Cell cell2 = null;
int width = temp.getWidth();
int height = temp.getHeight();
TextGrid.Cell fillCell = null;
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
TextGrid.Cell cCell = temp.new Cell(x, y);
if(temp.isBlank(cCell)){
fillCell = cCell;
break;
}
}
}
if(fillCell == null){
System.err.println("Unexpected error: fill method cannot fill anywhere");
return TYPE_UNDETERMINED;
}
temp.fillContinuousArea(fillCell, '*');
if(VERBOSE_DEBUG) {System.out.println("Buffer after filling:"); temp.printDebug();}
if(temp.hasBlankCells()) return TYPE_HAS_CLOSED_AREA;
else return TYPE_OPEN;
}
public void translate(int dx, int dy){
typeIsValid = false;
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cCell = (TextGrid.Cell) it.next();
cCell.x += dx;
cCell.y += dy;
}
}
public TextGrid.Cell find(TextGrid.Cell cell){
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cCell = it.next();
if(cCell.equals(cell)) return cCell;
}
return null;
}
public boolean contains(TextGrid.Cell cell){
if(cell == null) return false;
return internalSet.contains(cell);
}
// public boolean contains(TextGrid.Cell cell){
// Iterator it = iterator();
// while(it.hasNext()){
// TextGrid.Cell cCell = it.next();
// if(cCell.equals(cell)) return true;
// }
// return false;
// }
public void addSet(CellSet set){
typeIsValid = false;
this.addAll(set);
}
public boolean hasCommonCells(CellSet otherSet){
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cell = it.next();
if(otherSet.contains(cell)) return true;
}
return false;
}
public TextGrid.Cell find(int x, int y){
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cCell = (TextGrid.Cell) it.next();
if(cCell.x == x && cCell.y == y) return cCell;
}
return null;
}
public CellSet getFilledEquivalent(TextGrid textGrid){
if(this.getType(textGrid) == CellSet.TYPE_OPEN) return new CellSet(this);
TextGrid grid = new TextGrid(getMaxX()+2, getMaxY()+2);
grid.fillCellsWith(this, '*');
//find a cell that has a blank both on the east and the west
TextGrid.Cell cell = null;
boolean finished = false;
for(int y = 0; y < grid.getHeight() && !finished; y++){
for(int x = 0; x < grid.getWidth() && !finished; x++){
cell = grid.new Cell(x, y);
if(!grid.isBlank(cell)
&& grid.isBlank(cell.getEast())
&& grid.isBlank(cell.getWest())){
finished = true;
}
}
}
if(cell != null){
cell = cell.getEast();
if(grid.isOutOfBounds(cell)) return new CellSet(this);
grid.fillContinuousArea(cell, '*');
return grid.getAllNonBlank();
}
System.err.println("Unexpected error, cannot find the filled equivalent of CellSet");
return null;
}
/**
* Returns the first cell that is found to be next to cell
.
*
* @param cell
* @return
*/
public TextGrid.Cell findCellNextTo(TextGrid.Cell cell){
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cCell = (TextGrid.Cell) it.next();
if(cCell.isNextTo(cell)) return cCell;
}
return null;
}
/**
* Returns all the cells that are found to be next to cell
.
*
* @param cell
* @return
*/
public CellSet findCellsNextTo(TextGrid.Cell cell){
if(cell == null) throw new IllegalArgumentException("cell cannot be null");
CellSet set = new CellSet();
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cCell = (TextGrid.Cell) it.next();
if(cCell.isNextTo(cell)) set.add(cCell);
}
return set;
}
public void appendSet(CellSet set){
typeIsValid = false;
Iterator it = set.iterator();
while(it.hasNext()){
TextGrid.Cell cell = (TextGrid.Cell) it.next();
if(find(cell) == null) add(cell);
}
}
public void subtractSet(CellSet set){
typeIsValid = false;
Iterator it = set.iterator();
while(it.hasNext()){
TextGrid.Cell cell = (TextGrid.Cell) it.next();
TextGrid.Cell thisCell = find(cell);
if(thisCell != null) remove(thisCell);
}
}
public int getWidth(){
return getMaxX() - getMinX();
}
public int getHeight(){
return getMaxY() - getMinY();
}
public int getMaxX(){
int result = 0;
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cell = (TextGrid.Cell) it.next();
if(cell.x > result) result = cell.x;
}
return result;
}
public int getMinX(){
int result = Integer.MAX_VALUE;
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cell = (TextGrid.Cell) it.next();
if(cell.x < result) result = cell.x;
}
return result;
}
public int getMaxY(){
int result = 0;
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cell = (TextGrid.Cell) it.next();
if(cell.y > result) result = cell.y;
}
return result;
}
public int getMinY(){
int result = Integer.MAX_VALUE;
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cell = (TextGrid.Cell) it.next();
if(cell.y < result) result = cell.y;
}
return result;
}
public Object remove(TextGrid.Cell cell){
typeIsValid = false;
cell = find(cell);
if(cell != null) return internalSet.remove(cell);
else return null;
}
public boolean equals(Object o){
CellSet otherSet = (CellSet) o;
return internalSet.equals(otherSet.internalSet);
}
public static ArrayList removeDuplicateSets(ArrayList list) {
ArrayList uniqueSets = new ArrayList();
Iterator it = list.iterator();
while(it.hasNext()){
CellSet set = (CellSet) it.next();
boolean isOriginal = true;
Iterator uniquesIt = uniqueSets.iterator();
while(uniquesIt.hasNext()){
CellSet uniqueSet = (CellSet) uniquesIt.next();
if(set.equals(uniqueSet)){
isOriginal = false;
}
}
if(isOriginal) uniqueSets.add(set);
}
return uniqueSets;
}
/**
* Takes into account character info from the grid
*
* @return ArrayList of distinct BoundarySetS
*/
public ArrayList breakIntoDistinctBoundaries(TextGrid grid){
ArrayList result;
AbstractionGrid temp = new AbstractionGrid(grid, this);
result = temp.getDistinctShapes();
return result;
}
/**
*
* @return ArrayList of distinct BoundarySetS
*/
public ArrayList breakIntoDistinctBoundaries(){
ArrayList result = new ArrayList();
//CellSet tempSet = copyCellSet(this);
//tempSet.translate( - this.getMinX() + 1, - this.getMinY() + 1);
// TextGrid boundaryGrid = new TextGrid(tempSet.getMaxX()+2, tempSet.getMaxY()+2);
// boundaryGrid.fillCellsWith(tempSet, '*');
TextGrid boundaryGrid = new TextGrid(getMaxX()+2, getMaxY()+2);
boundaryGrid.fillCellsWith(this, '*');
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell cell = (TextGrid.Cell) it.next();
if(boundaryGrid.isBlank(cell.x, cell.y)) continue;
CellSet boundarySet = boundaryGrid.fillContinuousArea(cell.x, cell.y, ' ');
//boundarySet.translate( this.getMinX() - 1, this.getMinY() - 1);
result.add(boundarySet);
}
return result;
}
/**
*
* Breaks that:
*
* +-----+
* | |
* + ---+-------------------
* | |
* +-----+
*
*
* into the following 3:
*
*
* +-----+
* | |
* + +
* | |
* +-----+
*
* ---
* -------------------
*
*
* @param grid
* @return a list of boundaries that are either open or closed but not mixed
* and they are equivalent to the this
*/
public ArrayList breakTrulyMixedBoundaries(TextGrid grid){
ArrayList result = new ArrayList();
CellSet visitedEnds = new CellSet();
TextGrid workGrid = TextGrid.makeSameSizeAs(grid);
grid.copyCellsTo(this, workGrid);
if (DEBUG){
System.out.println("Breaking truly mixed boundaries below:");
workGrid.printDebug();
}
Iterator it = iterator();
while(it.hasNext()){
TextGrid.Cell start = (TextGrid.Cell) it.next();
if(workGrid.isLinesEnd(start) && !visitedEnds.contains(start)){
if (DEBUG)
System.out.println("Starting new subshape:");
CellSet set = new CellSet();
set.add(start);
if(DEBUG) System.out.println("Added boundary "+start);
TextGrid.Cell previous = start;
TextGrid.Cell cell = null;
CellSet nextCells = workGrid.followCell(previous);
if(nextCells.size() == 0)
throw new IllegalArgumentException("This shape is either open but multipart or has only one cell, and cannot be processed by this method");
cell = (TextGrid.Cell) nextCells.getFirst();
set.add(cell);
if(DEBUG) System.out.println("Added boundary "+cell);
boolean finished = false;
if(workGrid.isLinesEnd(cell)){
visitedEnds.add(cell);
finished = true;
}
while(!finished){
nextCells = workGrid.followCell(cell, previous);
if(nextCells.size() == 1) {
set.add(cell);
if(DEBUG) System.out.println("Added boundary " + cell);
previous = cell;
cell = (TextGrid.Cell) nextCells.getFirst();
//if(!cell.equals(start) && grid.isPointCell(cell))
// s.addToPoints(makePointForCell(cell, workGrid, cellWidth, cellHeight, allRound));
if(workGrid.isLinesEnd(cell)){
visitedEnds.add(cell);
finished = true;
}
} else if(nextCells.size() > 1) {
finished = true;
}
}
result.add(set);
}
}
//substract all boundary sets from this CellSet
CellSet whatsLeft = new CellSet(this);
it = result.iterator();
while (it.hasNext()) {
CellSet set = (CellSet) it.next();
whatsLeft.subtractSet(set);
if(DEBUG) set.printAsGrid();
}
result.add(whatsLeft);
if(DEBUG) whatsLeft.printAsGrid();
return result;
}
public TextGrid makeIntoGrid(){
TextGrid grid = new TextGrid(getMaxX()+2, getMaxY()+2);
grid.fillCellsWith(this, '*');
return grid;
}
public CellSet makeScaledOneThirdEquivalent(){
TextGrid gridBig = this.makeIntoGrid();
gridBig.fillCellsWith(this, '*');
if (VERBOSE_DEBUG){
System.out.println("---> making ScaledOneThirdEquivalent of:");
gridBig.printDebug();
}
TextGrid gridSmall = new TextGrid((getMaxX() + 2) / 3, (getMaxY() + 2) / 3);
for(int y = 0; y < gridBig.getHeight(); y++){
for(int x = 0; x < gridBig.getWidth(); x++){
TextGrid.Cell cell = gridBig.new Cell(x, y);
if(!gridBig.isBlank(cell)) gridSmall.set(x/3, y/3, '*');
}
}
if (VERBOSE_DEBUG){
System.out.println("---> made into grid:");
gridSmall.printDebug();
}
return gridSmall.getAllNonBlank();
}
}