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