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}