1 /* 2 * ObserverCameraController.java 09 mars 2012 3 * 4 * Sweet Home 3D, Copyright (c) 2012 Emmanuel PUYBARET / eTeks <info@eteks.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 package com.eteks.sweethome3d.viewcontroller; 21 22 import java.beans.PropertyChangeListener; 23 import java.beans.PropertyChangeSupport; 24 import java.util.List; 25 26 import com.eteks.sweethome3d.model.Home; 27 import com.eteks.sweethome3d.model.HomeEnvironment; 28 import com.eteks.sweethome3d.model.Level; 29 import com.eteks.sweethome3d.model.ObserverCamera; 30 import com.eteks.sweethome3d.model.UserPreferences; 31 32 /** 33 * A MVC controller for observer camera attributes view. 34 * @author Emmanuel Puybaret 35 */ 36 public class ObserverCameraController implements Controller { 37 /** 38 * The properties that may be edited by the view associated to this controller. 39 */ 40 public enum Property {X, Y, ELEVATION, MINIMUM_ELEVATION, 41 YAW_IN_DEGREES, YAW, PITCH_IN_DEGREES, PITCH, FIELD_OF_VIEW_IN_DEGREES, FIELD_OF_VIEW, 42 OBSERVER_CAMERA_ELEVATION_ADJUSTED} 43 44 private final Home home; 45 private final UserPreferences preferences; 46 private final ViewFactory viewFactory; 47 private final PropertyChangeSupport propertyChangeSupport; 48 private DialogView observerCameraView; 49 50 private float x; 51 private float y; 52 private float elevation; 53 private float minimumElevation; 54 private int yawInDegrees; 55 private float yaw; 56 private int pitchInDegrees; 57 private float pitch; 58 private int fieldOfViewInDegrees; 59 private float fieldOfView; 60 private boolean elevationAdjusted; 61 62 /** 63 * Creates the controller of 3D view with undo support. 64 */ ObserverCameraController(Home home, UserPreferences preferences, ViewFactory viewFactory)65 public ObserverCameraController(Home home, 66 UserPreferences preferences, 67 ViewFactory viewFactory) { 68 this.home = home; 69 this.preferences = preferences; 70 this.viewFactory = viewFactory; 71 this.propertyChangeSupport = new PropertyChangeSupport(this); 72 73 updateProperties(); 74 } 75 76 /** 77 * Returns the view associated with this controller. 78 */ getView()79 public DialogView getView() { 80 // Create view lazily only once it's needed 81 if (this.observerCameraView == null) { 82 this.observerCameraView = this.viewFactory.createObserverCameraView(this.preferences, this); 83 } 84 return this.observerCameraView; 85 } 86 87 /** 88 * Displays the view controlled by this controller. 89 */ displayView(View parentView)90 public void displayView(View parentView) { 91 getView().displayView(parentView); 92 } 93 94 /** 95 * Adds the property change <code>listener</code> in parameter to this controller. 96 */ addPropertyChangeListener(Property property, PropertyChangeListener listener)97 public void addPropertyChangeListener(Property property, PropertyChangeListener listener) { 98 this.propertyChangeSupport.addPropertyChangeListener(property.name(), listener); 99 } 100 101 /** 102 * Removes the property change <code>listener</code> in parameter from this controller. 103 */ removePropertyChangeListener(Property property, PropertyChangeListener listener)104 public void removePropertyChangeListener(Property property, PropertyChangeListener listener) { 105 this.propertyChangeSupport.removePropertyChangeListener(property.name(), listener); 106 } 107 108 /** 109 * Updates edited properties from the 3D attributes of the home edited by this controller. 110 */ updateProperties()111 protected void updateProperties() { 112 ObserverCamera observerCamera = this.home.getObserverCamera(); 113 setX(observerCamera.getX()); 114 setY(observerCamera.getY()); 115 List<Level> levels = this.home.getLevels(); 116 setMinimumElevation(levels.size() == 0 117 ? 10 118 : 10 + levels.get(0).getElevation()); 119 setElevation(observerCamera.getZ()); 120 setYaw(observerCamera.getYaw()); 121 setPitch(observerCamera.getPitch()); 122 setFieldOfView(observerCamera.getFieldOfView()); 123 HomeEnvironment homeEnvironment = this.home.getEnvironment(); 124 setElevationAdjusted(homeEnvironment.isObserverCameraElevationAdjusted()); 125 } 126 127 /** 128 * Sets the edited abscissa. 129 */ setX(float x)130 public void setX(float x) { 131 if (x != this.x) { 132 float oldX = this.x; 133 this.x = x; 134 this.propertyChangeSupport.firePropertyChange(Property.X.name(), oldX, x); 135 } 136 } 137 138 /** 139 * Returns the edited abscissa. 140 */ getX()141 public float getX() { 142 return this.x; 143 } 144 145 /** 146 * Sets the edited ordinate. 147 */ setY(float y)148 public void setY(float y) { 149 if (y != this.y) { 150 float oldY = this.y; 151 this.y = y; 152 this.propertyChangeSupport.firePropertyChange(Property.Y.name(), oldY, y); 153 } 154 } 155 156 /** 157 * Returns the edited ordinate. 158 */ getY()159 public float getY() { 160 return this.y; 161 } 162 163 /** 164 * Sets the edited camera elevation. 165 */ setElevation(float elevation)166 public void setElevation(float elevation) { 167 if (elevation != this.elevation) { 168 float oldObserverCameraElevation = this.elevation; 169 this.elevation = elevation; 170 this.propertyChangeSupport.firePropertyChange(Property.ELEVATION.name(), oldObserverCameraElevation, elevation); 171 } 172 } 173 174 /** 175 * Returns the edited camera elevation. 176 */ getElevation()177 public float getElevation() { 178 return this.elevation; 179 } 180 181 /** 182 * Sets the minimum elevation. 183 */ setMinimumElevation(float minimumElevation)184 private void setMinimumElevation(float minimumElevation) { 185 if (minimumElevation != this.minimumElevation) { 186 float oldMinimumElevation = this.minimumElevation; 187 this.minimumElevation = minimumElevation; 188 this.propertyChangeSupport.firePropertyChange(Property.MINIMUM_ELEVATION.name(), oldMinimumElevation, minimumElevation); 189 } 190 } 191 192 /** 193 * Returns the minimum elevation. 194 */ getMinimumElevation()195 public float getMinimumElevation() { 196 return this.minimumElevation; 197 } 198 199 /** 200 * Returns <code>true</code> if the observer elevation should be adjusted according 201 * to the elevation of the selected level. 202 */ isElevationAdjusted()203 public boolean isElevationAdjusted() { 204 return this.elevationAdjusted; 205 } 206 207 /** 208 * Sets whether the observer elevation should be adjusted according 209 * to the elevation of the selected level. 210 */ setElevationAdjusted(boolean observerCameraElevationAdjusted)211 public void setElevationAdjusted(boolean observerCameraElevationAdjusted) { 212 if (this.elevationAdjusted != observerCameraElevationAdjusted) { 213 this.elevationAdjusted = observerCameraElevationAdjusted; 214 this.propertyChangeSupport.firePropertyChange(Property.OBSERVER_CAMERA_ELEVATION_ADJUSTED.name(), 215 !observerCameraElevationAdjusted, observerCameraElevationAdjusted); 216 Level selectedLevel = this.home.getSelectedLevel(); 217 if (selectedLevel != null) { 218 if (observerCameraElevationAdjusted) { 219 setElevation(getElevation() - selectedLevel.getElevation()); 220 } else { 221 setElevation(getElevation() + selectedLevel.getElevation()); 222 } 223 } 224 } 225 } 226 227 /** 228 * Returns <code>true</code> if the adjustment of the observer camera according to the current level is modifiable. 229 */ isObserverCameraElevationAdjustedEditable()230 public boolean isObserverCameraElevationAdjustedEditable() { 231 return this.home.getLevels().size() > 1; 232 } 233 234 /** 235 * Sets the edited yaw in degrees. 236 */ setYawInDegrees(int yawInDegrees)237 public void setYawInDegrees(int yawInDegrees) { 238 setYawInDegrees(yawInDegrees, true); 239 } 240 setYawInDegrees(int yawInDegrees, boolean updateYaw)241 private void setYawInDegrees(int yawInDegrees, boolean updateYaw) { 242 if (yawInDegrees != this.yawInDegrees) { 243 int oldYawInDegrees = this.yawInDegrees; 244 this.yawInDegrees = yawInDegrees; 245 this.propertyChangeSupport.firePropertyChange(Property.YAW_IN_DEGREES.name(), oldYawInDegrees, yawInDegrees); 246 if (updateYaw) { 247 setYaw((float)Math.toRadians(yawInDegrees), false); 248 } 249 } 250 } 251 252 /** 253 * Returns the edited yaw in degrees. 254 */ getYawInDegrees()255 public int getYawInDegrees() { 256 return this.yawInDegrees; 257 } 258 259 /** 260 * Sets the edited yaw in radians. 261 * @since 5.5 262 */ setYaw(float yaw)263 public void setYaw(float yaw) { 264 setYaw(yaw, true); 265 } 266 setYaw(float yaw, boolean updateYawInDegrees)267 private void setYaw(float yaw, boolean updateYawInDegrees) { 268 if (yaw != this.yaw) { 269 float oldYaw = this.yaw; 270 this.yaw = yaw; 271 this.propertyChangeSupport.firePropertyChange(Property.YAW.name(), oldYaw, yaw); 272 if (updateYawInDegrees) { 273 setYawInDegrees((int)Math.round(Math.toDegrees(yaw)), false); 274 } 275 } 276 } 277 278 /** 279 * Returns the edited yaw in radians. 280 * @since 5.5 281 */ getYaw()282 public float getYaw() { 283 return this.yaw; 284 } 285 286 /** 287 * Sets the edited pitch in degrees. 288 */ setPitchInDegrees(int pitchInDegrees)289 public void setPitchInDegrees(int pitchInDegrees) { 290 setPitchInDegrees(pitchInDegrees, true); 291 } 292 setPitchInDegrees(int pitchInDegrees, boolean updatePitch)293 private void setPitchInDegrees(int pitchInDegrees, boolean updatePitch) { 294 if (pitchInDegrees != this.pitchInDegrees) { 295 int oldPitchInDegrees = this.pitchInDegrees; 296 this.pitchInDegrees = pitchInDegrees; 297 this.propertyChangeSupport.firePropertyChange(Property.PITCH_IN_DEGREES.name(), oldPitchInDegrees, pitchInDegrees); 298 if (updatePitch) { 299 setPitch((float)Math.toRadians(pitchInDegrees), false); 300 } 301 } 302 } 303 304 /** 305 * Returns the edited pitch in degrees. 306 */ getPitchInDegrees()307 public int getPitchInDegrees() { 308 return this.pitchInDegrees; 309 } 310 311 /** 312 * Sets the edited pitch in radians. 313 * @since 5.5 314 */ setPitch(float pitch)315 public void setPitch(float pitch) { 316 setPitch(pitch, true); 317 } 318 setPitch(float pitch, boolean updatePitchInDegrees)319 private void setPitch(float pitch, boolean updatePitchInDegrees) { 320 if (pitch != this.pitch) { 321 float oldPitch = this.pitch; 322 this.pitch = pitch; 323 this.propertyChangeSupport.firePropertyChange(Property.PITCH.name(), oldPitch, pitch); 324 if (updatePitchInDegrees) { 325 setPitchInDegrees((int)(Math.round(Math.toDegrees(pitch))), false); 326 } 327 } 328 } 329 330 /** 331 * Returns the edited pitch in radians. 332 * @since 5.5 333 */ getPitch()334 public float getPitch() { 335 return this.pitch; 336 } 337 338 /** 339 * Sets the edited observer field of view in degrees. 340 */ setFieldOfViewInDegrees(int fieldOfViewInDegrees)341 public void setFieldOfViewInDegrees(int fieldOfViewInDegrees) { 342 setFieldOfViewInDegrees(fieldOfViewInDegrees, true); 343 } 344 setFieldOfViewInDegrees(int fieldOfViewInDegrees, boolean updateFieldOfView)345 public void setFieldOfViewInDegrees(int fieldOfViewInDegrees, boolean updateFieldOfView) { 346 if (fieldOfViewInDegrees != this.fieldOfViewInDegrees) { 347 int oldFieldOfViewInDegrees = this.fieldOfViewInDegrees; 348 this.fieldOfViewInDegrees = fieldOfViewInDegrees; 349 this.propertyChangeSupport.firePropertyChange(Property.FIELD_OF_VIEW_IN_DEGREES.name(), 350 oldFieldOfViewInDegrees, fieldOfViewInDegrees); 351 if (updateFieldOfView) { 352 setFieldOfView((float)Math.toRadians(fieldOfViewInDegrees), false); 353 } 354 } 355 } 356 357 /** 358 * Returns the edited observer field of view in degrees. 359 */ getFieldOfViewInDegrees()360 public int getFieldOfViewInDegrees() { 361 return this.fieldOfViewInDegrees; 362 } 363 364 /** 365 * Sets the edited observer field of view in radians. 366 * @since 5.5 367 */ setFieldOfView(float fieldOfView)368 public void setFieldOfView(float fieldOfView) { 369 setFieldOfView(fieldOfView, true); 370 } 371 setFieldOfView(float fieldOfView, boolean updateFieldOfViewInDegrees)372 private void setFieldOfView(float fieldOfView, boolean updateFieldOfViewInDegrees) { 373 if (fieldOfView != this.fieldOfView) { 374 float oldFieldOfView = this.fieldOfView; 375 this.fieldOfView = fieldOfView; 376 this.propertyChangeSupport.firePropertyChange(Property.FIELD_OF_VIEW.name(), oldFieldOfView, fieldOfView); 377 if (updateFieldOfViewInDegrees) { 378 setFieldOfViewInDegrees((int)(Math.round(Math.toDegrees(fieldOfView))), false); 379 } 380 } 381 } 382 383 /** 384 * Returns the edited observer field of view in radians. 385 * @since 5.5 386 */ getFieldOfView()387 public float getFieldOfView() { 388 return this.fieldOfView; 389 } 390 391 /** 392 * Controls the modification of the observer camera of the edited home. 393 */ modifyObserverCamera()394 public void modifyObserverCamera() { 395 float x = getX(); 396 float y = getY(); 397 float z = getElevation(); 398 boolean observerCameraElevationAdjusted = isElevationAdjusted(); 399 Level selectedLevel = this.home.getSelectedLevel(); 400 if (observerCameraElevationAdjusted && selectedLevel != null) { 401 z += selectedLevel.getElevation(); 402 List<Level> levels = this.home.getLevels(); 403 z = Math.max(z, levels.size() == 0 ? 10 : 10 + levels.get(0).getElevation()); 404 } 405 float yaw = getYaw(); 406 float pitch = getPitch(); 407 float fieldOfView = getFieldOfView(); 408 409 // Apply modification with no undo 410 ObserverCamera observerCamera = this.home.getObserverCamera(); 411 observerCamera.setX(x); 412 observerCamera.setY(y); 413 observerCamera.setZ(z); 414 observerCamera.setYaw(yaw); 415 observerCamera.setPitch(pitch); 416 observerCamera.setFieldOfView(fieldOfView); 417 HomeEnvironment homeEnvironment = this.home.getEnvironment(); 418 homeEnvironment.setObserverCameraElevationAdjusted(observerCameraElevationAdjusted); 419 } 420 } 421