001/*
002 * To change this license header, choose License Headers in Project Properties.
003 * To change this template file, choose Tools | Templates
004 * and open the template in the editor.
005 */
006
007package org.openstreetmap.josm.plugins.streetside.utils;
008
009import java.awt.image.BufferedImage;
010
011import org.openstreetmap.josm.plugins.streetside.cubemap.CubemapUtils;
012import org.openstreetmap.josm.plugins.streetside.cubemap.GraphicsUtils;
013
014import javafx.animation.AnimationTimer;
015import javafx.beans.property.DoubleProperty;
016import javafx.beans.property.SimpleDoubleProperty;
017import javafx.geometry.Rectangle2D;
018import javafx.scene.Group;
019import javafx.scene.PerspectiveCamera;
020import javafx.scene.image.Image;
021import javafx.scene.image.ImageView;
022import javafx.scene.transform.Affine;
023import javafx.scene.transform.Rotate;
024import javafx.scene.transform.Transform;
025
026/**
027 *
028 * @author Dub
029 * @author renerr18
030 */
031@SuppressWarnings("restriction")
032public class CubemapBox extends Group {
033
034  public enum CubemapBoxImageType {
035    MULTIPLE, SINGLE
036  }
037
038  private final Affine affine = new Affine();
039
040  private final ImageView front = new ImageView(), right = new ImageView(), back = new ImageView(),
041    left = new ImageView(), up = new ImageView(), down = new ImageView();
042
043  {
044    front.setId(CubemapUtils.CubemapFaces.FRONT.getValue());
045    right.setId(CubemapUtils.CubemapFaces.RIGHT.getValue());
046    back.setId(CubemapUtils.CubemapFaces.BACK.getValue());
047    left.setId(CubemapUtils.CubemapFaces.LEFT.getValue());
048    up.setId(CubemapUtils.CubemapFaces.UP.getValue());
049    down.setId(CubemapUtils.CubemapFaces.DOWN.getValue());
050
051  }
052  private final ImageView[] views = new ImageView[] { front, right, back, left, up, down };
053
054  private Image frontImg, rightImg, backImg, leftImg, upImg, downImg, singleImg;
055
056  // private WritableImage convertedImage;
057
058  private final PerspectiveCamera camera;
059  private AnimationTimer timer;
060  private final CubemapBoxImageType imageType;
061
062  public CubemapBox(Image singleImg, double size, PerspectiveCamera camera) {
063    super();
064    imageType = CubemapBoxImageType.SINGLE;
065
066    this.singleImg = singleImg;
067    this.size.set(size);
068    this.camera = camera;
069
070    getTransforms().add(affine);
071
072    loadImageViews();
073
074    getChildren().addAll(views);
075  }
076
077  // private constructor to prevent instantiation
078  public CubemapBox(double size, PerspectiveCamera camera) {
079    super();
080    imageType = CubemapBoxImageType.SINGLE;
081
082    this.size.set(size);
083    this.camera = camera;
084
085    getTransforms().add(affine);
086
087    loadImageViews();
088
089    getChildren().addAll(views);
090  }
091
092  public CubemapBox(
093    Image frontImg, Image rightImg, Image backImg, Image leftImg, Image upImg, Image downImg, double size,
094    PerspectiveCamera camera
095  ) {
096    super();
097    imageType = CubemapBoxImageType.MULTIPLE;
098
099    this.frontImg = frontImg;
100    this.rightImg = rightImg;
101    this.backImg = backImg;
102    this.leftImg = leftImg;
103    this.upImg = upImg;
104    this.downImg = downImg;
105    this.size.set(size);
106    this.camera = camera;
107
108    loadImageViews();
109
110    getTransforms().add(affine);
111
112    getChildren().addAll(views);
113
114    startTimer();
115  }
116
117  public void loadImageViews() {
118
119    for (ImageView iv : views) {
120      iv.setSmooth(true);
121      iv.setPreserveRatio(true);
122    }
123
124    validateImageType();
125  }
126
127  private void layoutViews() {
128
129    for (ImageView v : views) {
130      v.setFitWidth(getSize());
131      v.setFitHeight(getSize());
132    }
133
134    back.setTranslateX(-0.5 * getSize());
135    back.setTranslateY(-0.5 * getSize());
136    back.setTranslateZ(-0.5 * getSize());
137
138    front.setTranslateX(-0.5 * getSize());
139    front.setTranslateY(-0.5 * getSize());
140    front.setTranslateZ(0.5 * getSize());
141    front.setRotationAxis(Rotate.Z_AXIS);
142    front.setRotate(-180);
143    front.getTransforms().add(new Rotate(180, front.getFitHeight() / 2, 0, 0, Rotate.X_AXIS));
144    front.setTranslateY(front.getTranslateY() - getSize());
145
146    up.setTranslateX(-0.5 * getSize());
147    up.setTranslateY(-1 * getSize());
148    up.setRotationAxis(Rotate.X_AXIS);
149    up.setRotate(-90);
150
151    down.setTranslateX(-0.5 * getSize());
152    down.setTranslateY(0);
153    down.setRotationAxis(Rotate.X_AXIS);
154    down.setRotate(90);
155
156    left.setTranslateX(-1 * getSize());
157    left.setTranslateY(-0.5 * getSize());
158    left.setRotationAxis(Rotate.Y_AXIS);
159    left.setRotate(90);
160
161    right.setTranslateX(0);
162    right.setTranslateY(-0.5 * getSize());
163    right.setRotationAxis(Rotate.Y_AXIS);
164    right.setRotate(-90);
165
166  }
167
168  /**
169   * for single image creates viewports and sets all views(image) to singleImg for multiple... sets images per view.
170   */
171  private void validateImageType() {
172    switch (imageType) {
173    case SINGLE:
174      loadSingleImageViewports();
175      break;
176    case MULTIPLE:
177      setMultipleImages();
178      break;
179    }
180  }
181
182  private void loadSingleImageViewports() {
183    layoutViews();
184    double width = singleImg.getWidth(), height = singleImg.getHeight();
185
186    // simple check to see if cells will be square
187    if (width / 4 != height / 3) {
188      throw new UnsupportedOperationException("Image does not comply with size constraints");
189    }
190    double cellSize = singleImg.getWidth() - singleImg.getHeight();
191
192    recalculateSize(cellSize);
193
194    double topx = cellSize, topy = 0,
195
196      botx = cellSize, boty = cellSize * 2,
197
198      leftx = 0, lefty = cellSize,
199
200      rightx = cellSize * 2, righty = cellSize,
201
202      fwdx = cellSize, fwdy = cellSize,
203
204      backx = cellSize * 3, backy = cellSize;
205
206    // add top padding x+, y+, width-, height
207    up.setViewport(new Rectangle2D(topx, topy, cellSize, cellSize));
208
209    // add left padding x, y+, width, height-
210    left.setViewport(new Rectangle2D(leftx, lefty, cellSize - 1, cellSize - 1));
211
212    // add front padding x+, y+, width-, height
213    front.setViewport(new Rectangle2D(fwdx, fwdy, cellSize, cellSize));
214
215    // add right padding x, y+, width, height-
216    right.setViewport(new Rectangle2D(rightx, righty, cellSize, cellSize));
217
218    // add back padding x, y+, width, height-
219    back.setViewport(new Rectangle2D(backx + 1, backy - 1, cellSize - 1, cellSize - 1));
220
221    // add bottom padding x+, y, width-, height-
222    down.setViewport(new Rectangle2D(botx, boty, cellSize, cellSize));
223
224    for (ImageView v : views) {
225      v.setImage(singleImg);
226    }
227  }
228
229  private void recalculateSize(double cell) {
230    double factor = Math.floor(getSize() / cell);
231    setSize(cell * factor);
232  }
233
234  public synchronized void setImage(BufferedImage img, int position) {
235    views[position].setImage(GraphicsUtils.convertBufferedImage2JavaFXImage(img));
236
237  }
238
239  private void setMultipleImages() {
240    GraphicsUtils.PlatformHelper.run(() -> {
241      layoutViews();
242      front.setImage(frontImg);
243      right.setImage(rightImg);
244      back.setImage(backImg);
245      left.setImage(leftImg);
246      up.setImage(upImg);
247      down.setImage(downImg);
248
249    });
250  }
251
252  public void startTimer() {
253    timer = new AnimationTimer() {
254      @Override
255      public void handle(long now) {
256        Transform ct = (camera != null) ? camera.getLocalToSceneTransform() : null;
257        if (ct != null) {
258          affine.setTx(ct.getTx());
259          affine.setTy(ct.getTy());
260          affine.setTz(ct.getTz());
261        }
262      }
263    };
264    timer.start();
265  }
266
267  /*
268   * Properties
269   */
270  private final DoubleProperty size = new SimpleDoubleProperty() {
271    @Override
272    protected void invalidated() {
273      switch (imageType) {
274      case SINGLE:
275        layoutViews();
276        break;
277      case MULTIPLE:
278        break;
279      }
280
281    }
282  };
283
284  public final double getSize() {
285    return size.get();
286  }
287
288  public final void setSize(double value) {
289    size.set(value);
290  }
291
292  public DoubleProperty sizeProperty() {
293    return size;
294  }
295
296  public ImageView[] getViews() {
297    return views;
298  }
299}