/*
* RoomController.java 20 nov. 2008
*
* Sweet Home 3D, Copyright (c) 2008 Emmanuel PUYBARET / eTeks <info@eteks.com>
*
* 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 com.eteks.sweethome3d.viewcontroller;
import java.awt.BasicStroke;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoableEdit;
import javax.swing.undo.UndoableEditSupport;
import com.eteks.sweethome3d.model.Home;
import com.eteks.sweethome3d.model.HomeTexture;
import com.eteks.sweethome3d.model.Room;
import com.eteks.sweethome3d.model.Selectable;
import com.eteks.sweethome3d.model.UserPreferences;
import com.eteks.sweethome3d.model.Wall;
/**
* A MVC controller for room view.
* @author Emmanuel Puybaret
*/
public class RoomController implements Controller {
/**
* The properties that may be edited by the view associated to this controller.
*/
public enum Property {NAME, AREA_VISIBLE, FLOOR_VISIBLE, FLOOR_COLOR, FLOOR_PAINT, FLOOR_SHININESS,
CEILING_VISIBLE, CEILING_COLOR, CEILING_PAINT, CEILING_SHININESS,
SPLIT_SURROUNDING_WALLS, WALL_SIDES_COLOR, WALL_SIDES_PAINT, WALL_SIDES_SHININESS}
/**
* The possible values for {@linkplain #getFloorPaint() room paint type}.
*/
public enum RoomPaint {COLORED, TEXTURED}
private final Home home;
private final UserPreferences preferences;
private final ViewFactory viewFactory;
private final ContentManager contentManager;
private final UndoableEditSupport undoSupport;
private TextureChoiceController floorTextureController;
private TextureChoiceController ceilingTextureController;
private TextureChoiceController wallSidesTextureController;
private final PropertyChangeSupport propertyChangeSupport;
private DialogView roomView;
private String name;
private Boolean areaVisible;
private Boolean floorVisible;
private Integer floorColor;
private RoomPaint floorPaint;
private Float floorShininess;
private Boolean ceilingVisible;
private Integer ceilingColor;
private RoomPaint ceilingPaint;
private Float ceilingShininess;
private boolean wallSidesEditable;
private boolean splitSurroundingWalls;
private boolean splitSurroundingWallsNeeded;
private Integer wallSidesColor;
private RoomPaint wallSidesPaint;
private Float wallSidesShininess;
/**
* Creates the controller of room view with undo support.
*/
public RoomController(final Home home,
UserPreferences preferences,
ViewFactory viewFactory,
ContentManager contentManager,
UndoableEditSupport undoSupport) {
this.home = home;
this.preferences = preferences;
this.viewFactory = viewFactory;
this.contentManager = contentManager;
this.undoSupport = undoSupport;
this.propertyChangeSupport = new PropertyChangeSupport(this);
updateProperties();
}
/**
* Returns the texture controller of the room floor.
*/
public TextureChoiceController getFloorTextureController() {
// Create sub controller lazily only once it's needed
if (this.floorTextureController == null) {
this.floorTextureController = new TextureChoiceController(
this.preferences.getLocalizedString(RoomController.class, "floorTextureTitle"),
this.preferences, this.viewFactory, this.contentManager);
this.floorTextureController.addPropertyChangeListener(TextureChoiceController.Property.TEXTURE,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
setFloorPaint(RoomPaint.TEXTURED);
}
});
}
return this.floorTextureController;
}
/**
* Returns the texture controller of the room ceiling.
*/
public TextureChoiceController getCeilingTextureController() {
// Create sub controller lazily only once it's needed
if (this.ceilingTextureController == null) {
this.ceilingTextureController = new TextureChoiceController(
this.preferences.getLocalizedString(RoomController.class, "ceilingTextureTitle"),
this.preferences, this.viewFactory, this.contentManager);
this.ceilingTextureController.addPropertyChangeListener(TextureChoiceController.Property.TEXTURE,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
setCeilingPaint(RoomPaint.TEXTURED);
}
});
}
return this.ceilingTextureController;
}
/**
* Returns the texture controller of the room wall sides.
*/
public TextureChoiceController getWallSidesTextureController() {
// Create sub controller lazily only once it's needed
if (this.wallSidesTextureController == null) {
this.wallSidesTextureController = new TextureChoiceController(
this.preferences.getLocalizedString(RoomController.class, "wallSidesTextureTitle"),
this.preferences, this.viewFactory, this.contentManager);
this.wallSidesTextureController.addPropertyChangeListener(TextureChoiceController.Property.TEXTURE,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
setWallSidesPaint(RoomPaint.TEXTURED);
}
});
}
return this.wallSidesTextureController;
}
/**
* Returns the view associated with this controller.
*/
public DialogView getView() {
// Create view lazily only once it's needed
if (this.roomView == null) {
this.roomView = this.viewFactory.createRoomView(this.preferences, this);
}
return this.roomView;
}
/**
* Displays the view controlled by this controller.
*/
public void displayView(View parentView) {
getView().displayView(parentView);
}
/**
* Adds the property change <code>listener</code> in parameter to this controller.
*/
public void addPropertyChangeListener(Property property, PropertyChangeListener listener) {
this.propertyChangeSupport.addPropertyChangeListener(property.name(), listener);
}
/**
* Removes the property change <code>listener</code> in parameter from this controller.
*/
public void removePropertyChangeListener(Property property, PropertyChangeListener listener) {
this.propertyChangeSupport.removePropertyChangeListener(property.name(), listener);
}
/**
* Returns <code>true</code> if the given <code>property</code> is editable.
* Depending on whether a property is editable or not, the view associated to this controller
* may render it differently.
* The implementation of this method always returns <code>true</code> except for <code>WALLS</code> properties.
*/
public boolean isPropertyEditable(Property property) {
switch (property) {
case SPLIT_SURROUNDING_WALLS :
case WALL_SIDES_COLOR :
case WALL_SIDES_PAINT :
case WALL_SIDES_SHININESS :
return this.wallSidesEditable;
default :
return true;
}
}
/**
* Updates edited properties from selected rooms in the home edited by this controller.
*/
protected void updateProperties() {
List<Room> selectedRooms = Home.getRoomsSubList(this.home.getSelectedItems());
if (selectedRooms.isEmpty()) {
setAreaVisible(null); // Nothing to edit
setFloorColor(null);
getFloorTextureController().setTexture(null);
setFloorPaint(null);
setFloorShininess(null);
setCeilingColor(null);
getCeilingTextureController().setTexture(null);
setCeilingPaint(null);
setCeilingShininess(null);
} else {
// Search the common properties among selected rooms
Room firstRoom = selectedRooms.get(0);
String name = firstRoom.getName();
if (name != null) {
for (int i = 1; i < selectedRooms.size(); i++) {
if (!name.equals(selectedRooms.get(i).getName())) {
name = null;
break;
}
}
}
setName(name);
// Search the common areaVisible value among rooms
Boolean areaVisible = firstRoom.isAreaVisible();
for (int i = 1; i < selectedRooms.size(); i++) {
if (areaVisible != selectedRooms.get(i).isAreaVisible()) {
areaVisible = null;
break;
}
}
setAreaVisible(areaVisible);
// Search the common floorVisible value among rooms
Boolean floorVisible = firstRoom.isFloorVisible();
for (int i = 1; i < selectedRooms.size(); i++) {
if (floorVisible != selectedRooms.get(i).isFloorVisible()) {
floorVisible = null;
break;
}
}
setFloorVisible(floorVisible);
// Search the common floor color among rooms
Integer floorColor = firstRoom.getFloorColor();
if (floorColor != null) {
for (int i = 1; i < selectedRooms.size(); i++) {
if (!floorColor.equals(selectedRooms.get(i).getFloorColor())) {
floorColor = null;
break;
}
}
}
setFloorColor(floorColor);
// Search the common floor texture among rooms
HomeTexture floorTexture = firstRoom.getFloorTexture();
if (floorTexture != null) {
for (int i = 1; i < selectedRooms.size(); i++) {
if (!floorTexture.equals(selectedRooms.get(i).getFloorTexture())) {
floorTexture = null;
break;
}
}
}
getFloorTextureController().setTexture(floorTexture);
if (floorColor != null) {
setFloorPaint(RoomPaint.COLORED);
} else if (floorTexture != null) {
setFloorPaint(RoomPaint.TEXTURED);
} else {
setFloorPaint(null);
}
// Search the common floor shininess among rooms
Float floorShininess = firstRoom.getFloorShininess();
for (int i = 1; i < selectedRooms.size(); i++) {
if (!floorShininess.equals(selectedRooms.get(i).getFloorShininess())) {
floorShininess = null;
break;
}
}
setFloorShininess(floorShininess);
// Search the common ceilingVisible value among rooms
Boolean ceilingVisible = firstRoom.isCeilingVisible();
for (int i = 1; i < selectedRooms.size(); i++) {
if (ceilingVisible != selectedRooms.get(i).isCeilingVisible()) {
ceilingVisible = null;
break;
}
}
setCeilingVisible(ceilingVisible);
// Search the common ceiling color among rooms
Integer ceilingColor = firstRoom.getCeilingColor();
if (ceilingColor != null) {
for (int i = 1; i < selectedRooms.size(); i++) {
if (!ceilingColor.equals(selectedRooms.get(i).getCeilingColor())) {
ceilingColor = null;
break;
}
}
}
setCeilingColor(ceilingColor);
// Search the common ceiling texture among rooms
HomeTexture ceilingTexture = firstRoom.getCeilingTexture();
if (ceilingTexture != null) {
for (int i = 1; i < selectedRooms.size(); i++) {
if (!ceilingTexture.equals(selectedRooms.get(i).getCeilingTexture())) {
ceilingTexture = null;
break;
}
}
}
getCeilingTextureController().setTexture(ceilingTexture);
if (ceilingColor != null) {
setCeilingPaint(RoomPaint.COLORED);
} else if (ceilingTexture != null) {
setCeilingPaint(RoomPaint.TEXTURED);
} else {
setCeilingPaint(null);
}
// Search the common ceiling shininess among rooms
Float ceilingShininess = firstRoom.getCeilingShininess();
for (int i = 1; i < selectedRooms.size(); i++) {
if (!ceilingShininess.equals(selectedRooms.get(i).getCeilingShininess())) {
ceilingShininess = null;
break;
}
}
setCeilingShininess(ceilingShininess);
}
List<WallSide> wallSides = getRoomsWallSides(selectedRooms, null);
if (wallSides.isEmpty()) {
this.wallSidesEditable =
this.splitSurroundingWallsNeeded =
this.splitSurroundingWalls = false;
setWallSidesColor(null);
setWallSidesPaint(null);
setWallSidesShininess(null);
} else {
this.wallSidesEditable = true;
this.splitSurroundingWallsNeeded = splitWalls(wallSides, null, null, null);
this.splitSurroundingWalls = false;
WallSide firstWallSide = wallSides.get(0);
// Search the common wall color among wall sides
Integer wallSidesColor = firstWallSide.getSide() == WallSide.LEFT_SIDE
? firstWallSide.getWall().getLeftSideColor()
: firstWallSide.getWall().getRightSideColor();
if (wallSidesColor != null) {
for (int i = 1; i < wallSides.size(); i++) {
WallSide wallSide = wallSides.get(i);
if (!wallSidesColor.equals(wallSide.getSide() == WallSide.LEFT_SIDE
? wallSide.getWall().getLeftSideColor()
: wallSide.getWall().getRightSideColor())) {
wallSidesColor = null;
break;
}
}
}
setWallSidesColor(wallSidesColor);
// Search the common wall texture among wall sides
HomeTexture wallSidesTexture = firstWallSide.getSide() == WallSide.LEFT_SIDE
? firstWallSide.getWall().getLeftSideTexture()
: firstWallSide.getWall().getRightSideTexture();
if (wallSidesTexture != null) {
for (int i = 1; i < wallSides.size(); i++) {
WallSide wallSide = wallSides.get(i);
if (!wallSidesTexture.equals(wallSide.getSide() == WallSide.LEFT_SIDE
? wallSide.getWall().getLeftSideTexture()
: wallSide.getWall().getRightSideTexture())) {
wallSidesTexture = null;
break;
}
}
}
getWallSidesTextureController().setTexture(wallSidesTexture);
// Search the common floor shininess among rooms
Float wallSidesShininess = firstWallSide.getSide() == WallSide.LEFT_SIDE
? firstWallSide.getWall().getLeftSideShininess()
: firstWallSide.getWall().getRightSideShininess();
if (wallSidesShininess != null) {
for (int i = 1; i < wallSides.size(); i++) {
WallSide wallSide = wallSides.get(i);
if (!wallSidesShininess.equals(wallSide.getSide() == WallSide.LEFT_SIDE
? wallSide.getWall().getLeftSideShininess()
: wallSide.getWall().getRightSideShininess())) {
wallSidesShininess = null;
break;
}
}
}
setWallSidesShininess(wallSidesShininess);
}
}
/**
* Returns the wall sides close to each room of <code>rooms</code>.
*/
private List<WallSide> getRoomsWallSides(List<Room> rooms, List<WallSide> defaultWallSides) {
List<WallSide> wallSides = new ArrayList<WallSide>();
for (Room room : rooms) {
float [][] points = room.getPoints();
GeneralPath roomShape = new GeneralPath();
roomShape.moveTo(points [0][0], points [0][1]);
for (int i = 1; i < points.length; i++) {
roomShape.lineTo(points [i][0], points [i][1]);
}
roomShape.closePath();
Area roomArea = new Area(roomShape);
if (defaultWallSides != null) {
for (WallSide wallSide : defaultWallSides) {
if (isRoomItersectingWallSide(wallSide.getWall().getPoints(), wallSide.getSide(), roomArea)) {
wallSides.add(wallSide);
}
}
} else {
for (Wall wall : this.home.getWalls()) {
if (wall.isAtLevel(this.home.getSelectedLevel())) {
float [][] wallPoints = wall.getPoints();
if (isRoomItersectingWallSide(wallPoints, WallSide.LEFT_SIDE, roomArea)) {
wallSides.add(new WallSide(wall, WallSide.LEFT_SIDE));
}
if (isRoomItersectingWallSide(wallPoints, WallSide.RIGHT_SIDE, roomArea)) {
wallSides.add(new WallSide(wall, WallSide.RIGHT_SIDE));
}
}
}
}
}
return wallSides;
}
/**
* Returns <code>true</code> if the wall points on the given <code>wallSide</code>
* intersects room area.
*/
private boolean isRoomItersectingWallSide(float [][] wallPoints, int wallSide, Area roomArea) {
BasicStroke lineStroke = new BasicStroke(2);
Shape wallSideShape = getWallSideShape(wallPoints, wallSide);
Area wallSideTestArea = new Area(lineStroke.createStrokedShape(wallSideShape));
float wallSideTestAreaSurface = getSurface(wallSideTestArea);
wallSideTestArea.intersect(roomArea);
if (!wallSideTestArea.isEmpty()) {
float wallSideIntersectionSurface = getSurface(wallSideTestArea);
// Take into account only walls that shares a minimum surface with the room
if (wallSideIntersectionSurface > wallSideTestAreaSurface * 0.02f) {
return true;
}
}
return false;
}
/**
* Returns the shape of the side of the given <code>wall</code>.
*/
private Shape getWallSideShape(float [][] wallPoints, int wallSide) {
if (wallPoints.length == 4) {
if (wallSide == WallSide.LEFT_SIDE) {
return new Line2D.Float(wallPoints [0][0], wallPoints [0][1], wallPoints [1][0], wallPoints [1][1]);
} else {
return new Line2D.Float(wallPoints [2][0], wallPoints [2][1], wallPoints [3][0], wallPoints [3][1]);
}
} else {
float [][] wallSidePoints = new float [wallPoints.length / 2][];
System.arraycopy(wallPoints, wallSide == WallSide.LEFT_SIDE ? 0 : wallSidePoints.length,
wallSidePoints, 0, wallSidePoints.length);
return getPath(wallSidePoints, false);
}
}
/**
* Returns the shape matching the coordinates in <code>points</code> array.
*/
private GeneralPath getPath(float [][] points, boolean closedPath) {
GeneralPath path = new GeneralPath();
path.moveTo(points [0][0], points [0][1]);
for (int i = 1; i < points.length; i++) {
path.lineTo(points [i][0], points [i][1]);
}
if (closedPath) {
path.closePath();
}
return path;
}
/**
* Returns the surface of the given <code>area</code>.
*/
private float getSurface(Area area) {
// Add the surface of the different polygons of this room
float surface = 0;
List<float []> currentPathPoints = new ArrayList<float[]>();
for (PathIterator it = area.getPathIterator(null); !it.isDone(); ) {
float [] roomPoint = new float[2];
switch (it.currentSegment(roomPoint)) {
case PathIterator.SEG_MOVETO :
currentPathPoints.add(roomPoint);
break;
case PathIterator.SEG_LINETO :
currentPathPoints.add(roomPoint);
break;
case PathIterator.SEG_CLOSE :
float [][] pathPoints =
currentPathPoints.toArray(new float [currentPathPoints.size()][]);
surface += Math.abs(getSignedSurface(pathPoints));
currentPathPoints.clear();
break;
}
it.next();
}
return surface;
}
private float getSignedSurface(float areaPoints [][]) {
// From "Area of a General Polygon" algorithm described in
// http://www.davidchandler.com/AreaOfAGeneralPolygon.pdf
float area = 0;
for (int i = 1; i < areaPoints.length; i++) {
area += areaPoints [i][0] * areaPoints [i - 1][1];
area -= areaPoints [i][1] * areaPoints [i - 1][0];
}
area += areaPoints [0][0] * areaPoints [areaPoints.length - 1][1];
area -= areaPoints [0][1] * areaPoints [areaPoints.length - 1][0];
return area / 2;
}
/**
* Sets the edited name.
*/
public void setName(String name) {
if (name != this.name) {
String oldName = this.name;
this.name = name;
this.propertyChangeSupport.firePropertyChange(Property.NAME.name(), oldName, name);
}
}
/**
* Returns the edited name.
*/
public String getName() {
return this.name;
}
/**
* Sets whether room area is visible or not.
*/
public void setAreaVisible(Boolean areaVisible) {
if (areaVisible != this.areaVisible) {
Boolean oldAreaVisible = this.areaVisible;
this.areaVisible = areaVisible;
this.propertyChangeSupport.firePropertyChange(Property.AREA_VISIBLE.name(), oldAreaVisible, areaVisible);
}
}
/**
* Returns whether room area is visible or not.
*/
public Boolean getAreaVisible() {
return this.areaVisible;
}
/**
* Sets whether room floor is visible or not.
*/
public void setFloorVisible(Boolean floorVisible) {
if (floorVisible != this.floorVisible) {
Boolean oldFloorVisible = this.floorVisible;
this.floorVisible = floorVisible;
this.propertyChangeSupport.firePropertyChange(Property.FLOOR_VISIBLE.name(), oldFloorVisible, floorVisible);
}
}
/**
* Returns whether room floor is visible or not.
*/
public Boolean getFloorVisible() {
return this.floorVisible;
}
/**
* Sets the edited color of the floor.
*/
public void setFloorColor(Integer floorColor) {
if (floorColor != this.floorColor) {
Integer oldFloorColor = this.floorColor;
this.floorColor = floorColor;
this.propertyChangeSupport.firePropertyChange(Property.FLOOR_COLOR.name(), oldFloorColor, floorColor);
setFloorPaint(RoomPaint.COLORED);
}
}
/**
* Returns the edited color of the floor.
*/
public Integer getFloorColor() {
return this.floorColor;
}
/**
* Sets whether the floor is colored, textured or unknown painted.
*/
public void setFloorPaint(RoomPaint floorPaint) {
if (floorPaint != this.floorPaint) {
RoomPaint oldFloorPaint = this.floorPaint;
this.floorPaint = floorPaint;
this.propertyChangeSupport.firePropertyChange(Property.FLOOR_PAINT.name(), oldFloorPaint, floorPaint);
}
}
/**
* Returns whether the floor is colored, textured or unknown painted.
*/
public RoomPaint getFloorPaint() {
return this.floorPaint;
}
/**
* Sets the edited shininess of the floor.
*/
public void setFloorShininess(Float floorShininess) {
if (floorShininess != this.floorShininess) {
Float oldFloorShininess = this.floorShininess;
this.floorShininess = floorShininess;
this.propertyChangeSupport.firePropertyChange(Property.FLOOR_SHININESS.name(), oldFloorShininess, floorShininess);
}
}
/**
* Returns the edited shininess of the floor.
*/
public Float getFloorShininess() {
return this.floorShininess;
}
/**
* Sets whether room ceiling is visible or not.
*/
public void setCeilingVisible(Boolean ceilingCeilingVisible) {
if (ceilingCeilingVisible != this.ceilingVisible) {
Boolean oldCeilingVisible = this.ceilingVisible;
this.ceilingVisible = ceilingCeilingVisible;
this.propertyChangeSupport.firePropertyChange(Property.CEILING_VISIBLE.name(), oldCeilingVisible, ceilingCeilingVisible);
}
}
/**
* Returns whether room ceiling is ceilingCeilingVisible or not.
*/
public Boolean getCeilingVisible() {
return this.ceilingVisible;
}
/**
* Sets the edited color of the ceiling.
*/
public void setCeilingColor(Integer ceilingColor) {
if (ceilingColor != this.ceilingColor) {
Integer oldCeilingColor = this.ceilingColor;
this.ceilingColor = ceilingColor;
this.propertyChangeSupport.firePropertyChange(Property.CEILING_COLOR.name(), oldCeilingColor, ceilingColor);
setCeilingPaint(RoomPaint.COLORED);
}
}
/**
* Returns the edited color of the ceiling.
*/
public Integer getCeilingColor() {
return this.ceilingColor;
}
/**
* Sets whether the ceiling is colored, textured or unknown painted.
*/
public void setCeilingPaint(RoomPaint ceilingPaint) {
if (ceilingPaint != this.ceilingPaint) {
RoomPaint oldCeilingPaint = this.ceilingPaint;
this.ceilingPaint = ceilingPaint;
this.propertyChangeSupport.firePropertyChange(Property.CEILING_PAINT.name(), oldCeilingPaint, ceilingPaint);
}
}
/**
* Returns whether the ceiling is colored, textured or unknown painted.
*/
public RoomPaint getCeilingPaint() {
return this.ceilingPaint;
}
/**
* Sets the edited shininess of the ceiling.
*/
public void setCeilingShininess(Float ceilingShininess) {
if (ceilingShininess != this.ceilingShininess) {
Float oldCeilingShininess = this.ceilingShininess;
this.ceilingShininess = ceilingShininess;
this.propertyChangeSupport.firePropertyChange(Property.CEILING_SHININESS.name(), oldCeilingShininess, ceilingShininess);
}
}
/**
* Returns the edited shininess of the ceiling.
*/
public Float getCeilingShininess() {
return this.ceilingShininess;
}
/**
* Returns <code>true</code> if walls around the edited rooms should be split.
* @since 4.0
*/
public boolean isSplitSurroundingWalls() {
return this.splitSurroundingWalls;
}
/**
* Sets whether walls around the edited rooms should be split or not.
* @since 4.0
*/
public void setSplitSurroundingWalls(boolean splitSurroundingWalls) {
if (splitSurroundingWalls != this.splitSurroundingWalls) {
this.splitSurroundingWalls = splitSurroundingWalls;
this.propertyChangeSupport.firePropertyChange(Property.SPLIT_SURROUNDING_WALLS.name(), !splitSurroundingWalls, splitSurroundingWalls);
}
}
/**
* Returns <code>true</code> if walls around the edited rooms need to be split
* to avoid changing the color of wall sides that belong to neighborhood rooms.
* @since 4.0
*/
public boolean isSplitSurroundingWallsNeeded() {
return this.splitSurroundingWallsNeeded;
}
/**
* Sets the edited color of the wall sides.
* @since 4.0
*/
public void setWallSidesColor(Integer wallSidesColor) {
if (wallSidesColor != this.wallSidesColor) {
Integer oldWallSidesColor = this.wallSidesColor;
this.wallSidesColor = wallSidesColor;
this.propertyChangeSupport.firePropertyChange(Property.WALL_SIDES_COLOR.name(), oldWallSidesColor, wallSidesColor);
setWallSidesPaint(RoomPaint.COLORED);
}
}
/**
* Returns the edited color of the wall sides.
* @since 4.0
*/
public Integer getWallSidesColor() {
return this.wallSidesColor;
}
/**
* Sets whether the wall sides are colored, textured or unknown painted.
* @since 4.0
*/
public void setWallSidesPaint(RoomPaint wallSidesPaint) {
if (wallSidesPaint != this.wallSidesPaint) {
RoomPaint oldWallSidesPaint = this.wallSidesPaint;
this.wallSidesPaint = wallSidesPaint;
this.propertyChangeSupport.firePropertyChange(Property.WALL_SIDES_PAINT.name(), oldWallSidesPaint, wallSidesPaint);
}
}
/**
* Returns whether the wall sides are colored, textured or unknown painted.
* @since 4.0
*/
public RoomPaint getWallSidesPaint() {
return this.wallSidesPaint;
}
/**
* Sets the edited shininess of the wall sides.
* @since 4.0
*/
public void setWallSidesShininess(Float wallSidesShininess) {
if (wallSidesShininess != this.wallSidesShininess) {
Float oldWallSidesShininess = this.wallSidesShininess;
this.wallSidesShininess = wallSidesShininess;
this.propertyChangeSupport.firePropertyChange(Property.WALL_SIDES_SHININESS.name(), oldWallSidesShininess, wallSidesShininess);
}
}
/**
* Returns the edited shininess of the wall sides.
* @since 4.0
*/
public Float getWallSidesShininess() {
return this.wallSidesShininess;
}
/**
* Controls the modification of selected rooms in edited home.
*/
public void modifyRooms() {
List<Selectable> oldSelection = this.home.getSelectedItems();
List<Room> selectedRooms = Home.getRoomsSubList(oldSelection);
if (!selectedRooms.isEmpty()) {
String name = getName();
Boolean areaVisible = getAreaVisible();
Boolean floorVisible = getFloorVisible();
Integer floorColor = getFloorPaint() == RoomPaint.COLORED
? getFloorColor() : null;
HomeTexture floorTexture = getFloorPaint() == RoomPaint.TEXTURED
? getFloorTextureController().getTexture() : null;
Float floorShininess = getFloorShininess();
Boolean ceilingVisible = getCeilingVisible();
Integer ceilingColor = getCeilingPaint() == RoomPaint.COLORED
? getCeilingColor() : null;
HomeTexture ceilingTexture = getCeilingPaint() == RoomPaint.TEXTURED
? getCeilingTextureController().getTexture() : null;
Float ceilingShininess = getCeilingShininess();
Integer wallSidesColor = getWallSidesPaint() == RoomPaint.COLORED
? getWallSidesColor() : null;
HomeTexture wallSidesTexture = getWallSidesPaint() == RoomPaint.TEXTURED
? getWallSidesTextureController().getTexture() : null;
Float wallSidesShininess = getWallSidesShininess();
List<WallSide> selectedRoomsWallSides = getRoomsWallSides(selectedRooms, null);
// Create an array of modified rooms with their current properties values
ModifiedRoom [] modifiedRooms = new ModifiedRoom [selectedRooms.size()];
for (int i = 0; i < modifiedRooms.length; i++) {
modifiedRooms [i] = new ModifiedRoom(selectedRooms.get(i));
}
// Apply modification
List<ModifiedWall> deletedWalls = new ArrayList<ModifiedWall>();
List<ModifiedWall> addedWalls = new ArrayList<ModifiedWall>();
List<Selectable> newSelection = new ArrayList<Selectable>(oldSelection);
if (this.splitSurroundingWalls) {
if (splitWalls(selectedRoomsWallSides, deletedWalls, addedWalls, newSelection)) {
this.home.setSelectedItems(newSelection);
// Update wall sides
selectedRoomsWallSides = getRoomsWallSides(selectedRooms, selectedRoomsWallSides);
}
}
// Create an array of modified wall sides with their current properties values
ModifiedWallSide [] modifiedWallSides = new ModifiedWallSide [selectedRoomsWallSides.size()];
for (int i = 0; i < modifiedWallSides.length; i++) {
modifiedWallSides [i] = new ModifiedWallSide(selectedRoomsWallSides.get(i));
}
doModifyRoomsAndWallSides(home, modifiedRooms, name, areaVisible,
floorVisible, floorColor, floorTexture, floorShininess,
ceilingVisible, ceilingColor, ceilingTexture, ceilingShininess,
modifiedWallSides, wallSidesColor, wallSidesTexture, wallSidesShininess, null, null);
if (this.undoSupport != null) {
UndoableEdit undoableEdit = new RoomsAndWallSidesModificationUndoableEdit(
this.home, this.preferences, oldSelection, newSelection, modifiedRooms, name, areaVisible,
floorColor, floorTexture, floorVisible, floorShininess,
ceilingColor, ceilingTexture, ceilingVisible, ceilingShininess,
modifiedWallSides, wallSidesColor, wallSidesTexture, wallSidesShininess,
deletedWalls.toArray(new ModifiedWall [deletedWalls.size()]),
addedWalls.toArray(new ModifiedWall [addedWalls.size()]));
this.undoSupport.postEdit(undoableEdit);
}
if (name != null) {
this.preferences.addAutoCompletionString("RoomName", name);
}
}
}
/**
* Splits walls that overfill on other rooms if needed and returns <code>false</code> if the operation wasn't needed.
*/
private boolean splitWalls(List<WallSide> wallSides,
List<ModifiedWall> deletedWalls,
List<ModifiedWall> addedWalls,
List<Selectable> selectedItems) {
Map<Wall, ModifiedWall> existingWalls = null;
List<Wall> newWalls = new ArrayList<Wall>();
WallSide splitWallSide;
do {
splitWallSide = null;
Wall firstWall = null;
Wall secondWall = null;
ModifiedWall deletedWall = null;
for (Iterator<WallSide> it = wallSides.iterator();
it.hasNext() && splitWallSide == null; ) {
WallSide wallSide = (WallSide)it.next();
Wall wall = wallSide.getWall();
if (wall.getArcExtent() == null) { // Ignore round walls
Area wallArea = new Area(getPath(wall.getPoints(), true));
for (WallSide intersectedWallSide : wallSides) {
Wall intersectedWall = intersectedWallSide.getWall();
if (wall != intersectedWall) {
Area intersectedWallArea = new Area(getPath(intersectedWall.getPoints(), true));
intersectedWallArea.intersect(wallArea);
if (!intersectedWallArea.isEmpty()
&& intersectedWallArea.isSingular()) {
float [] intersection = computeIntersection(
wall.getXStart(), wall.getYStart(), wall.getXEnd(), wall.getYEnd(),
intersectedWall.getXStart(), intersectedWall.getYStart(), intersectedWall.getXEnd(), intersectedWall.getYEnd());
if (intersection != null) {
// Clone new walls to copy their characteristics
firstWall = wall.clone();
secondWall = wall.clone();
// Change split walls end and start point
firstWall.setXEnd(intersection [0]);
firstWall.setYEnd(intersection [1]);
secondWall.setXStart(intersection [0]);
secondWall.setYStart(intersection [1]);
if (firstWall.getLength() > intersectedWall.getThickness() / 2
&& secondWall.getLength() > intersectedWall.getThickness() / 2) {
// If method is called for test purpose
if (deletedWalls == null) {
return true;
}
if (existingWalls == null) {
// Store all walls at start and end in case there would be more than one change on a wall
existingWalls = new HashMap<Wall, ModifiedWall>(wallSides.size());
for (WallSide side : wallSides) {
if (!existingWalls.containsKey(side.getWall())) {
existingWalls.put(side.getWall(), new ModifiedWall(side.getWall()));
}
}
}
deletedWall = existingWalls.get(wall);
Wall wallAtStart = wall.getWallAtStart();
if (wallAtStart != null) {
firstWall.setWallAtStart(wallAtStart);
if (wallAtStart.getWallAtEnd() == wall) {
wallAtStart.setWallAtEnd(firstWall);
} else {
wallAtStart.setWallAtStart(firstWall);
}
}
Wall wallAtEnd = wall.getWallAtEnd();
if (wallAtEnd != null) {
secondWall.setWallAtEnd(wallAtEnd);
if (wallAtEnd.getWallAtEnd() == wall) {
wallAtEnd.setWallAtEnd(secondWall);
} else {
wallAtEnd.setWallAtStart(secondWall);
}
}
firstWall.setWallAtEnd(secondWall);
secondWall.setWallAtStart(firstWall);
if (wall.getHeightAtEnd() != null) {
Float heightAtIntersecion = wall.getHeight()
+ (wall.getHeightAtEnd() - wall.getHeight())
* (float)Point2D.distance(wall.getXStart(), wall.getYStart(), intersection [0], intersection [1])
/ wall.getLength();
firstWall.setHeightAtEnd(heightAtIntersecion);
secondWall.setHeight(heightAtIntersecion);
}
splitWallSide = wallSide;
break;
}
}
}
}
}
}
}
if (splitWallSide != null) {
newWalls.add(firstWall);
newWalls.add(secondWall);
Wall splitWall = splitWallSide.getWall();
if (this.home.getWalls().contains(splitWall)) {
deletedWalls.add(deletedWall);
} else {
// Remove from newWalls in case it was a wall split twice
for (Iterator<Wall> it = newWalls.iterator(); it.hasNext(); ) {
if (it.next() == splitWall) {
it.remove();
break;
}
}
}
// Update selected items
if (selectedItems.remove(splitWall)) {
selectedItems.add(firstWall);
selectedItems.add(secondWall);
}
wallSides.remove(splitWallSide);
wallSides.add(new WallSide(firstWall, splitWallSide.getSide()));
wallSides.add(new WallSide(secondWall, splitWallSide.getSide()));
// Update any wall side that reference the same wall
List<WallSide> sameWallSides = new ArrayList<WallSide>();
for (Iterator<WallSide> it = wallSides.iterator(); it.hasNext(); ) {
WallSide wallSide = it.next();
if (wallSide.getWall() == splitWall) {
it.remove();
sameWallSides.add(new WallSide(firstWall, wallSide.getSide()));
sameWallSides.add(new WallSide(secondWall, wallSide.getSide()));
}
}
wallSides.addAll(sameWallSides);
}
} while (splitWallSide != null);
// If method is called for test purpose
if (deletedWalls == null) {
return false;
} else {
for (Wall newWall : newWalls) {
this.home.addWall(newWall);
addedWalls.add(new ModifiedWall(newWall));
}
for (ModifiedWall deletedWall : deletedWalls) {
this.home.deleteWall(deletedWall.getWall());
}
return !deletedWalls.isEmpty();
}
}
/**
* Returns the intersection between a line segment and a second line.
*/
private float [] computeIntersection(float xPoint1, float yPoint1, float xPoint2, float yPoint2,
float xPoint3, float yPoint3, float xPoint4, float yPoint4) {
float [] point = PlanController.computeIntersection(xPoint1, yPoint1, xPoint2, yPoint2,
xPoint3, yPoint3, xPoint4, yPoint4);
if (Line2D.ptSegDistSq(xPoint1, yPoint1, xPoint2, yPoint2, point [0], point [1]) < 1E-7
&& (Math.abs(xPoint1 - point [0]) > 1E-4
|| Math.abs(yPoint1 - point [1]) > 1E-4)
&& (Math.abs(xPoint2 - point [0]) > 1E-4
|| Math.abs(yPoint2 - point [1]) > 1E-4)) {
return point;
} else {
return null;
}
}
/**
* Undoable edit for rooms modification. This class isn't anonymous to avoid
* being bound to controller and its view.
*/
private static class RoomsAndWallSidesModificationUndoableEdit extends AbstractUndoableEdit {
private final Home home;
private final UserPreferences preferences;
private final List<Selectable> oldSelection;
private final List<Selectable> newSelection;
private final ModifiedRoom [] modifiedRooms;
private final String name;
private final Boolean areaVisible;
private final Integer floorColor;
private final HomeTexture floorTexture;
private final Boolean floorVisible;
private final Float floorShininess;
private final Integer ceilingColor;
private final HomeTexture ceilingTexture;
private final Boolean ceilingVisible;
private final Float ceilingShininess;
private final ModifiedWallSide [] modifiedWallSides;
private final Integer wallSidesColor;
private final HomeTexture wallSidesTexture;
private final Float wallSidesShininess;
private final ModifiedWall [] deletedWalls;
private final ModifiedWall [] addedWalls;
private RoomsAndWallSidesModificationUndoableEdit(Home home,
UserPreferences preferences,
List<Selectable> oldSelection,
List<Selectable> newSelection,
ModifiedRoom [] modifiedRooms,
String name,
Boolean areaVisible,
Integer floorColor,
HomeTexture floorTexture,
Boolean floorVisible,
Float floorShininess,
Integer ceilingColor,
HomeTexture ceilingTexture,
Boolean ceilingVisible,
Float ceilingShininess,
ModifiedWallSide [] modifiedWallSides,
Integer wallSidesColor,
HomeTexture wallSidesTexture,
Float wallSidesShininess,
ModifiedWall [] deletedWalls,
ModifiedWall [] addedWalls) {
this.home = home;
this.preferences = preferences;
this.oldSelection = oldSelection;
this.newSelection = newSelection;
this.modifiedRooms = modifiedRooms;
this.name = name;
this.areaVisible = areaVisible;
this.floorColor = floorColor;
this.floorTexture = floorTexture;
this.floorVisible = floorVisible;
this.floorShininess = floorShininess;
this.ceilingColor = ceilingColor;
this.ceilingTexture = ceilingTexture;
this.ceilingVisible = ceilingVisible;
this.ceilingShininess = ceilingShininess;
this.modifiedWallSides = modifiedWallSides;
this.wallSidesColor = wallSidesColor;
this.wallSidesTexture = wallSidesTexture;
this.wallSidesShininess = wallSidesShininess;
this.deletedWalls = deletedWalls;
this.addedWalls = addedWalls;
}
@Override
public void undo() throws CannotUndoException {
super.undo();
undoModifyRoomsAndWallSides(this.home, this.modifiedRooms, this.modifiedWallSides, this.deletedWalls, this.addedWalls);
this.home.setSelectedItems(this.oldSelection);
}
@Override
public void redo() throws CannotRedoException {
super.redo();
doModifyRoomsAndWallSides(this.home,
this.modifiedRooms, this.name, this.areaVisible,
this.floorVisible, this.floorColor, this.floorTexture, this.floorShininess,
this.ceilingVisible, this.ceilingColor, this.ceilingTexture, this.ceilingShininess,
this.modifiedWallSides, this.wallSidesColor, this.wallSidesTexture, this.wallSidesShininess,
this.deletedWalls, this.addedWalls);
this.home.setSelectedItems(this.newSelection);
}
@Override
public String getPresentationName() {
return this.preferences.getLocalizedString(RoomController.class, "undoModifyRoomsName");
}
}
/**
* Modifies rooms and walls properties with the values in parameter.
*/
private static void doModifyRoomsAndWallSides(Home home, ModifiedRoom [] modifiedRooms,
String name, Boolean areaVisible,
Boolean floorVisible, Integer floorColor, HomeTexture floorTexture, Float floorShininess,
Boolean ceilingVisible, Integer ceilingColor, HomeTexture ceilingTexture, Float ceilingShininess,
ModifiedWallSide [] modifiedWallSides,
Integer wallSidesColor, HomeTexture wallSidesTexture, Float wallSidesShininess,
ModifiedWall [] deletedWalls,
ModifiedWall [] addedWalls) {
if (deletedWalls != null) {
for (ModifiedWall newWall : addedWalls) {
newWall.reset();
home.addWall(newWall.getWall());
}
for (ModifiedWall deletedWall : deletedWalls) {
home.deleteWall(deletedWall.getWall());
}
}
for (ModifiedRoom modifiedRoom : modifiedRooms) {
Room room = modifiedRoom.getRoom();
if (name != null) {
room.setName(name);
}
if (areaVisible != null) {
room.setAreaVisible(areaVisible);
}
if (floorVisible != null) {
room.setFloorVisible(floorVisible);
}
if (floorTexture != null) {
room.setFloorTexture(floorTexture);
room.setFloorColor(null);
} else if (floorColor != null) {
room.setFloorColor(floorColor);
room.setFloorTexture(null);
}
if (floorShininess != null) {
room.setFloorShininess(floorShininess);
}
if (ceilingVisible != null) {
room.setCeilingVisible(ceilingVisible);
}
if (ceilingTexture != null) {
room.setCeilingTexture(ceilingTexture);
room.setCeilingColor(null);
} else if (ceilingColor != null) {
room.setCeilingColor(ceilingColor);
room.setCeilingTexture(null);
}
if (ceilingShininess != null) {
room.setCeilingShininess(ceilingShininess);
}
}
for (ModifiedWallSide modifiedWallSide : modifiedWallSides) {
WallSide wallSide = modifiedWallSide.getWallSide();
if (wallSidesColor != null) {
if (wallSide.getSide() == WallSide.LEFT_SIDE) {
wallSide.getWall().setLeftSideColor(wallSidesColor);
} else {
wallSide.getWall().setRightSideColor(wallSidesColor);
}
}
if (wallSidesTexture != null || wallSidesColor != null) {
if (wallSide.getSide() == WallSide.LEFT_SIDE) {
wallSide.getWall().setLeftSideTexture(wallSidesTexture);
} else {
wallSide.getWall().setRightSideTexture(wallSidesTexture);
}
}
if (wallSidesShininess != null) {
if (wallSide.getSide() == WallSide.LEFT_SIDE) {
wallSide.getWall().setLeftSideShininess(wallSidesShininess);
} else {
wallSide.getWall().setRightSideShininess(wallSidesShininess);
}
}
}
}
/**
* Restores room properties from the values stored in <code>modifiedRooms</code> and <code>modifiedWallSides</code>.
*/
private static void undoModifyRoomsAndWallSides(Home home,
ModifiedRoom [] modifiedRooms,
ModifiedWallSide [] modifiedWallSides,
ModifiedWall [] deletedWalls,
ModifiedWall [] addedWalls) {
for (ModifiedRoom modifiedRoom : modifiedRooms) {
modifiedRoom.reset();
}
for (ModifiedWallSide modifiedWallSide : modifiedWallSides) {
modifiedWallSide.reset();
}
for (ModifiedWall newWall : addedWalls) {
home.deleteWall(newWall.getWall());
}
for (ModifiedWall deletedWall : deletedWalls) {
deletedWall.reset();
home.addWall(deletedWall.getWall());
}
}
/**
* Stores the current properties values of a modified room.
*/
private static final class ModifiedRoom {
private final Room room;
private final String name;
private final boolean areaVisible;
private final boolean floorVisible;
private final Integer floorColor;
private final HomeTexture floorTexture;
private final float floorShininess;
private final boolean ceilingVisible;
private final Integer ceilingColor;
private final HomeTexture ceilingTexture;
private final float ceilingShininess;
public ModifiedRoom(Room room) {
this.room = room;
this.name = room.getName();
this.areaVisible = room.isAreaVisible();
this.floorVisible = room.isFloorVisible();
this.floorColor = room.getFloorColor();
this.floorTexture = room.getFloorTexture();
this.floorShininess = room.getFloorShininess();
this.ceilingVisible = room.isCeilingVisible();
this.ceilingColor = room.getCeilingColor();
this.ceilingTexture = room.getCeilingTexture();
this.ceilingShininess = room.getCeilingShininess();
}
public Room getRoom() {
return this.room;
}
public void reset() {
this.room.setName(this.name);
this.room.setAreaVisible(this.areaVisible);
this.room.setFloorVisible(this.floorVisible);
this.room.setFloorColor(this.floorColor);
this.room.setFloorTexture(this.floorTexture);
this.room.setFloorShininess(this.floorShininess);
this.room.setCeilingVisible(this.ceilingVisible);
this.room.setCeilingColor(this.ceilingColor);
this.room.setCeilingTexture(this.ceilingTexture);
this.room.setCeilingShininess(this.ceilingShininess);
}
}
/**
* A wall side.
*/
private class WallSide {
public static final int LEFT_SIDE = 0;
public static final int RIGHT_SIDE = 1;
private Wall wall;
private int side;
private final Wall wallAtStart;
private final Wall wallAtEnd;
private final boolean joinedAtEndOfWallAtStart;
private final boolean joinedAtStartOfWallAtEnd;
public WallSide(Wall wall, int side) {
this.wall = wall;
this.side = side;
this.wallAtStart = wall.getWallAtStart();
this.joinedAtEndOfWallAtStart =
this.wallAtStart != null
&& this.wallAtStart.getWallAtEnd() == wall;
this.wallAtEnd = wall.getWallAtEnd();
this.joinedAtStartOfWallAtEnd =
this.wallAtEnd != null
&& wallAtEnd.getWallAtStart() == wall;
}
public Wall getWall() {
return this.wall;
}
public int getSide() {
return this.side;
}
public Wall getWallAtStart() {
return this.wallAtStart;
}
public Wall getWallAtEnd() {
return this.wallAtEnd;
}
public boolean isJoinedAtEndOfWallAtStart() {
return this.joinedAtEndOfWallAtStart;
}
public boolean isJoinedAtStartOfWallAtEnd() {
return this.joinedAtStartOfWallAtEnd;
}
}
/**
* A modified wall.
*/
private class ModifiedWall {
private Wall wall;
private final Wall wallAtStart;
private final Wall wallAtEnd;
private final boolean joinedAtEndOfWallAtStart;
private final boolean joinedAtStartOfWallAtEnd;
public ModifiedWall(Wall wall) {
this.wall = wall;
this.wallAtStart = wall.getWallAtStart();
this.joinedAtEndOfWallAtStart =
this.wallAtStart != null
&& this.wallAtStart.getWallAtEnd() == wall;
this.wallAtEnd = wall.getWallAtEnd();
this.joinedAtStartOfWallAtEnd =
this.wallAtEnd != null
&& wallAtEnd.getWallAtStart() == wall;
}
public Wall getWall() {
return this.wall;
}
public void reset() {
if (this.wallAtStart != null) {
this.wall.setWallAtStart(this.wallAtStart);
if (this.joinedAtEndOfWallAtStart) {
this.wallAtStart.setWallAtEnd(this.wall);
} else {
this.wallAtStart.setWallAtStart(this.wall);
}
}
if (this.wallAtEnd != null) {
this.wall.setWallAtEnd(wallAtEnd);
if (this.joinedAtStartOfWallAtEnd) {
this.wallAtEnd.setWallAtStart(this.wall);
} else {
this.wallAtEnd.setWallAtEnd(this.wall);
}
}
}
}
/**
* Stores the current properties values of a modified wall side.
*/
private static final class ModifiedWallSide {
private final WallSide wallSide;
private final Integer wallColor;
private final HomeTexture wallTexture;
private final Float wallShininess;
public ModifiedWallSide(WallSide wallSide) {
this.wallSide = wallSide;
if (wallSide.getSide() == WallSide.LEFT_SIDE) {
this.wallColor = wallSide.getWall().getLeftSideColor();
this.wallTexture = wallSide.getWall().getLeftSideTexture();
this.wallShininess = wallSide.getWall().getLeftSideShininess();
} else {
this.wallColor = wallSide.getWall().getRightSideColor();
this.wallTexture = wallSide.getWall().getRightSideTexture();
this.wallShininess = wallSide.getWall().getRightSideShininess();
}
}
public WallSide getWallSide() {
return this.wallSide;
}
public void reset() {
if (this.wallSide.getSide() == WallSide.LEFT_SIDE) {
this.wallSide.getWall().setLeftSideColor(this.wallColor);
this.wallSide.getWall().setLeftSideTexture(this.wallTexture);
this.wallSide.getWall().setLeftSideShininess(this.wallShininess);
} else {
this.wallSide.getWall().setRightSideColor(this.wallColor);
this.wallSide.getWall().setRightSideTexture(this.wallTexture);
this.wallSide.getWall().setRightSideShininess(this.wallShininess);
}
Wall wall = wallSide.getWall();
Wall wallAtStart = wallSide.getWallAtStart();
if (wallAtStart != null) {
wall.setWallAtStart(wallAtStart);
if (wallSide.isJoinedAtEndOfWallAtStart()) {
wallAtStart.setWallAtEnd(wall);
} else {
wallAtStart.setWallAtStart(wall);
}
}
Wall wallAtEnd = wallSide.getWallAtEnd();
if (wallAtEnd != null) {
wall.setWallAtEnd(wallAtEnd);
if (wallSide.isJoinedAtStartOfWallAtEnd()) {
wallAtEnd.setWallAtStart(wall);
} else {
wallAtEnd.setWallAtEnd(wall);
}
}
}
}
}