diff --git a/puzzles files/skyscrapers/1646651 b/puzzles files/skyscrapers/1646651
new file mode 100644
index 000000000..847d8639c
--- /dev/null
+++ b/puzzles files/skyscrapers/1646651
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/puzzles files/skyscrapers/easy1.xml b/puzzles files/skyscrapers/easy1.xml
new file mode 100644
index 000000000..9d3135bff
--- /dev/null
+++ b/puzzles files/skyscrapers/easy1.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java
index a4c157c77..331a3dddf 100644
--- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java
+++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java
@@ -62,7 +62,6 @@ public void executeCommand() {
board.setModifiable(false);
transition.setBoard(board);
transition.setRule(caseRule);
- transition.setSelection(elementView.getPuzzleElement().copy());
caseTrans.add(transition);
TreeNode childNode = (TreeNode) tree.addTreeElement(transition);
diff --git a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java
index 3d84287e3..97c9205cf 100644
--- a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java
+++ b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java
@@ -11,7 +11,6 @@ public abstract class PuzzleElement {
protected boolean isModified;
protected boolean isGiven;
protected boolean isValid;
- protected int casesDepended;
/**
* PuzzleElement Constructor creates a new puzzle element.
@@ -23,7 +22,6 @@ public PuzzleElement() {
this.isModified = false;
this.isGiven = false;
this.isValid = true;
- this.casesDepended = 0;
}
/**
@@ -150,24 +148,6 @@ public void setValid(boolean isValid) {
this.isValid = isValid;
}
- /**
- * Get the number of case rules that depend upon the state of this element
- *
- * @return number of cases
- */
- public int getCasesDepended() {
- return this.casesDepended;
- }
-
- /**
- * Sets the number of case rules that depend upon the state of this element
- *
- * @param cases number of cases
- */
- public void setCasesDepended(int cases) {
- this.casesDepended = cases;
- }
-
/**
* Tests whether two puzzle elements objects have the same puzzle element
*
diff --git a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java
index efb96e21a..d9c7e73e5 100644
--- a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java
+++ b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java
@@ -7,8 +7,9 @@
import edu.rpi.legup.model.tree.TreeTransition;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
import static edu.rpi.legup.model.rules.RuleType.CASE;
@@ -78,7 +79,6 @@ public String checkRule(TreeTransition transition) {
String check = checkRuleRaw(transition);
- // Mark transition and new data as valid or not
boolean isCorrect = (check == null);
for (TreeTransition childTrans : parentNodes.get(0).getChildren()) {
childTrans.setCorrect(isCorrect);
@@ -125,31 +125,6 @@ public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement
*/
@Override
public abstract String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement);
-
- /**
- * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid
- * Overridden by case rules dependent on more than just the modified data
- *
- * @param board board state at application
- * @param puzzleElement selected puzzleElement
- * @return List of puzzle elements (typically cells) this application of the case rule depends upon.
- * Defaults to any element modified by any case
- */
- public List dependentElements(Board board, PuzzleElement puzzleElement) {
- List elements = new ArrayList<>();
-
- List cases = getCases(board,puzzleElement);
- for (Board caseBoard : cases) {
- Set data = caseBoard.getModifiedData();
- for (PuzzleElement element : data) {
- if(!elements.contains(board.getPuzzleElement(element))){
- elements.add(board.getPuzzleElement(element));
- }
- }
- }
-
- return elements;
- }
}
diff --git a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java
index 847764b7b..0465ca88c 100644
--- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java
+++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java
@@ -33,10 +33,6 @@ public String checkRule(TreeTransition transition) {
transition.getParents().get(0).getChildren().size() != 1) {
return "State must have only 1 parent and 1 child";
}
- else if (finalBoard.getModifiedData().isEmpty()) {
- // null transition
- return null;
- }
else {
return checkRuleRaw(transition);
}
diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java
index 72572ac72..e1d042626 100644
--- a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java
+++ b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java
@@ -2,7 +2,6 @@
import edu.rpi.legup.model.gameboard.Board;
import edu.rpi.legup.model.gameboard.PuzzleElement;
-import edu.rpi.legup.model.rules.CaseRule;
import edu.rpi.legup.model.rules.Rule;
import edu.rpi.legup.model.rules.RuleType;
@@ -13,8 +12,6 @@ public class TreeTransition extends TreeElement {
private ArrayList parents;
private TreeNode childNode;
private Rule rule;
-
- private PuzzleElement selection;
private boolean isCorrect;
private boolean isVerified;
@@ -29,7 +26,6 @@ public TreeTransition(Board board) {
this.childNode = null;
this.board = board;
this.rule = null;
- this.selection = null;
this.isCorrect = false;
this.isVerified = false;
}
@@ -91,42 +87,13 @@ public void propagateChange(PuzzleElement element) {
}
}
else {
- // Overwrite previous modifications to this element
- board.removeModifiedData(board.getPuzzleElement(element));
-
- // apply changes to tranistion
- board.notifyChange(element);
-
- // mark first transition as modified
- if (!board.getPuzzleElement(element).equalsData(parents.get(0).getBoard().getPuzzleElement(element))) {
- board.addModifiedData(element);
- }
-
- // propagate to children
if (childNode != null) {
-
- // find starting board
- TreeNode head = childNode;
- while (head.getParent() != null) {
- head = head.getParent().getParents().get(0);
- }
- Board headBoard = head.getBoard();
-
- PuzzleElement copy = element.copy();
- // Set as modifiable if reverted to starting value (and started modifiable)
- if (headBoard.getPuzzleElement(element).equalsData(element)) {
- copy.setModifiable(headBoard.getPuzzleElement(element).isModifiable());
- }
- else{
- copy.setModifiable(false);
- }
-
- // apply changes to result node
- childNode.getBoard().notifyChange(copy);
-
- // apply to all child transitions
+ board.notifyChange(element);
+ childNode.getBoard().notifyChange(element.copy());
for (TreeTransition child : childNode.getChildren()) {
- child.propagateChange(copy.copy());
+ PuzzleElement copy = element.copy();
+ copy.setModifiable(false);
+ child.propagateChange(copy);
}
}
}
@@ -360,27 +327,6 @@ public void setRule(Rule rule) {
isVerified = false;
}
- /**
- * Gets he selected element associated with this transition
- *
- * @return If this is a case rule, the selected element for that rule, null otherwise
- */
- public PuzzleElement getSelection() {
- if (this.rule instanceof CaseRule) {
- return selection;
- }
- return null;
- }
-
- /**
- * Sets the selected element associated with this transition
- *
- * @param selection selected element for this transition
- */
- public void setSelection(PuzzleElement selection) {
- this.selection = selection;
- }
-
/**
* Gets whether this transition is correctly justified
*
diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java
index cf7b70ccd..1f166685b 100644
--- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java
@@ -287,67 +287,4 @@ private List getAdjacentCells(LightUpBoard board, LightUpCell cell)
}
return cells;
}
-
- /**
- * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid
- * Overridden by case rules dependent on more than just the modified data
- *
- * @param board board state at application
- * @param puzzleElement selected puzzleElement
- * @return List of puzzle elements (typically cells) this application of the case rule depends upon.
- * Defaults to any element modified by any case
- */
- @Override
- public List dependentElements(Board board, PuzzleElement puzzleElement) {
- List elements = new ArrayList<>();
-
- LightUpBoard puzzleBoard = (LightUpBoard) board;
- LightUpCell point = (LightUpCell)puzzleBoard.getPuzzleElement(puzzleElement);
-
- List cells = getAdjacentCells(puzzleBoard,point);
-
- for (LightUpCell cell : cells) {
- //add cells that can light adjacents from any direction
- Point location = cell.getLocation();
- for (int i = location.x; i < puzzleBoard.getWidth(); i++) {
- System.out.println(i);
- LightUpCell c = puzzleBoard.getCell(i, location.y);
- if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) {
- break;
- }
- else if (!elements.contains(board.getPuzzleElement(c))) {
- elements.add(board.getPuzzleElement(c));
- }
- }
- for (int i = location.x; i >= 0; i--) {
- LightUpCell c = puzzleBoard.getCell(i, location.y);
- if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) {
- break;
- }
- else if (!elements.contains(board.getPuzzleElement(c))) {
- elements.add(board.getPuzzleElement(c));
- }
- }
- for (int i = location.y; i < puzzleBoard.getHeight(); i++) {
- LightUpCell c = puzzleBoard.getCell(location.x, i);
- if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) {
- break;
- }
- else if (!elements.contains(board.getPuzzleElement(c))) {
- elements.add(board.getPuzzleElement(c));
- }
- }
- for (int i = location.y; i >= 0; i--) {
- LightUpCell c = puzzleBoard.getCell(location.x, i);
- if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) {
- break;
- }
- else if (!elements.contains(board.getPuzzleElement(c))) {
- elements.add(board.getPuzzleElement(c));
- }
- }
- }
-
- return elements;
- }
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java
index 0e25586a8..2a40bf45d 100644
--- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java
+++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java
@@ -1,137 +1,119 @@
-package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule;
-
-import edu.rpi.legup.model.gameboard.Board;
-import edu.rpi.legup.model.gameboard.CaseBoard;
-import edu.rpi.legup.model.gameboard.PuzzleElement;
-
-import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard;
-import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell;
-import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType;
-import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement;
-import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation;
-
-import java.util.List;
-import java.util.ArrayList;
-
-public abstract class CaseRule_GenericStatement extends CaseRule_Generic {
-
- public CaseRule_GenericStatement(String ruleID, char operation, String title,
- ShortTruthTableCellType[][] trueCases,
- ShortTruthTableCellType[][] falseCases) {
- super(ruleID, ShortTruthTableOperation.getRuleName(operation),
- title + " case",
- "A known " + title.toUpperCase() + " statement can have multiple forms");
-
- this.operation = operation;
-
- this.trueCases = trueCases;
- this.falseCases = falseCases;
- }
-
- private final char operation;
-
- private final ShortTruthTableCellType[][] trueCases;
- private final ShortTruthTableCellType[][] falseCases;
-
- protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE;
- protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE;
- protected static final ShortTruthTableCellType U = ShortTruthTableCellType.UNKNOWN;
-
- // Adds all elements that can be selected for this caserule
- @Override
- public CaseBoard getCaseBoard(Board board) {
- //copy the board and add all elements that can be selected
- ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy();
- sttBoard.setModifiable(false);
- CaseBoard caseBoard = new CaseBoard(sttBoard, this);
-
- //add all elements that can be selected for the case rule statement
- for (PuzzleElement element : sttBoard.getPuzzleElements()) {
- //get the cell object
- ShortTruthTableCell cell = sttBoard.getCellFromElement(element);
- //the cell must match the symbol
- if (cell.getSymbol() != this.operation) continue;
- //the statement must be assigned with unassigned sub-statements
- if (!cell.getType().isTrueOrFalse()) continue;
- if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue;
- if (this.operation != ShortTruthTableOperation.NOT &&
- cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) {
- continue;
- }
- //if the element has passed all the checks, it can be selected
- caseBoard.addPickableElement(element);
- }
- return caseBoard;
- }
-
- /**
- * Gets the possible cases at a specific location based on this case rule
- *
- * @param board the current board state
- * @param puzzleElement equivalent puzzleElement
- * @return a list of elements the specified could be
- */
- @SuppressWarnings("unchecked")
- @Override
- public ArrayList getCases(Board board, PuzzleElement puzzleElement) {
- ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board);
- ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement);
-
- // If the statement is set to true, collect true cases. Otherwise, collect the false cases
- if (cell.getType() == ShortTruthTableCellType.TRUE) {
- return getCasesFromCell(sttBoard, puzzleElement, trueCases);
- }
- return getCasesFromCell(sttBoard, puzzleElement, falseCases);
- }
-
- /**
- * Collects a list of boards for each possible outcome of case-rule application
- * @param board current board state
- * @param puzzleElement case rule operator
- * @param possibilities list of possibilities for operator state
- * @return ArrayList of Boards
- */
- private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) {
- // Create branch case for each possibility
- ArrayList cases = new ArrayList<>();
- for (int i = 0; i < possibilities.length; i++) {
- // Create a new board to modify and get statement of selected square
- ShortTruthTableBoard b = board.copy();
- ShortTruthTableCell cell = b.getCellFromElement(puzzleElement);
- ShortTruthTableStatement statement = cell.getStatementReference();
-
- // Modify neighboring cells of case-rule application by the provided logical cases
- if (possibilities[i][0] != ShortTruthTableCellType.UNKNOWN) {
- ShortTruthTableCell leftCell = statement.getLeftStatement().getCell();
- leftCell.setData(possibilities[i][0]);
- b.addModifiedData(leftCell);
- }
- if (possibilities[i][1] != ShortTruthTableCellType.UNKNOWN) {
- ShortTruthTableCell rightCell = statement.getRightStatement().getCell();
- rightCell.setData(possibilities[i][1]);
- b.addModifiedData(rightCell);
- }
-
- cases.add(b);
- }
- return cases;
- }
-
- /**
- * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid
- * Overridden by case rules dependent on more than just the modified data
- *
- * @param board board state at application
- * @param puzzleElement selected puzzleElement
- * @return List of puzzle elements (typically cells) this application of the case rule depends upon.
- * Defaults to any element modified by any case
- */
- @Override
- public List dependentElements(Board board, PuzzleElement puzzleElement) {
- List elements = super.dependentElements(board,puzzleElement);
-
- elements.add(board.getPuzzleElement(puzzleElement));
-
- return elements;
- }
-}
+package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule;
+
+import edu.rpi.legup.model.gameboard.Board;
+import edu.rpi.legup.model.gameboard.CaseBoard;
+import edu.rpi.legup.model.gameboard.PuzzleElement;
+
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation;
+
+
+import java.util.ArrayList;
+
+public abstract class CaseRule_GenericStatement extends CaseRule_Generic {
+
+ public CaseRule_GenericStatement(String ruleID, char operation, String title,
+ ShortTruthTableCellType[][] trueCases,
+ ShortTruthTableCellType[][] falseCases) {
+ super(ruleID, ShortTruthTableOperation.getRuleName(operation),
+ title + " case",
+ "A known " + title.toUpperCase() + " statement can have multiple forms");
+
+ this.operation = operation;
+
+ this.trueCases = trueCases;
+ this.falseCases = falseCases;
+ }
+
+ private final char operation;
+
+ private final ShortTruthTableCellType[][] trueCases;
+ private final ShortTruthTableCellType[][] falseCases;
+
+ protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE;
+ protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE;
+ protected static final ShortTruthTableCellType U = ShortTruthTableCellType.UNKNOWN;
+
+ // Adds all elements that can be selected for this caserule
+ @Override
+ public CaseBoard getCaseBoard(Board board) {
+ //copy the board and add all elements that can be selected
+ ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy();
+ sttBoard.setModifiable(false);
+ CaseBoard caseBoard = new CaseBoard(sttBoard, this);
+
+ //add all elements that can be selected for the case rule statement
+ for (PuzzleElement element : sttBoard.getPuzzleElements()) {
+ //get the cell object
+ ShortTruthTableCell cell = sttBoard.getCellFromElement(element);
+ //the cell must match the symbol
+ if (cell.getSymbol() != this.operation) continue;
+ //the statement must be assigned with unassigned sub-statements
+ if (!cell.getType().isTrueOrFalse()) continue;
+ if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue;
+ if (this.operation != ShortTruthTableOperation.NOT &&
+ cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) {
+ continue;
+ }
+ //if the element has passed all the checks, it can be selected
+ caseBoard.addPickableElement(element);
+ }
+ return caseBoard;
+ }
+
+ /**
+ * Gets the possible cases at a specific location based on this case rule
+ *
+ * @param board the current board state
+ * @param puzzleElement equivalent puzzleElement
+ * @return a list of elements the specified could be
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public ArrayList getCases(Board board, PuzzleElement puzzleElement) {
+ ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board);
+ ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement);
+
+ // If the statement is set to true, collect true cases. Otherwise, collect the false cases
+ if (cell.getType() == ShortTruthTableCellType.TRUE) {
+ return getCasesFromCell(sttBoard, puzzleElement, trueCases);
+ }
+ return getCasesFromCell(sttBoard, puzzleElement, falseCases);
+ }
+
+ /**
+ * Collects a list of boards for each possible outcome of case-rule application
+ * @param board current board state
+ * @param puzzleElement case rule operator
+ * @param possibilities list of possibilities for operator state
+ * @return ArrayList of Boards
+ */
+ private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) {
+ // Create branch case for each possibility
+ ArrayList cases = new ArrayList<>();
+ for (int i = 0; i < possibilities.length; i++) {
+ // Create a new board to modify and get statement of selected square
+ ShortTruthTableBoard b = board.copy();
+ ShortTruthTableCell cell = b.getCellFromElement(puzzleElement);
+ ShortTruthTableStatement statement = cell.getStatementReference();
+
+ // Modify neighboring cells of case-rule application by the provided logical cases
+ if (possibilities[i][0] != ShortTruthTableCellType.UNKNOWN) {
+ ShortTruthTableCell leftCell = statement.getLeftStatement().getCell();
+ leftCell.setData(possibilities[i][0]);
+ b.addModifiedData(leftCell);
+ }
+ if (possibilities[i][1] != ShortTruthTableCellType.UNKNOWN) {
+ ShortTruthTableCell rightCell = statement.getRightStatement().getCell();
+ rightCell.setData(possibilities[i][1]);
+ b.addModifiedData(rightCell);
+ }
+
+ cases.add(b);
+ }
+ return cases;
+ }
+}
diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java
index 52e8a6400..ca6bcbe02 100644
--- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java
+++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java
@@ -25,6 +25,9 @@ public class SkyscrapersBoard extends GridBoard {
private boolean viewFlag = false;
private boolean dupeFlag = false;
+ private SkyscrapersClue modClue = null;
+ //helper variable for case rule verification, tracks recently modified row/col
+
public SkyscrapersBoard(int size) {
super(size, size);
@@ -91,6 +94,14 @@ public void setViewFlag(boolean newFlag) {
viewFlag = newFlag;
}
+ public SkyscrapersClue getmodClue() {
+ return modClue;
+ }
+
+ public void setModClue(SkyscrapersClue newClue) {
+ modClue = newClue;
+ }
+
@Override
public SkyscrapersCell getCell(int x, int y) {
return (SkyscrapersCell) super.getCell(x, y);
diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java
index 1e7b1b45e..dc68f45c7 100644
--- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java
+++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java
@@ -47,9 +47,6 @@ public void setType(SkyscrapersType type) {
}
public SkyscrapersClue copy() {
- SkyscrapersClue copy = new SkyscrapersClue(data, clueIndex, type);
- copy.setIndex(index);
- copy.setModifiable(isModifiable);
- return copy;
+ return null;
}
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java
index 01527294a..683b742bf 100644
--- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java
@@ -69,6 +69,7 @@ public ArrayList getCasesFor(Board board, PuzzleElement puzzleElement, In
PuzzleElement newCell = newCase.getPuzzleElement(cell);
newCell.setData(number);
newCase.addModifiedData(newCell);
+ newCase.setModClue((SkyscrapersClue) newCase.getPuzzleElement(clue));
//if flags
boolean passed = true;
@@ -102,7 +103,12 @@ public String checkRuleRaw(TreeTransition transition) {
return "This case rule must have at least one child.";
}
- if (childTransitions.size() != getCasesFor(oldBoard, oldBoard.getPuzzleElement(transition.getSelection()), (Integer) childTransitions.get(0).getBoard().getModifiedData().iterator().next().getData()).size()) {
+ //find changed row/col
+ SkyscrapersClue modClue = ((SkyscrapersBoard) childTransitions.get(0).getBoard()).getmodClue();
+
+ //System.out.println(modClue.getType());
+ //System.out.println(modClue.getClueIndex());
+ if (childTransitions.size() != getCasesFor(oldBoard, modClue, (Integer) childTransitions.get(0).getBoard().getModifiedData().iterator().next().getData()).size()) {
//System.out.println("Wrong number of cases.");
return "Wrong number of cases.";
}
@@ -126,49 +132,4 @@ public String checkRuleRaw(TreeTransition transition) {
public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
return checkRuleRaw(transition);
}
-
- /**
- * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid
- * Overridden by case rules dependent on more than just the modified data
- *
- * @param board board state at application
- * @param puzzleElement selected puzzleElement
- * @return List of puzzle elements (typically cells) this application of the case rule depends upon.
- * Defaults to any element modified by any case
- */
- @Override
- public List dependentElements(Board board, PuzzleElement puzzleElement) {
- List elements = new ArrayList<>();
-
- SkyscrapersBoard puzzleBoard = (SkyscrapersBoard) board;
- SkyscrapersClue clue = (SkyscrapersClue)puzzleBoard.getPuzzleElement(puzzleElement);
-
- // check each point in modified row/col
- List data = puzzleBoard.getRowCol(clue.getClueIndex(),SkyscrapersType.ANY,clue.getType() == SkyscrapersType.CLUE_WEST);
- for (SkyscrapersCell point : data) {
- List cells = new ArrayList<>(List.of(point));
-
- // if dependent on row/col
- if ((puzzleBoard.getDupeFlag() || puzzleBoard.getViewFlag()) && point.getType() == SkyscrapersType.UNKNOWN) {
- // get perpendicular row/col intersecting this point
- int index;
- if (clue.getType() == SkyscrapersType.CLUE_WEST) {
- index = point.getLocation().x;
- }
- else {
- index = point.getLocation().y;
- }
- cells.addAll(puzzleBoard.getRowCol(index,SkyscrapersType.ANY,clue.getType() != SkyscrapersType.CLUE_WEST));
- }
-
- // add all to result
- for (SkyscrapersCell cell : cells) {
- if (!elements.contains(board.getPuzzleElement(cell))) {
- elements.add(board.getPuzzleElement(cell));
- }
- }
- }
-
- return elements;
- }
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java
index 3bf0de70a..a061c62a3 100644
--- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java
@@ -7,7 +7,6 @@
import edu.rpi.legup.model.tree.TreeTransition;
import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard;
import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell;
-import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersClue;
import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType;
import java.awt.*;
@@ -135,38 +134,4 @@ public String checkRuleRaw(TreeTransition transition) {
public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
return checkRuleRaw(transition);
}
-
- /**
- * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid
- * Overridden by case rules dependent on more than just the modified data
- *
- * @param board board state at application
- * @param puzzleElement selected puzzleElement
- * @return List of puzzle elements (typically cells) this application of the case rule depends upon.
- * Defaults to any element modified by any case
- */
- @Override
- public List dependentElements(Board board, PuzzleElement puzzleElement) {
- List elements = new ArrayList<>();
-
- SkyscrapersBoard puzzleBoard = (SkyscrapersBoard) board;
- SkyscrapersCell point = (SkyscrapersCell)puzzleBoard.getPuzzleElement(puzzleElement);
-
- List cells = new ArrayList<>(List.of(point));
-
- // if dependent on row/col
- if (puzzleBoard.getDupeFlag() || puzzleBoard.getViewFlag()) {
- // add all cells in row/col intersecting given point
- cells.addAll(puzzleBoard.getRowCol(point.getLocation().x,SkyscrapersType.ANY,false));
- cells.addAll(puzzleBoard.getRowCol(point.getLocation().y,SkyscrapersType.ANY,true));
- }
-
- for (SkyscrapersCell cell : cells) {
- if (!elements.contains(board.getPuzzleElement(cell))) {
- elements.add(board.getPuzzleElement(cell));
- }
- }
-
- return elements;
- }
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java
index dc809f34d..2542ea335 100644
--- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java
+++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java
@@ -124,11 +124,21 @@ public void notifyDeletion(PuzzleElement puzzleElement) {
public List getAdjacent(TreeTentCell cell, TreeTentType type) {
List adj = new ArrayList<>();
Point loc = cell.getLocation();
- for (int i = -2; i < 2; i++) {
- TreeTentCell adjCell = getCell(loc.x + (i % 2), loc.y + ((i + 1) % 2));
- if (adjCell != null && adjCell.getType() == type) {
- adj.add(adjCell);
- }
+ TreeTentCell up = getCell(loc.x, loc.y - 1);
+ TreeTentCell right = getCell(loc.x + 1, loc.y);
+ TreeTentCell down = getCell(loc.x, loc.y + 1);
+ TreeTentCell left = getCell(loc.x - 1, loc.y);
+ if (up != null && up.getType() == type) {
+ adj.add(up);
+ }
+ if (right != null && right.getType() == type) {
+ adj.add(right);
+ }
+ if (down != null && down.getType() == type) {
+ adj.add(down);
+ }
+ if (left != null && left.getType() == type) {
+ adj.add(left);
}
return adj;
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java
index 698b3aa5e..538772b74 100644
--- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java
@@ -160,29 +160,4 @@ public String checkRuleRaw(TreeTransition transition) {
public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
return null;
}
-
- /**
- * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid
- * Overridden by case rules dependent on more than just the modified data
- *
- * @param board board state at application
- * @param puzzleElement selected puzzleElement
- * @return List of puzzle elements (typically cells) this application of the case rule depends upon.
- * Defaults to any element modified by any case
- */
- @Override
- public List dependentElements(Board board, PuzzleElement puzzleElement) {
- List elements = new ArrayList<>();
-
- TreeTentBoard treeTentBoard = (TreeTentBoard) board;
- TreeTentClue clue = (TreeTentClue) puzzleElement;
-
- // add all elements of filled row
- for (int i = 0; i < treeTentBoard.getWidth(); i++) {
- TreeTentCell cell = treeTentBoard.getCell(i, clue.getClueIndex()-1);
- elements.add(board.getPuzzleElement((cell)));
- }
-
- return elements;
- }
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java
index 39b1d0251..36f466f87 100644
--- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java
@@ -5,6 +5,7 @@
import edu.rpi.legup.model.gameboard.PuzzleElement;
import edu.rpi.legup.model.rules.CaseRule;
import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.puzzle.treetent.TreeTent;
import edu.rpi.legup.puzzle.treetent.TreeTentBoard;
import edu.rpi.legup.puzzle.treetent.TreeTentType;
import edu.rpi.legup.puzzle.treetent.TreeTentCell;
@@ -150,18 +151,4 @@ public String checkRuleRaw(TreeTransition transition) {
public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
return checkRuleRaw(transition);
}
-
- /**
- * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid
- * Overridden by case rules dependent on more than just the modified data
- *
- * @param board board state at application
- * @param puzzleElement selected puzzleElement
- * @return List of puzzle elements (typically cells) this application of the case rule depends upon.
- * Defaults to any element modified by any case
- */
- @Override
- public List dependentElements(Board board, PuzzleElement puzzleElement) {
- return List.of(board.getPuzzleElement(puzzleElement));
- }
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java
index 72ffd62eb..249547301 100644
--- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java
@@ -10,7 +10,6 @@
import edu.rpi.legup.puzzle.treetent.TreeTentLine;
import edu.rpi.legup.puzzle.treetent.TreeTentType;
-import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -153,32 +152,4 @@ public String checkRuleRaw(TreeTransition transition) {
public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
return checkRuleRaw(transition);
}
-
- /**
- * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid
- * Overridden by case rules dependent on more than just the modified data
- *
- * @param board board state at application
- * @param puzzleElement selected puzzleElement
- * @return List of puzzle elements (typically cells) this application of the case rule depends upon.
- * Defaults to any element modified by any case
- */
- @Override
- public List dependentElements(Board board, PuzzleElement puzzleElement) {
- List elements = new ArrayList<>(List.of(board.getPuzzleElement(puzzleElement)));
-
- TreeTentBoard treeTentBoard = (TreeTentBoard) board;
- TreeTentCell point = (TreeTentCell) puzzleElement;
-
- // get all adjacent cells
- Point loc = point.getLocation();
- for (int i = -2; i < 2; i++) {
- TreeTentCell cell = treeTentBoard.getCell(loc.x + (i % 2), loc.y + ((i + 1) % 2));
- if (cell != null) {
- elements.add(board.getPuzzleElement(cell));
- }
- }
-
- return elements;
- }
}
diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java
index 9bfffe60a..ecf59146d 100644
--- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java
+++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java
@@ -1,886 +1,756 @@
-package edu.rpi.legup.ui.proofeditorui.treeview;
-
-import edu.rpi.legup.app.GameBoardFacade;
-import edu.rpi.legup.controller.TreeController;
-import edu.rpi.legup.model.gameboard.Board;
-import edu.rpi.legup.model.gameboard.GridCell;
-import edu.rpi.legup.model.gameboard.PuzzleElement;
-import edu.rpi.legup.model.observer.ITreeListener;
-import edu.rpi.legup.model.rules.CaseRule;
-import edu.rpi.legup.model.rules.Rule;
-import edu.rpi.legup.model.tree.Tree;
-import edu.rpi.legup.model.tree.TreeElement;
-import edu.rpi.legup.model.tree.TreeNode;
-import edu.rpi.legup.model.tree.TreeTransition;
-import edu.rpi.legup.ui.ScrollView;
-import edu.rpi.legup.utility.DisjointSets;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.util.*;
-import java.util.List;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import static edu.rpi.legup.model.tree.TreeElementType.NODE;
-import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION;
-import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER;
-import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS;
-
-public class TreeView extends ScrollView implements ITreeListener {
- private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName());
-
- private static final int TRANS_GAP = 5;
-
- private static final int NODE_GAP_WIDTH = 70;
- private static final int NODE_GAP_HEIGHT = 15;
-
- private static final int BORDER_GAP_HEIGHT = 20;
- private static final int BORDER_GAP_WIDTH = 20;
-
- private static final int BORDER_SPACING = 100;
-
- private TreeNodeView nodeHover;
-
- private ArrayList currentStateBoxes;
- private Rectangle bounds = new Rectangle(0, 0, 0, 0);
-
- private Tree tree;
- private TreeNodeView rootNodeView;
- private Map viewMap;
- private Dimension dimension;
-
- private TreeViewSelection selection;
-
- public TreeView(TreeController treeController) {
- super(treeController);
- currentStateBoxes = new ArrayList<>();
- setSize(dimension = new Dimension(100, 200));
- setPreferredSize(new Dimension(640, 160));
-
- viewMap = new HashMap<>();
-
- selection = new TreeViewSelection();
- }
-
- public TreeViewSelection getSelection() {
- return selection;
- }
-
- /**
- * Gets the tree node puzzleElement that the mouse is hovering over
- *
- * @return tree node puzzleElement that the mouse is hovering over
- */
- public TreeNodeView getNodeHover() {
- return nodeHover;
- }
-
- /**
- * Sets the tree node puzzleElement that the mouse is hovering over
- *
- * @param nodeHover tree node puzzleElement the mouse is hovering over
- */
- public void setNodeHover(TreeNodeView nodeHover) {
- this.nodeHover = nodeHover;
- }
-
- /**
- * Gets the TreeElementView by the specified point or null if no view exists at the specified point
- *
- * @param point location to query for a view
- * @return TreeElementView at the point specified, otherwise null
- */
- public TreeElementView getTreeElementView(Point point) {
- return getTreeElementView(point, rootNodeView);
- }
-
- /**
- * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or
- * the view specified is null
- *
- * @param point location to query for a view
- * @param elementView view to determine if the point is contained within it
- * @return TreeElementView at the point specified, otherwise null
- */
- private TreeElementView getTreeElementView(Point point, TreeElementView elementView) {
- if (elementView == null) {
- return null;
- }
- else {
- if (elementView.contains(point) && elementView.isVisible()) {
- if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) {
- return null;
- }
- return elementView;
- }
- else {
- if (elementView.getType() == NODE) {
- TreeNodeView nodeView = (TreeNodeView) elementView;
- for (TreeTransitionView transitionView : nodeView.getChildrenViews()) {
- TreeElementView view = getTreeElementView(point, transitionView);
- if (view != null) {
- return view;
- }
- }
- }
- else {
- TreeTransitionView transitionView = (TreeTransitionView) elementView;
- return getTreeElementView(point, transitionView.getChildView());
- }
- }
- }
- return null;
- }
-
- public void updateTreeView(Tree tree) {
- this.tree = tree;
- if (selection.getSelectedViews().size() == 0) {
- selection.newSelection(new TreeNodeView(tree.getRootNode()));
- }
- repaint();
- }
-
- /**
- * Sets the tree associated with this TreeView
- *
- * @param tree tree
- */
- public void setTree(Tree tree) {
- this.tree = tree;
- }
-
- public void updateTreeSize() {
- if (GameBoardFacade.getInstance().getTree() == null) {
- return;
- }
- setSize(bounds.getSize());
- }
-
- public void reset() {
- if (bounds.x != 0 || bounds.y != 0) {
- updateTreeSize();
- }
- }
-
- public void zoomFit() {
- double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200);
- double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120);
- zoomTo(Math.min(fitWidth, fitHeight));
- viewport.setViewPosition(new Point(0, viewport.getHeight() / 2));
- }
-
- /**
- * Creates a customized viewport for the scroll pane
- *
- * @return viewport for the scroll pane
- */
- @Override
- protected JViewport createViewport() {
- return new JViewport() {
- @Override
- protected LayoutManager createLayoutManager() {
- return new ViewportLayout() {
- @Override
- public void layoutContainer(Container parent) {
- Point point = viewport.getViewPosition();
- // determine the maximum x and y view positions
- int mx = getCanvas().getWidth() - viewport.getWidth();
- int my = getCanvas().getHeight() - viewport.getHeight();
- // obey edge boundaries
- if (point.x < 0) {
- point.x = 0;
- }
- if (point.x > mx) {
- point.x = mx;
- }
- if (point.y < 0) {
- point.y = 0;
- }
- if (point.y > my) {
- point.y = my;
- }
- // center margins
- if (mx < 0) {
- point.x = 0;
- }
- if (my < 0) {
- point.y = my / 2;
- }
- viewport.setViewPosition(point);
- }
- };
- }
- };
- }
-
- public void draw(Graphics2D graphics2D) {
- currentStateBoxes.clear();
- Tree tree = GameBoardFacade.getInstance().getTree();
- if (tree != null) {
- //setSize(bounds.getDimension());
- graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
-
- drawTree(graphics2D);
-
- dimension.width += BORDER_SPACING;
- setSize(dimension);
-// graphics2D.drawRect(0,0, dimension.width, dimension.height);
-
- if (selection.getHover() != null) {
- drawMouseOver(graphics2D);
- }
- }
- }
-
- public void zoomReset() {
- zoomTo(1.0);
- viewport.setViewPosition(new Point(0, 0));
- }
-
- private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) {
- if (nodeView != null) {
- nodeView.draw(graphics2D);
- for (TreeTransitionView transitionView : nodeView.getChildrenViews()) {
- transitionView.draw(graphics2D);
- redrawTree(graphics2D, transitionView.getChildView());
- }
- }
- }
-
- public void removeTreeElement(TreeElementView view) {
- if (view.getType() == NODE) {
- TreeNodeView nodeView = (TreeNodeView) view;
- nodeView.getParentView().setChildView(null);
- }
- else {
- TreeTransitionView transitionView = (TreeTransitionView) view;
- transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView));
- }
- }
-
- /**
- * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image
- *
- * @param g the graphics to use to draw
- */
- public void drawMouseOver(Graphics2D g) {
- if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) {
- TreeTransition transition = (TreeTransition) selection.getHover().treeElement;
- int imgWidth = 100;
- int imgHeight = 100;
-
- BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB);
- image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null);
- Point mousePoint = selection.getMousePoint();
- g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null);
- }
- }
-
- public void resetView() {
- this.tree = null;
- this.rootNodeView = null;
- this.selection.clearSelection();
- this.selection.clearHover();
- }
-
- /**
- * Called when a tree puzzleElement is added to the tree
- *
- * @param treeElement TreeElement that was added to the tree
- */
- @Override
- public void onTreeElementAdded(TreeElement treeElement) {
- if (treeElement.getType() == NODE) {
- addTreeNode((TreeNode) treeElement);
- }
- else {
- addTreeTransition((TreeTransition) treeElement);
- }
- repaint();
- }
-
- /**
- * Called when a tree puzzleElement is removed from the tree
- *
- * @param element TreeElement that was removed to the tree
- */
- @Override
- public void onTreeElementRemoved(TreeElement element) {
- if (element.getType() == NODE) {
- TreeNode node = (TreeNode) element;
- TreeNodeView nodeView = (TreeNodeView) viewMap.get(node);
-
- nodeView.getParentView().setChildView(null);
- removeTreeNode(node);
- }
- else {
- TreeTransition trans = (TreeTransition) element;
- TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
-
- // unlock ancestor elements if case rule deleted
- Rule rule = trans.getRule();
- for (TreeNode node : trans.getParents()) {
-
- // only if the last case of a case rule will be deleted
- if (!(rule instanceof CaseRule && node.getChildren().isEmpty())) {
- continue;
- }
-
- CaseRule caseRule = (CaseRule)rule;
- // set dependent elements to be modifiable by ancestors (if not dependent on others)
- List ancestors = node.getAncestors();
- for (TreeNode ancestor : ancestors) {
- // for all ancestors but root
- if (ancestor.getParent() == null) {
- continue;
- }
-
- for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), trans.getSelection())) {
- // decrement, unlock if 0 cases depended
- PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement);
- oldElement.setCasesDepended(oldElement.getCasesDepended() - 1);
- if (oldElement.getCasesDepended() != 0) {
- continue;
- }
-
- // set modifiable if started modifiable
- boolean modifiable = tree.getRootNode().getBoard().getPuzzleElement(oldElement).isModifiable();
-
- // unmodifiable if already modified
- TreeNode modNode = ancestor.getParent().getParents().get(0);
- while (modNode.getParent()!=null) {
- Board modBoard = modNode.getParent().getBoard();
- if (modBoard.getModifiedData().contains(modBoard.getPuzzleElement(oldElement))) {
- modifiable = false;
- break;
- }
- modNode = modNode.getParent().getParents().get(0);
- }
- oldElement.setModifiable(modifiable);
- }
- }
- }
-
- transView.getParentViews().forEach(n -> n.removeChildrenView(transView));
- removeTreeTransition(trans);
- }
- repaint();
- }
-
- /**
- * Called when the tree selection was changed
- *
- * @param selection tree selection that was changed
- */
- @Override
- public void onTreeSelectionChanged(TreeViewSelection selection) {
- this.selection.getSelectedViews().forEach(v -> v.setSelected(false));
- selection.getSelectedViews().forEach(v -> v.setSelected(true));
- this.selection = selection;
- repaint();
- }
-
- /**
- * Called when the model has finished updating the tree.
- */
- @Override
- public void onUpdateTree() {
- repaint();
- }
-
- /**
- * Gets the TreeElementView by the corresponding TreeElement associated with it
- *
- * @param element TreeElement of the view
- * @return TreeElementView of the TreeElement associated with it
- */
- public TreeElementView getElementView(TreeElement element) {
- return viewMap.get(element);
- }
-
- private void removeTreeNode(TreeNode node) {
- viewMap.remove(node);
- List children = node.getChildren();
-
- // if child is a case rule, unlock ancestor elements
- if (!children.isEmpty()) {
- Rule rule = children.get(0).getRule();
- if (rule instanceof CaseRule) {
- CaseRule caseRule = (CaseRule)rule;
- // set dependent elements to be modifiable by ancestors (if not dependent on others)
- List ancestors = node.getAncestors();
- for (TreeNode ancestor : ancestors) {
- // for all ancestors but root
- if (ancestor.getParent() == null) {
- continue;
- }
- for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), children.get(0).getSelection())) {
- // decrement, unlock if 0 cases depended
- PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement);
- oldElement.setCasesDepended(oldElement.getCasesDepended() - 1);
- if (oldElement.getCasesDepended() == 0) {
- continue;
- }
-
- // set modifiable if started modifiable
- boolean modifiable = tree.getRootNode().getBoard().getPuzzleElement(oldElement).isModifiable();
-
- // unmodifiable if already modified
- TreeNode modNode = ancestor.getParent().getParents().get(0);
- while (modNode.getParent() != null) {
- Board modBoard = modNode.getParent().getBoard();
- if (modBoard.getModifiedData().contains(modBoard.getPuzzleElement(oldElement))) {
- modifiable = false;
- break;
- }
- modNode = modNode.getParent().getParents().get(0);
- }
- oldElement.setModifiable(modifiable);
- }
- }
- }
- }
- node.getChildren().forEach(t -> removeTreeTransition(t));
- }
-
- private void removeTreeTransition(TreeTransition trans) {
- viewMap.remove(trans);
- if (trans.getChildNode() != null) {
- removeTreeNode(trans.getChildNode());
- }
- }
-
- private void addTreeNode(TreeNode node) {
- TreeTransition parent = node.getParent();
-
- TreeNodeView nodeView = new TreeNodeView(node);
- TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent);
-
- nodeView.setParentView(parentView);
- parentView.setChildView(nodeView);
-
- viewMap.put(node, nodeView);
-
- if (!node.getChildren().isEmpty()) {
-
- // if adding a case rule, lock dependent ancestor elements
- Rule rule = node.getChildren().get(0).getRule();
- if (rule instanceof CaseRule) {
- CaseRule caseRule = (CaseRule)rule;
-
- List ancestors = node.getAncestors();
- for (TreeNode ancestor : ancestors) {
- // for all ancestors but root
- if (ancestor.getParent() == null) {
- continue;
- }
- for (PuzzleElement element : caseRule.dependentElements(node.getBoard(), node.getChildren().get(0).getSelection())) {
- // increment and lock
- PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element);
- oldElement.setCasesDepended(oldElement.getCasesDepended()+1);
- oldElement.setModifiable(false);
- }
- }
- }
-
- node.getChildren().forEach(t -> addTreeTransition(t));
- }
- }
-
- private void addTreeTransition(TreeTransition trans) {
- List parents = trans.getParents();
-
- TreeTransitionView transView = new TreeTransitionView(trans);
- for (TreeNode parent : parents) {
- TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent);
- transView.addParentView(parentNodeView);
- parentNodeView.addChildrenView(transView);
-
- // if transition is a new case rule, lock dependent ancestor elements
- Rule rule = trans.getRule();
- if (rule instanceof CaseRule && parent.getChildren().size()==1) {
- CaseRule caseRule = (CaseRule)rule;
-
- List ancestors = parent.getAncestors();
- for (TreeNode ancestor : ancestors) {
- // for all ancestors but root
- if (ancestor.getParent() == null) {
- continue;
- }
- for (PuzzleElement element : caseRule.dependentElements(parent.getBoard(), trans.getSelection())) {
- // increment and lock
- PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element);
- oldElement.setCasesDepended(oldElement.getCasesDepended()+1);
- oldElement.setModifiable(false);
- }
- }
- }
- }
-
- viewMap.put(trans, transView);
-
- if (trans.getChildNode() != null) {
- addTreeNode(trans.getChildNode());
- }
- }
-
- ///New Draw Methods
-
- public void drawTree(Graphics2D graphics2D) {
- if (tree == null) {
- LOGGER.error("Unable to draw tree.");
- }
- else {
- if (rootNodeView == null) {
- rootNodeView = new TreeNodeView(tree.getRootNode());
-
- LOGGER.debug("Creating new views for tree view.");
- createViews(rootNodeView);
-
- selection.newSelection(rootNodeView);
- }
-
- dimension = new Dimension(0, 0);
- calcSpan(rootNodeView);
- rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING);
-
- calculateViewLocations(rootNodeView, 0);
- dimension.height = (int) rootNodeView.getSpan();
-
- redrawTree(graphics2D, rootNodeView);
- LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height);
- }
- }
-
- public void createViews(TreeNodeView nodeView) {
- if (nodeView != null) {
- viewMap.put(nodeView.getTreeElement(), nodeView);
-
- TreeNode node = nodeView.getTreeElement();
- for (TreeTransition trans : node.getChildren()) {
- TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
- if (transView != null) {
- nodeView.addChildrenView(transView);
- transView.addParentView(nodeView);
- break;
- }
- transView = new TreeTransitionView(trans);
-
- viewMap.put(transView.getTreeElement(), transView);
-
- transView.addParentView(nodeView);
- nodeView.addChildrenView(transView);
-
- TreeNode childNode = trans.getChildNode();
- if (childNode != null) {
- TreeNodeView childNodeView = new TreeNodeView(childNode);
- viewMap.put(childNodeView.getTreeElement(), childNodeView);
-
- childNodeView.setParentView(transView);
- transView.setChildView(childNodeView);
-
- createViews(childNodeView);
- }
- }
- }
- }
-
- public void calculateViewLocations(TreeNodeView nodeView, int depth) {
- nodeView.setDepth(depth);
- int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER;
- nodeView.setX(xLoc);
- dimension.width = Math.max(dimension.width, xLoc);
-
- TreeTransitionView parentTransView = nodeView.getParentView();
- int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY();
- nodeView.setY(yLoc);
-
- ArrayList children = nodeView.getChildrenViews();
- switch (children.size()) {
- case 0:
- break;
- case 1: {
- TreeTransitionView childView = children.get(0);
-
- List parentsViews = childView.getParentViews();
- if (parentsViews.size() == 1) {
- childView.setEndY(yLoc);
-
- childView.setDepth(depth);
-
- Point lineStartPoint = childView.getLineStartPoint(0);
- lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2;
- lineStartPoint.y = yLoc;
- childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
-
- dimension.width = Math.max(dimension.width, childView.getEndX());
-
- TreeNodeView childNodeView = childView.getChildView();
- if (childNodeView != null) {
- calculateViewLocations(childNodeView, depth + 1);
- }
- }
- else {
- if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) {
- int yAvg = 0;
- for (int i = 0; i < parentsViews.size(); i++) {
- TreeNodeView parentNodeView = parentsViews.get(i);
- depth = Math.max(depth, parentNodeView.getDepth());
- yAvg += parentNodeView.getY();
-
- Point lineStartPoint = childView.getLineStartPoint(i);
- lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2;
- lineStartPoint.y = parentNodeView.getY();
- }
- yAvg /= parentsViews.size();
- childView.setEndY(yAvg);
-
- childView.setDepth(depth);
-
- childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
-
- dimension.width = Math.max(dimension.width, childView.getEndX());
-
- TreeNodeView childNodeView = childView.getChildView();
- if (childNodeView != null) {
- calculateViewLocations(childNodeView, depth + 1);
- }
- }
- }
- break;
- }
- default: {
- int span = 0;
- for (TreeTransitionView childView : children) {
- span += childView.getSpan();
- }
-
- span = (int) ((nodeView.getSpan() - span) / 2);
- for (int i = 0; i < children.size(); i++) {
- TreeTransitionView childView = children.get(i);
-
- childView.setDepth(depth);
-
- Point lineStartPoint = childView.getLineStartPoint(0);
- lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2;
- lineStartPoint.y = yLoc;
- childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
- childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2));
-
- span += childView.getSpan();
- TreeNodeView childNodeView = childView.getChildView();
- if (childNodeView != null) {
- calculateViewLocations(childNodeView, depth + 1);
- }
- }
- break;
- }
- }
- }
-
- public void calcSpan(TreeElementView view) {
- if (view.getType() == NODE) {
- TreeNodeView nodeView = (TreeNodeView) view;
- TreeNode node = nodeView.getTreeElement();
- if (nodeView.getChildrenViews().size() == 0) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- if (nodeView.getChildrenViews().size() == 1) {
- TreeTransitionView childView = nodeView.getChildrenViews().get(0);
- calcSpan(childView);
- if (childView.getParentViews().size() > 1) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- nodeView.setSpan(childView.getSpan());
- }
- }
- else {
- DisjointSets branches = node.findMergingBranches();
- List children = node.getChildren();
-
- if (node == children.get(0).getParents().get(0)) {
- reorderBranches(node, branches);
- ArrayList newChildrenViews = new ArrayList<>();
- for (TreeTransition trans : node.getChildren()) {
- newChildrenViews.add((TreeTransitionView) viewMap.get(trans));
- }
- nodeView.setChildrenViews(newChildrenViews);
- }
-
- List> mergingSets = branches.getAllSets();
-
- double span = 0.0;
- for (Set mergeSet : mergingSets) {
- if (mergeSet.size() > 1) {
- TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet);
- TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint);
- double subSpan = 0.0;
- for (TreeTransition branch : mergeSet) {
- TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch);
- subCalcSpan(branchView, mergePointView);
- subSpan += branchView.getSpan();
- }
- calcSpan(mergePointView);
- span += Math.max(mergePointView.getSpan(), subSpan);
- }
- else {
- TreeTransition trans = mergeSet.iterator().next();
- TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
- calcSpan(transView);
- span += transView.getSpan();
- }
- }
- nodeView.setSpan(span);
- }
- }
- }
- else {
- TreeTransitionView transView = (TreeTransitionView) view;
- TreeNodeView nodeView = transView.getChildView();
- if (nodeView == null) {
- transView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- calcSpan(nodeView);
- transView.setSpan(nodeView.getSpan());
- }
- }
- }
-
- /**
- * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view
- * specified as stop. Stop tree puzzleElement is NOT included in the span calculation
- *
- * @param view
- * @param stop
- */
- private void subCalcSpan(TreeElementView view, TreeElementView stop) {
- //safe-guard for infinite loop
- if (view == stop) {
- return;
- }
-
- if (view.getType() == NODE) {
- TreeNodeView nodeView = (TreeNodeView) view;
- TreeNode node = nodeView.getTreeElement();
- if (nodeView.getChildrenViews().size() == 0) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- if (nodeView.getChildrenViews().size() == 1) {
- TreeTransitionView childView = nodeView.getChildrenViews().get(0);
- if (childView == stop) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- subCalcSpan(childView, stop);
- if (childView.getParentViews().size() > 1) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- nodeView.setSpan(childView.getSpan());
- }
- }
- }
- else {
- DisjointSets branches = node.findMergingBranches();
- List children = node.getChildren();
-
- if (node == children.get(0).getParents().get(0)) {
- reorderBranches(node, branches);
- }
-
- List> mergingSets = branches.getAllSets();
-
- double span = 0.0;
- for (Set mergeSet : mergingSets) {
- if (mergeSet.size() > 1) {
- TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet);
- TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint);
- double subSpan = 0.0;
- for (TreeTransition branch : mergeSet) {
- TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch);
- subCalcSpan(branchView, mergePointView);
- subSpan += branchView.getSpan();
- }
- subCalcSpan(mergePointView, stop);
- span += Math.max(mergePointView.getSpan(), subSpan);
- }
- else {
- TreeTransition trans = mergeSet.iterator().next();
- TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
- subCalcSpan(transView, stop);
- span += transView.getSpan();
- }
- }
-
- nodeView.setSpan(span);
- }
- }
- }
- else {
- TreeTransitionView transView = (TreeTransitionView) view;
- TreeNodeView nodeView = transView.getChildView();
- if (nodeView == null || nodeView == stop) {
- transView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- calcSpan(nodeView);
- transView.setSpan(nodeView.getSpan());
- }
- }
- }
-
- /**
- * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in
- * relative order in the list of child transitions of the specified node
- *
- * @param node root node of the branches
- * @param branches DisjointSets of the child branches of the specified node which determine which branches merge
- */
- private void reorderBranches(TreeNode node, DisjointSets branches) {
- List children = node.getChildren();
- List> mergingSets = branches.getAllSets();
-
- List> newOrder = new ArrayList<>();
- for (Set set : mergingSets) {
- List mergeBranch = new ArrayList<>();
- newOrder.add(mergeBranch);
- children.forEach(t -> {
- if (set.contains(t)) {
- mergeBranch.add(t);
- }
- });
- mergeBranch.sort((TreeTransition t1, TreeTransition t2) ->
- children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1);
- }
-
- newOrder.sort((List b1, List b2) -> {
- int low1 = -1;
- int low2 = -1;
- for (TreeTransition t1 : b1) {
- int curIndex = children.indexOf(t1);
- if (low1 == -1 || curIndex < low1) {
- low1 = curIndex;
- }
- }
- for (TreeTransition t1 : b2) {
- int curIndex = children.indexOf(t1);
- if (low1 == -1 || curIndex < low1) {
- low1 = curIndex;
- }
- }
- return low1 < low2 ? -1 : 1;
- });
-
- List newChildren = new ArrayList<>();
- newOrder.forEach(l -> newChildren.addAll(l));
- node.setChildren(newChildren);
- }
+package edu.rpi.legup.ui.proofeditorui.treeview;
+
+import edu.rpi.legup.app.GameBoardFacade;
+import edu.rpi.legup.controller.TreeController;
+import edu.rpi.legup.model.observer.ITreeListener;
+import edu.rpi.legup.model.tree.Tree;
+import edu.rpi.legup.model.tree.TreeElement;
+import edu.rpi.legup.model.tree.TreeNode;
+import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.ui.ScrollView;
+import edu.rpi.legup.utility.DisjointSets;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.*;
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static edu.rpi.legup.model.tree.TreeElementType.NODE;
+import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION;
+import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER;
+import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS;
+
+public class TreeView extends ScrollView implements ITreeListener {
+ private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName());
+
+ private static final int TRANS_GAP = 5;
+
+ private static final int NODE_GAP_WIDTH = 70;
+ private static final int NODE_GAP_HEIGHT = 15;
+
+ private static final int BORDER_GAP_HEIGHT = 20;
+ private static final int BORDER_GAP_WIDTH = 20;
+
+ private static final int BORDER_SPACING = 100;
+
+ private TreeNodeView nodeHover;
+
+ private ArrayList currentStateBoxes;
+ private Rectangle bounds = new Rectangle(0, 0, 0, 0);
+
+ private Tree tree;
+ private TreeNodeView rootNodeView;
+ private Map viewMap;
+ private Dimension dimension;
+
+ private TreeViewSelection selection;
+
+ public TreeView(TreeController treeController) {
+ super(treeController);
+ currentStateBoxes = new ArrayList<>();
+ setSize(dimension = new Dimension(100, 200));
+ setPreferredSize(new Dimension(640, 160));
+
+ viewMap = new HashMap<>();
+
+ selection = new TreeViewSelection();
+ }
+
+ public TreeViewSelection getSelection() {
+ return selection;
+ }
+
+ /**
+ * Gets the tree node puzzleElement that the mouse is hovering over
+ *
+ * @return tree node puzzleElement that the mouse is hovering over
+ */
+ public TreeNodeView getNodeHover() {
+ return nodeHover;
+ }
+
+ /**
+ * Sets the tree node puzzleElement that the mouse is hovering over
+ *
+ * @param nodeHover tree node puzzleElement the mouse is hovering over
+ */
+ public void setNodeHover(TreeNodeView nodeHover) {
+ this.nodeHover = nodeHover;
+ }
+
+ /**
+ * Gets the TreeElementView by the specified point or null if no view exists at the specified point
+ *
+ * @param point location to query for a view
+ * @return TreeElementView at the point specified, otherwise null
+ */
+ public TreeElementView getTreeElementView(Point point) {
+ return getTreeElementView(point, rootNodeView);
+ }
+
+ /**
+ * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or
+ * the view specified is null
+ *
+ * @param point location to query for a view
+ * @param elementView view to determine if the point is contained within it
+ * @return TreeElementView at the point specified, otherwise null
+ */
+ private TreeElementView getTreeElementView(Point point, TreeElementView elementView) {
+ if (elementView == null) {
+ return null;
+ }
+ else {
+ if (elementView.contains(point) && elementView.isVisible()) {
+ if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) {
+ return null;
+ }
+ return elementView;
+ }
+ else {
+ if (elementView.getType() == NODE) {
+ TreeNodeView nodeView = (TreeNodeView) elementView;
+ for (TreeTransitionView transitionView : nodeView.getChildrenViews()) {
+ TreeElementView view = getTreeElementView(point, transitionView);
+ if (view != null) {
+ return view;
+ }
+ }
+ }
+ else {
+ TreeTransitionView transitionView = (TreeTransitionView) elementView;
+ return getTreeElementView(point, transitionView.getChildView());
+ }
+ }
+ }
+ return null;
+ }
+
+ public void updateTreeView(Tree tree) {
+ this.tree = tree;
+ if (selection.getSelectedViews().size() == 0) {
+ selection.newSelection(new TreeNodeView(tree.getRootNode()));
+ }
+ repaint();
+ }
+
+ /**
+ * Sets the tree associated with this TreeView
+ *
+ * @param tree tree
+ */
+ public void setTree(Tree tree) {
+ this.tree = tree;
+ }
+
+ public void updateTreeSize() {
+ if (GameBoardFacade.getInstance().getTree() == null) {
+ return;
+ }
+ setSize(bounds.getSize());
+ }
+
+ public void reset() {
+ if (bounds.x != 0 || bounds.y != 0) {
+ updateTreeSize();
+ }
+ }
+
+ public void zoomFit() {
+ double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200);
+ double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120);
+ zoomTo(Math.min(fitWidth, fitHeight));
+ viewport.setViewPosition(new Point(0, viewport.getHeight() / 2));
+ }
+
+ /**
+ * Creates a customized viewport for the scroll pane
+ *
+ * @return viewport for the scroll pane
+ */
+ @Override
+ protected JViewport createViewport() {
+ return new JViewport() {
+ @Override
+ protected LayoutManager createLayoutManager() {
+ return new ViewportLayout() {
+ @Override
+ public void layoutContainer(Container parent) {
+ Point point = viewport.getViewPosition();
+ // determine the maximum x and y view positions
+ int mx = getCanvas().getWidth() - viewport.getWidth();
+ int my = getCanvas().getHeight() - viewport.getHeight();
+ // obey edge boundaries
+ if (point.x < 0) {
+ point.x = 0;
+ }
+ if (point.x > mx) {
+ point.x = mx;
+ }
+ if (point.y < 0) {
+ point.y = 0;
+ }
+ if (point.y > my) {
+ point.y = my;
+ }
+ // center margins
+ if (mx < 0) {
+ point.x = 0;
+ }
+ if (my < 0) {
+ point.y = my / 2;
+ }
+ viewport.setViewPosition(point);
+ }
+ };
+ }
+ };
+ }
+
+ public void draw(Graphics2D graphics2D) {
+ currentStateBoxes.clear();
+ Tree tree = GameBoardFacade.getInstance().getTree();
+ if (tree != null) {
+ //setSize(bounds.getDimension());
+ graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+ drawTree(graphics2D);
+
+ dimension.width += BORDER_SPACING;
+ setSize(dimension);
+// graphics2D.drawRect(0,0, dimension.width, dimension.height);
+
+ if (selection.getHover() != null) {
+ drawMouseOver(graphics2D);
+ }
+ }
+ }
+
+ public void zoomReset() {
+ zoomTo(1.0);
+ viewport.setViewPosition(new Point(0, 0));
+ }
+
+ private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) {
+ if (nodeView != null) {
+ nodeView.draw(graphics2D);
+ for (TreeTransitionView transitionView : nodeView.getChildrenViews()) {
+ transitionView.draw(graphics2D);
+ redrawTree(graphics2D, transitionView.getChildView());
+ }
+ }
+ }
+
+ public void removeTreeElement(TreeElementView view) {
+ if (view.getType() == NODE) {
+ TreeNodeView nodeView = (TreeNodeView) view;
+ nodeView.getParentView().setChildView(null);
+ }
+ else {
+ TreeTransitionView transitionView = (TreeTransitionView) view;
+ transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView));
+ }
+ }
+
+ /**
+ * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image
+ *
+ * @param g the graphics to use to draw
+ */
+ public void drawMouseOver(Graphics2D g) {
+ if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) {
+ TreeTransition transition = (TreeTransition) selection.getHover().treeElement;
+ int imgWidth = 100;
+ int imgHeight = 100;
+
+ BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB);
+ image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null);
+ Point mousePoint = selection.getMousePoint();
+ g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null);
+ }
+ }
+
+ public void resetView() {
+ this.tree = null;
+ this.rootNodeView = null;
+ this.selection.clearSelection();
+ this.selection.clearHover();
+ }
+
+ /**
+ * Called when a tree puzzleElement is added to the tree
+ *
+ * @param treeElement TreeElement that was added to the tree
+ */
+ @Override
+ public void onTreeElementAdded(TreeElement treeElement) {
+ if (treeElement.getType() == NODE) {
+ addTreeNode((TreeNode) treeElement);
+ }
+ else {
+ addTreeTransition((TreeTransition) treeElement);
+ }
+ repaint();
+ }
+
+ /**
+ * Called when a tree puzzleElement is removed from the tree
+ *
+ * @param element TreeElement that was removed to the tree
+ */
+ @Override
+ public void onTreeElementRemoved(TreeElement element) {
+ if (element.getType() == NODE) {
+ TreeNode node = (TreeNode) element;
+ TreeNodeView nodeView = (TreeNodeView) viewMap.get(node);
+
+ nodeView.getParentView().setChildView(null);
+ removeTreeNode(node);
+ }
+ else {
+ TreeTransition trans = (TreeTransition) element;
+ TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
+
+ transView.getParentViews().forEach(n -> n.removeChildrenView(transView));
+ removeTreeTransition(trans);
+ }
+ repaint();
+ }
+
+ /**
+ * Called when the tree selection was changed
+ *
+ * @param selection tree selection that was changed
+ */
+ @Override
+ public void onTreeSelectionChanged(TreeViewSelection selection) {
+ this.selection.getSelectedViews().forEach(v -> v.setSelected(false));
+ selection.getSelectedViews().forEach(v -> v.setSelected(true));
+ this.selection = selection;
+ repaint();
+ }
+
+ /**
+ * Called when the model has finished updating the tree.
+ */
+ @Override
+ public void onUpdateTree() {
+ repaint();
+ }
+
+ /**
+ * Gets the TreeElementView by the corresponding TreeElement associated with it
+ *
+ * @param element TreeElement of the view
+ * @return TreeElementView of the TreeElement associated with it
+ */
+ public TreeElementView getElementView(TreeElement element) {
+ return viewMap.get(element);
+ }
+
+ private void removeTreeNode(TreeNode node) {
+ viewMap.remove(node);
+ node.getChildren().forEach(t -> removeTreeTransition(t));
+ }
+
+ private void removeTreeTransition(TreeTransition trans) {
+ viewMap.remove(trans);
+ if (trans.getChildNode() != null) {
+ removeTreeNode(trans.getChildNode());
+ }
+ }
+
+ private void addTreeNode(TreeNode node) {
+ TreeTransition parent = node.getParent();
+
+ TreeNodeView nodeView = new TreeNodeView(node);
+ TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent);
+
+ nodeView.setParentView(parentView);
+ parentView.setChildView(nodeView);
+
+ viewMap.put(node, nodeView);
+
+ if (!node.getChildren().isEmpty()) {
+ node.getChildren().forEach(t -> addTreeTransition(t));
+ }
+ }
+
+ private void addTreeTransition(TreeTransition trans) {
+ List parents = trans.getParents();
+
+ TreeTransitionView transView = new TreeTransitionView(trans);
+ for (TreeNode parent : parents) {
+ TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent);
+ transView.addParentView(parentNodeView);
+ parentNodeView.addChildrenView(transView);
+ }
+
+ viewMap.put(trans, transView);
+
+ if (trans.getChildNode() != null) {
+ addTreeNode(trans.getChildNode());
+ }
+ }
+
+ ///New Draw Methods
+
+ public void drawTree(Graphics2D graphics2D) {
+ if (tree == null) {
+ LOGGER.error("Unable to draw tree.");
+ }
+ else {
+ if (rootNodeView == null) {
+ rootNodeView = new TreeNodeView(tree.getRootNode());
+
+ LOGGER.debug("Creating new views for tree view.");
+ createViews(rootNodeView);
+
+ selection.newSelection(rootNodeView);
+ }
+
+ dimension = new Dimension(0, 0);
+ calcSpan(rootNodeView);
+ rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING);
+
+ calculateViewLocations(rootNodeView, 0);
+ dimension.height = (int) rootNodeView.getSpan();
+
+ redrawTree(graphics2D, rootNodeView);
+ LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height);
+ }
+ }
+
+ public void createViews(TreeNodeView nodeView) {
+ if (nodeView != null) {
+ viewMap.put(nodeView.getTreeElement(), nodeView);
+
+ TreeNode node = nodeView.getTreeElement();
+ for (TreeTransition trans : node.getChildren()) {
+ TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
+ if (transView != null) {
+ nodeView.addChildrenView(transView);
+ transView.addParentView(nodeView);
+ break;
+ }
+ transView = new TreeTransitionView(trans);
+
+ viewMap.put(transView.getTreeElement(), transView);
+
+ transView.addParentView(nodeView);
+ nodeView.addChildrenView(transView);
+
+ TreeNode childNode = trans.getChildNode();
+ if (childNode != null) {
+ TreeNodeView childNodeView = new TreeNodeView(childNode);
+ viewMap.put(childNodeView.getTreeElement(), childNodeView);
+
+ childNodeView.setParentView(transView);
+ transView.setChildView(childNodeView);
+
+ createViews(childNodeView);
+ }
+ }
+ }
+ }
+
+ public void calculateViewLocations(TreeNodeView nodeView, int depth) {
+ nodeView.setDepth(depth);
+ int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER;
+ nodeView.setX(xLoc);
+ dimension.width = Math.max(dimension.width, xLoc);
+
+ TreeTransitionView parentTransView = nodeView.getParentView();
+ int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY();
+ nodeView.setY(yLoc);
+
+ ArrayList children = nodeView.getChildrenViews();
+ switch (children.size()) {
+ case 0:
+ break;
+ case 1: {
+ TreeTransitionView childView = children.get(0);
+
+ List parentsViews = childView.getParentViews();
+ if (parentsViews.size() == 1) {
+ childView.setEndY(yLoc);
+
+ childView.setDepth(depth);
+
+ Point lineStartPoint = childView.getLineStartPoint(0);
+ lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2;
+ lineStartPoint.y = yLoc;
+ childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
+
+ dimension.width = Math.max(dimension.width, childView.getEndX());
+
+ TreeNodeView childNodeView = childView.getChildView();
+ if (childNodeView != null) {
+ calculateViewLocations(childNodeView, depth + 1);
+ }
+ }
+ else {
+ if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) {
+ int yAvg = 0;
+ for (int i = 0; i < parentsViews.size(); i++) {
+ TreeNodeView parentNodeView = parentsViews.get(i);
+ depth = Math.max(depth, parentNodeView.getDepth());
+ yAvg += parentNodeView.getY();
+
+ Point lineStartPoint = childView.getLineStartPoint(i);
+ lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2;
+ lineStartPoint.y = parentNodeView.getY();
+ }
+ yAvg /= parentsViews.size();
+ childView.setEndY(yAvg);
+
+ childView.setDepth(depth);
+
+ childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
+
+ dimension.width = Math.max(dimension.width, childView.getEndX());
+
+ TreeNodeView childNodeView = childView.getChildView();
+ if (childNodeView != null) {
+ calculateViewLocations(childNodeView, depth + 1);
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ int span = 0;
+ for (TreeTransitionView childView : children) {
+ span += childView.getSpan();
+ }
+
+ span = (int) ((nodeView.getSpan() - span) / 2);
+ for (int i = 0; i < children.size(); i++) {
+ TreeTransitionView childView = children.get(i);
+
+ childView.setDepth(depth);
+
+ Point lineStartPoint = childView.getLineStartPoint(0);
+ lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2;
+ lineStartPoint.y = yLoc;
+ childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
+ childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2));
+
+ span += childView.getSpan();
+ TreeNodeView childNodeView = childView.getChildView();
+ if (childNodeView != null) {
+ calculateViewLocations(childNodeView, depth + 1);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ public void calcSpan(TreeElementView view) {
+ if (view.getType() == NODE) {
+ TreeNodeView nodeView = (TreeNodeView) view;
+ TreeNode node = nodeView.getTreeElement();
+ if (nodeView.getChildrenViews().size() == 0) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ if (nodeView.getChildrenViews().size() == 1) {
+ TreeTransitionView childView = nodeView.getChildrenViews().get(0);
+ calcSpan(childView);
+ if (childView.getParentViews().size() > 1) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ nodeView.setSpan(childView.getSpan());
+ }
+ }
+ else {
+ DisjointSets branches = node.findMergingBranches();
+ List children = node.getChildren();
+
+ if (node == children.get(0).getParents().get(0)) {
+ reorderBranches(node, branches);
+ ArrayList newChildrenViews = new ArrayList<>();
+ for (TreeTransition trans : node.getChildren()) {
+ newChildrenViews.add((TreeTransitionView) viewMap.get(trans));
+ }
+ nodeView.setChildrenViews(newChildrenViews);
+ }
+
+ List> mergingSets = branches.getAllSets();
+
+ double span = 0.0;
+ for (Set mergeSet : mergingSets) {
+ if (mergeSet.size() > 1) {
+ TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet);
+ TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint);
+ double subSpan = 0.0;
+ for (TreeTransition branch : mergeSet) {
+ TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch);
+ subCalcSpan(branchView, mergePointView);
+ subSpan += branchView.getSpan();
+ }
+ calcSpan(mergePointView);
+ span += Math.max(mergePointView.getSpan(), subSpan);
+ }
+ else {
+ TreeTransition trans = mergeSet.iterator().next();
+ TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
+ calcSpan(transView);
+ span += transView.getSpan();
+ }
+ }
+ nodeView.setSpan(span);
+ }
+ }
+ }
+ else {
+ TreeTransitionView transView = (TreeTransitionView) view;
+ TreeNodeView nodeView = transView.getChildView();
+ if (nodeView == null) {
+ transView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ calcSpan(nodeView);
+ transView.setSpan(nodeView.getSpan());
+ }
+ }
+ }
+
+ /**
+ * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view
+ * specified as stop. Stop tree puzzleElement is NOT included in the span calculation
+ *
+ * @param view
+ * @param stop
+ */
+ private void subCalcSpan(TreeElementView view, TreeElementView stop) {
+ //safe-guard for infinite loop
+ if (view == stop) {
+ return;
+ }
+
+ if (view.getType() == NODE) {
+ TreeNodeView nodeView = (TreeNodeView) view;
+ TreeNode node = nodeView.getTreeElement();
+ if (nodeView.getChildrenViews().size() == 0) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ if (nodeView.getChildrenViews().size() == 1) {
+ TreeTransitionView childView = nodeView.getChildrenViews().get(0);
+ if (childView == stop) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ subCalcSpan(childView, stop);
+ if (childView.getParentViews().size() > 1) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ nodeView.setSpan(childView.getSpan());
+ }
+ }
+ }
+ else {
+ DisjointSets branches = node.findMergingBranches();
+ List children = node.getChildren();
+
+ if (node == children.get(0).getParents().get(0)) {
+ reorderBranches(node, branches);
+ }
+
+ List> mergingSets = branches.getAllSets();
+
+ double span = 0.0;
+ for (Set mergeSet : mergingSets) {
+ if (mergeSet.size() > 1) {
+ TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet);
+ TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint);
+ double subSpan = 0.0;
+ for (TreeTransition branch : mergeSet) {
+ TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch);
+ subCalcSpan(branchView, mergePointView);
+ subSpan += branchView.getSpan();
+ }
+ subCalcSpan(mergePointView, stop);
+ span += Math.max(mergePointView.getSpan(), subSpan);
+ }
+ else {
+ TreeTransition trans = mergeSet.iterator().next();
+ TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
+ subCalcSpan(transView, stop);
+ span += transView.getSpan();
+ }
+ }
+
+ nodeView.setSpan(span);
+ }
+ }
+ }
+ else {
+ TreeTransitionView transView = (TreeTransitionView) view;
+ TreeNodeView nodeView = transView.getChildView();
+ if (nodeView == null || nodeView == stop) {
+ transView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ calcSpan(nodeView);
+ transView.setSpan(nodeView.getSpan());
+ }
+ }
+ }
+
+ /**
+ * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in
+ * relative order in the list of child transitions of the specified node
+ *
+ * @param node root node of the branches
+ * @param branches DisjointSets of the child branches of the specified node which determine which branches merge
+ */
+ private void reorderBranches(TreeNode node, DisjointSets branches) {
+ List children = node.getChildren();
+ List> mergingSets = branches.getAllSets();
+
+ List> newOrder = new ArrayList<>();
+ for (Set set : mergingSets) {
+ List mergeBranch = new ArrayList<>();
+ newOrder.add(mergeBranch);
+ children.forEach(t -> {
+ if (set.contains(t)) {
+ mergeBranch.add(t);
+ }
+ });
+ mergeBranch.sort((TreeTransition t1, TreeTransition t2) ->
+ children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1);
+ }
+
+ newOrder.sort((List b1, List b2) -> {
+ int low1 = -1;
+ int low2 = -1;
+ for (TreeTransition t1 : b1) {
+ int curIndex = children.indexOf(t1);
+ if (low1 == -1 || curIndex < low1) {
+ low1 = curIndex;
+ }
+ }
+ for (TreeTransition t1 : b2) {
+ int curIndex = children.indexOf(t1);
+ if (low1 == -1 || curIndex < low1) {
+ low1 = curIndex;
+ }
+ }
+ return low1 < low2 ? -1 : 1;
+ });
+
+ List newChildren = new ArrayList<>();
+ newOrder.forEach(l -> newChildren.addAll(l));
+ node.setChildren(newChildren);
+ }
}
\ No newline at end of file