/* This file is part of Eternity II Editor. * * Eternity II Editor 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 3 of the License, or * (at your option) any later version. * * Eternity II Editor 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 Eternity II Editor. If not, see <http://www.gnu.org/licenses/>. * * Eternity II Editor project is hosted on SourceForge: * http://sourceforge.net/projects/eternityii/ * and maintained by Yannick Kirschhoffer <alcibiade@alcibiade.org> */ package org.alcibiade.eternity.editor.model; import java.io.Serializable; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; public class QuadModel implements Cloneable, Serializable { private static final long serialVersionUID = 1L; public static final int DIR_NORTH = 0; public static final int DIR_EAST = 1; public static final int DIR_SOUTH = 2; public static final int DIR_WEST = 3; private Pattern[] patterns; private Set<QuadObserver> observers = new HashSet<QuadObserver>(); private Pattern defaultpat; private boolean locked = false; private boolean readOnly = false; public QuadModel() { defaultpat = Pattern.getDefaultPattern(); patterns = new Pattern[4]; patterns[0] = defaultpat; patterns[1] = defaultpat; patterns[2] = defaultpat; patterns[3] = defaultpat; } public QuadModel(Pattern... initialPatterns) { defaultpat = Pattern.getDefaultPattern(); patterns = new Pattern[4]; patterns[0] = initialPatterns[0]; patterns[1] = initialPatterns[1]; patterns[2] = initialPatterns[2]; patterns[3] = initialPatterns[3]; } @Override public QuadModel clone() { QuadModel newQuad = new QuadModel(); copyTo(newQuad); return newQuad; } public void copyTo(QuadModel q) { copyTo(q, true); } protected void copyTo(QuadModel q, boolean notifyObservers) { assert !q.readOnly; q.patterns[DIR_NORTH] = patterns[DIR_NORTH]; q.patterns[DIR_EAST] = patterns[DIR_EAST]; q.patterns[DIR_SOUTH] = patterns[DIR_SOUTH]; q.patterns[DIR_WEST] = patterns[DIR_WEST]; q.locked = isLocked(); if (notifyObservers) { q.notifyQuadUpdated(); } } public boolean isClear() { return patterns[DIR_NORTH] == defaultpat && patterns[DIR_EAST] == defaultpat && patterns[DIR_SOUTH] == defaultpat && patterns[DIR_WEST] == defaultpat; } public boolean isNull() { return patterns[DIR_NORTH] == null && patterns[DIR_EAST] == null && patterns[DIR_SOUTH] == null && patterns[DIR_WEST] == null; } public int countPattern(Pattern pattern) { int result = 0; if (patterns[0] == pattern) { result++; } if (patterns[1] == pattern) { result++; } if (patterns[2] == pattern) { result++; } if (patterns[3] == pattern) { result++; } return result; } public int countDefaultPattern() { return countPattern(defaultpat); } public boolean isLocked() { return locked; } public void setLocked(boolean l) { if (locked != l) { locked = l; notifyQuadUpdated(); } } public void clear() { assert !readOnly; patterns[DIR_NORTH] = defaultpat; patterns[DIR_SOUTH] = defaultpat; patterns[DIR_WEST] = defaultpat; patterns[DIR_EAST] = defaultpat; setLocked(false); notifyQuadUpdated(); } public Pattern getPattern(int direction) { return patterns[direction]; } // public Pattern getOppositePattern(int direction) { return patterns[(direction + 2) % 4]; } public void setPattern(int direction, Pattern pattern) { assert !readOnly; patterns[direction] = pattern; notifyQuadUpdated(); } public void swap(QuadModel other) { assert !readOnly; assert !other.readOnly; Pattern[] my_old_patterns = patterns; patterns = other.patterns; other.patterns = my_old_patterns; notifyQuadUpdated(); other.notifyQuadUpdated(); } public void rotatePattern(int direction) { assert !readOnly; rotatePattern(direction, true); } public void rotatePattern(int direction, boolean forward) { assert !readOnly; List<Pattern> allpats = Pattern.getAllPatterns(); Pattern pat = getPattern(direction); int patix = allpats.indexOf(pat); int offset = 1; if (!forward) { offset = -1; } setPattern(direction, allpats.get((patix + offset + allpats.size()) % allpats.size())); } public void rotateClockwise() { assert !readOnly; // Rotate array to the right Pattern buf = patterns[0]; patterns[0] = patterns[3]; patterns[3] = patterns[2]; patterns[2] = patterns[1]; patterns[1] = buf; notifyQuadUpdated(); } public void rotateClockwise(int steps) { assert !readOnly; int stepsLeft = steps; while (stepsLeft-- > 0) { rotateClockwise(); } } public void rotateCounterclockwise() { assert !readOnly; // Rotate array to the left Pattern buf = patterns[0]; patterns[0] = patterns[1]; patterns[1] = patterns[2]; patterns[2] = patterns[3]; patterns[3] = buf; notifyQuadUpdated(); } public void rotateCounterclockwise(int steps) { assert !readOnly; int stepsLeft = steps; while (stepsLeft-- > 0) { rotateCounterclockwise(); } } public void addQuadObserver(QuadObserver observer) { observers.add(observer); } public void removeQuadObserver(QuadObserver observer) { observers.remove(observer); } private void notifyQuadUpdated() { if (observers.size() > 0) { for (QuadObserver observer : observers) { observer.quadUpdated(); } } } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object o) { boolean result = false; if (o instanceof QuadModel) { QuadModel oQuad = (QuadModel) o; result = locked == oQuad.locked; result = result && Arrays.deepEquals(patterns, oQuad.patterns); } else { result = false; } return result; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return Arrays.hashCode(patterns); } /** * Compute the maximum number of matching sides. * * @param quad * The quad to compare * @return the number of matches. */ public int matchDegrees(QuadModel quad) { int matchDegrees = 0; for (int rot = 0; rot < 4; rot++) { int degrees = 0; for (int a = 0; a < 4; a++) { Pattern pat1 = patterns[(a + rot) % 4]; Pattern pat2 = quad.patterns[a]; if (pat1 == null || pat2 == null || pat1 == pat2) { degrees += 1; } } matchDegrees = Math.max(matchDegrees, degrees); } return matchDegrees; } public boolean matches(QuadModel quad) { boolean result = true; for (int a = 0; result && a < 4; a++) { Pattern pat1 = patterns[a]; Pattern pat2 = quad.patterns[a]; if (pat1 != null && pat2 != null && pat1 != pat2) { result = false; } } return result; } public boolean equalsIgnoreRotation(QuadModel quad) { return equalsIgnoreRotation(quad, 4); } public boolean equalsIgnoreRotation(QuadModel quad, int degrees) { return matchDegrees(quad) >= degrees; } // public boolean geneticEquals(QuadModel quad) { QuadModel compare = this.clone(); for(int d = 0; d < 4; d++) { if(compare.getPattern(0) == quad.getPattern(0) && compare.getPattern(1) == quad.getPattern(1) && compare.getPattern(2) == quad.getPattern(2) && compare.getPattern(3) == quad.getPattern(3)) { return true; } compare.rotateClockwise(); } return false; } @Override public String toString() { return "Q[" + getPattern(0) + "," + getPattern(1) + "," + getPattern(2) + "," + getPattern(3) + "]"; } public void flipHorizontal() { assert !readOnly; Pattern w = patterns[DIR_WEST]; patterns[DIR_WEST] = patterns[DIR_EAST]; patterns[DIR_EAST] = w; } public void flipVertical() { assert !readOnly; Pattern n = patterns[DIR_NORTH]; patterns[DIR_NORTH] = patterns[DIR_SOUTH]; patterns[DIR_SOUTH] = n; } public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } public String save() { return String.format("%d %d %d %d", getPattern(0).getCode(), getPattern(1).getCode(), getPattern(2).getCode(), getPattern(3).getCode()); } public void load(String text) { String[] patternStrings = text.split(" "); for (int i = 0; i < 4; i++) { int patternCode = Integer.parseInt(patternStrings[i]); Pattern pattern = Pattern.getPatternByCode(patternCode); setPattern(i, pattern); } } }