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
041    front = new ImageView(),
042        right = new ImageView(),
043        back  = new ImageView(),
044        left  = new ImageView(),
045    up    = new ImageView(),
046    down  = new ImageView();
047
048    {
049        front.setId(CubemapUtils.CubemapFaces.FRONT.getValue());
050        right.setId(CubemapUtils.CubemapFaces.RIGHT.getValue());
051        back.setId(CubemapUtils.CubemapFaces.BACK.getValue());
052        left.setId(CubemapUtils.CubemapFaces.LEFT.getValue());
053        up.setId(CubemapUtils.CubemapFaces.UP.getValue());
054        down .setId(CubemapUtils.CubemapFaces.DOWN.getValue());
055
056    }
057    private final ImageView[] views = new ImageView[]{
058        front,
059        right,
060        back,
061        left,
062        up,
063        down
064    };
065
066        private Image
067                frontImg, rightImg, backImg, leftImg, upImg, downImg, singleImg;
068
069        //private WritableImage convertedImage;
070
071    private final PerspectiveCamera camera;
072    private AnimationTimer timer;
073    private final CubemapBoxImageType imageType;
074
075    public CubemapBox(Image singleImg, double size, PerspectiveCamera camera) {
076        super();
077        imageType = CubemapBoxImageType.SINGLE;
078
079        this.singleImg = singleImg;
080        this.size.set(size);
081        this.camera = camera;
082
083        getTransforms().add(affine);
084
085        loadImageViews();
086
087        getChildren().addAll(views);
088    }
089
090    // private constructor to prevent instantiation
091    public CubemapBox(double size, PerspectiveCamera camera) {
092        super();
093        imageType = CubemapBoxImageType.SINGLE;
094
095        this.size.set(size);
096        this.camera = camera;
097
098        getTransforms().add(affine);
099
100        loadImageViews();
101
102        getChildren().addAll(views);
103    }
104
105    public CubemapBox(Image frontImg, Image rightImg, Image backImg, Image leftImg, Image upImg, Image downImg, double size, PerspectiveCamera camera) {
106        super();
107        imageType = CubemapBoxImageType.MULTIPLE;
108
109        this.frontImg = frontImg;
110        this.rightImg = rightImg;
111        this.backImg = backImg;
112        this.leftImg = leftImg;
113        this.upImg = upImg;
114        this.downImg = downImg;
115        this.size.set(size);
116        this.camera = camera;
117
118        loadImageViews();
119
120        getTransforms().add(affine);
121
122        getChildren().addAll(views);
123
124        startTimer();
125    }
126
127    public void loadImageViews(){
128
129        for(ImageView iv : views){
130            iv.setSmooth(true);
131            iv.setPreserveRatio(true);
132        }
133
134        validateImageType();
135    }
136
137    private void layoutViews() {
138
139        for(ImageView v : views){
140            v.setFitWidth(getSize());
141            v.setFitHeight(getSize());
142        }
143
144        back.setTranslateX(-0.5 * getSize());
145        back.setTranslateY(-0.5 * getSize());
146        back.setTranslateZ(-0.5 * getSize());
147
148        front.setTranslateX(-0.5 * getSize());
149        front.setTranslateY(-0.5 * getSize());
150        front.setTranslateZ(0.5 * getSize());
151        front.setRotationAxis(Rotate.Z_AXIS);
152        front.setRotate(-180);
153        front.getTransforms().add(new Rotate(180,front.getFitHeight() / 2, 0,0, Rotate.X_AXIS));
154        front.setTranslateY(front.getTranslateY() - getSize());
155
156        up.setTranslateX(-0.5 * getSize());
157        up.setTranslateY(-1 * getSize());
158        up.setRotationAxis(Rotate.X_AXIS);
159        up.setRotate(-90);
160
161        down.setTranslateX(-0.5 * getSize());
162        down.setTranslateY(0);
163        down.setRotationAxis(Rotate.X_AXIS);
164        down.setRotate(90);
165
166        left.setTranslateX(-1 * getSize());
167        left.setTranslateY(-0.5 * getSize());
168        left.setRotationAxis(Rotate.Y_AXIS);
169        left.setRotate(90);
170
171        right.setTranslateX(0);
172        right.setTranslateY(-0.5 * getSize());
173        right.setRotationAxis(Rotate.Y_AXIS);
174        right.setRotate(-90);
175
176    }
177    /**
178     *  for single image creates viewports and sets all views(image) to singleImg
179     *  for multiple... sets images per view.
180     */
181    private void validateImageType(){
182        switch(imageType){
183            case SINGLE:
184                loadSingleImageViewports();
185                break;
186            case MULTIPLE:
187                // TODO: implement single and double properly @rrh
188                setMultipleImages();
189                break;
190        }
191    }
192    // TODO: change layout - Streetside cubemaps are structured as follows: @rrh
193    /**
194     * 01-front, 02-right, 03-back, 10-left, 11-up, 12-down
195     *
196     *         ____ ____ ____ ____ ____ ____
197     *        |fwd |rght|back|left|top |bot |
198     *        |____|____|____|____|____|____|
199     *
200     */
201
202    private void loadSingleImageViewports(){
203        layoutViews();
204        double width = singleImg.getWidth(),
205               height = singleImg.getHeight();
206
207        //simple check to see if cells will be square
208        if(width / 4 != height / 3){
209            throw new UnsupportedOperationException("Image does not comply with size constraints");
210        }
211        double cellSize = singleImg.getWidth() - singleImg.getHeight();
212
213        recalculateSize(cellSize);
214
215        double
216                topx = cellSize, topy = 0,
217
218                botx = cellSize, boty = cellSize * 2,
219
220                leftx = 0, lefty = cellSize,
221
222                rightx = cellSize * 2, righty = cellSize,
223
224                fwdx = cellSize, fwdy = cellSize,
225
226                backx = cellSize * 3, backy = cellSize;
227
228        //add top padding x+, y+, width-, height
229        up.setViewport(new Rectangle2D(topx , topy , cellSize, cellSize ));
230
231        //add left padding x, y+, width, height-
232        left.setViewport(new Rectangle2D(leftx , lefty , cellSize - 1, cellSize - 1));
233
234        //add front padding x+, y+, width-, height
235        front.setViewport(new Rectangle2D(fwdx , fwdy, cellSize , cellSize));
236
237        //add right padding x, y+, width, height-
238        right.setViewport(new Rectangle2D(rightx, righty , cellSize , cellSize ));
239
240        //add back padding x, y+, width, height-
241        back.setViewport(new Rectangle2D(backx + 1, backy - 1, cellSize - 1, cellSize - 1));
242
243        //add bottom padding x+, y, width-, height-
244        down.setViewport(new Rectangle2D(botx, boty, cellSize , cellSize));
245
246        for(ImageView v : views){
247            v.setImage(singleImg);
248        }
249    }
250
251
252
253    private void recalculateSize(double cell){
254        double factor = Math.floor(getSize() / cell);
255        setSize(cell * factor);
256    }
257
258        public synchronized void setImage(BufferedImage img, int position) {
259                views[position].setImage(GraphicsUtils.convertBufferedImage2JavaFXImage(img));
260
261        }
262
263
264    private void setMultipleImages() {
265                GraphicsUtils.PlatformHelper.run(() -> {
266                        layoutViews();
267                        front.setImage(frontImg);
268                        right.setImage(rightImg);
269                        back.setImage(backImg);
270                        left.setImage(leftImg);
271                        up.setImage(upImg);
272                        down.setImage(downImg);
273
274                });
275        }
276
277    public void startTimer(){
278        timer = new AnimationTimer() {
279            @Override
280            public void handle(long now) {
281                Transform ct = (camera != null) ? camera.getLocalToSceneTransform() : null;
282                if(ct != null){
283                    affine.setTx(ct.getTx());
284                    affine.setTy(ct.getTy());
285                    affine.setTz(ct.getTz());
286                }
287            }
288        };
289        timer.start();
290    }
291
292    /*
293        Properties
294    */
295    private final DoubleProperty size = new SimpleDoubleProperty(){
296        @Override
297        protected void invalidated() {
298            switch(imageType){
299            case SINGLE:
300                layoutViews();
301                break;
302            case MULTIPLE:
303                break;
304            }
305
306        }
307    };
308
309    public final double getSize() {
310        return size.get();
311    }
312
313    public final void setSize(double value) {
314        size.set(value);
315    }
316
317    public DoubleProperty sizeProperty() {
318        return size;
319    }
320
321        public ImageView[] getViews() {
322                return views;
323        }
324}