    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}