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