package com.canoo.cog.ui.city.util; /* * #%L * code-of-gotham * %% * Copyright (C) 2015 Canoo Engineering AG * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import java.io.IOException; import java.util.List; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.beans.property.SimpleStringProperty; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.Scene; import javafx.scene.SceneAntialiasing; import javafx.scene.SubScene; import javafx.scene.input.MouseButton; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javafx.scene.transform.Rotate; import javafx.util.Duration; import com.canoo.cog.solver.CityNode; import com.canoo.cog.ui.city.CityToolBarService; import com.canoo.cog.ui.city.model.Building; import com.canoo.cog.ui.city.model.City; import com.canoo.cog.ui.city.model.Hood; import com.canoo.cog.ui.city.model.style.CityStyle; import com.canoo.cog.ui.city.model.text.Info; public class StageUtil { private static final int SCENE_HEIGHT = 1000; private static final int SCENE_WIDTH = 1500; public static final int TOOLBAR_WIDTH = 150; private final double CONTROL_MULTIPLIER = 0.1; private final double SHIFT_MULTIPLIER = 10.0; private final double MOUSE_SPEED = 0.1; private final double ROTATION_SPEED = 1.0; private final double TRACK_SPEED = 0.3; double mousePosX; double mousePosY; double mouseOldX; double mouseOldY; double mouseDeltaX; double mouseDeltaY; private double CAMERA_INITIAL_DISTANCE = -1200; private double CAMERA_INITIAL_DISTANCE2 = 0; private final double CAMERA_INITIAL_X_ANGLE = -30; private final double CAMERA_INITIAL_Y_ANGLE = 30.0; private final double CAMERA_NEAR_CLIP = 0.1; private final double CAMERA_FAR_CLIP = 10000.0; final Xform cameraXform = new Xform(); final Xform cameraXform2 = new Xform(); final Xform cameraXform3 = new Xform(); public void setTextProperties(Group root, City city, String title) { double depthCorrection = -city.getLayoutBounds().getDepth() / 2; double heightCorrectionTitle = -(SCENE_HEIGHT / 2 - 20); double heightCorrectionInfo = -SCENE_HEIGHT / 4; double widthCorrectionTitle = SCENE_WIDTH / 3 - 10; double widthCorrectionInfo = SCENE_WIDTH / 3; double tileFont = Math.min(city.getLayoutBounds().getDepth() / 6, 42); double txtFont = Math.min(city.getLayoutBounds().getDepth() / 12, 18); Text titleTxt = new Text(title); titleTxt.setFont(Font.font("Helvetica", FontWeight.BOLD, tileFont)); titleTxt.setFill(Color.WHITESMOKE); Text elementInfoTxt = new Text(); elementInfoTxt.setFont(Font.font("Helvetica", FontWeight.BOLD, txtFont)); elementInfoTxt.setFill(Color.WHITESMOKE); elementInfoTxt.textProperty().bind(Info.getInfoProperty()); Group titleGroup = new Group(); titleGroup.getChildren().add(titleTxt); Group elementInfoGroup = new Group(); elementInfoGroup.getChildren().add(elementInfoTxt); root.getChildren().add(titleGroup); root.getChildren().add(elementInfoGroup); titleGroup.setTranslateY(heightCorrectionTitle); titleGroup.setTranslateX(widthCorrectionTitle); elementInfoGroup.setTranslateY(heightCorrectionInfo); elementInfoGroup.setTranslateX(widthCorrectionInfo); } private SimpleStringProperty styleProperty = new SimpleStringProperty(); public static final CityStyle.Style INITIAL_STYLE = CityStyle.Style.GOTHAM; public void setStyle(SubScene subscene) { // initial style styleProperty.bind(CityStyle.getStyleProperty()); styleProperty.addListener(listener -> setBackgroundColor(subscene)); CityStyle.setStyle(INITIAL_STYLE); } private void setBackgroundColor(SubScene scene) { scene.setFill(CityStyle.getBackgroundColor(styleProperty.getValue())); } public City createCity(CityNode resultNode, String cityName) { // Add city to group and translate them according to model int hoodHeight = 1; int potentialHeight = resultNode.getSize() / 400; if (potentialHeight > 1) { hoodHeight = potentialHeight; } City city = new City(hoodHeight, resultNode.getSize(), resultNode.getSize(), cityName); addAllNodesRecursively(city, resultNode.getChildren()); LayoutManager layoutManager = new LayoutManager(); layoutManager.moveCityChildrenBackToCity(city); layoutManager.correctYDirection(city); layoutManager.setRelativeOffset(city); layoutManager.moveCityToChildren(city); city.setTranslateX(city.getWidth() / 2); city.setTranslateZ(city.getWidth() / 2); city.setTranslateY(100); return city; } private static final int HEIGHT_DIVISOR = 3; private void addAllNodesRecursively(Hood hood, List<CityNode> children) { for (CityNode node : children) { String info = node.getModel().getInfo(); if (node.isLeaf()) { double height = node.getModel().getLinesOfCode() / HEIGHT_DIVISOR; hood.addBuilding(new Building(node.getSize(), height, node.getX(), node.getY(), info, node.getModel(), hood.getLevel() + 1)); // Y==Z } else { double incrementedHeight = hood.getHeight(); Hood childHood = new Hood(incrementedHeight, node.getSize(), node.getSize(), node.getX(), node.getY(), info, hood.getLevel() + 1); hood.addHood(childHood); addAllNodesRecursively(childHood, node.getChildren()); } } } public void setMouseEventsToScene(Scene scene, PerspectiveCamera camera) { scene.setOnMousePressed(me -> { mousePosX = me.getSceneX(); mousePosY = me.getSceneY(); mouseOldX = me.getSceneX(); mouseOldY = me.getSceneY(); }); scene.setOnScroll(event -> { if (event.getDeltaY() < 0) { CAMERA_INITIAL_DISTANCE *= 1.1; } else { CAMERA_INITIAL_DISTANCE *= 0.9; } camera.setTranslateZ(CAMERA_INITIAL_DISTANCE); }); scene.setOnMouseDragged(me -> { mouseOldX = mousePosX; mouseOldY = mousePosY; mousePosX = me.getSceneX(); mousePosY = me.getSceneY(); mouseDeltaX = (mousePosX - mouseOldX); mouseDeltaY = (mousePosY - mouseOldY); double modifier = 1.0; if (me.isControlDown()) { modifier = CONTROL_MULTIPLIER; } if (me.isShiftDown()) { modifier = SHIFT_MULTIPLIER; } if (me.isPrimaryButtonDown()) { cameraXform.ry.setAngle(cameraXform.ry.getAngle() - mouseDeltaX * MOUSE_SPEED * modifier * ROTATION_SPEED); cameraXform.rx.setAngle(cameraXform.rx.getAngle() + mouseDeltaY * MOUSE_SPEED * modifier * ROTATION_SPEED); } else if (me.isSecondaryButtonDown()) { double z = camera.getTranslateZ(); double newZ = z + mouseDeltaX * MOUSE_SPEED * modifier; camera.setTranslateZ(newZ); } else if (me.isMiddleButtonDown()) { cameraXform2.t.setX(cameraXform2.t.getX() + mouseDeltaX * MOUSE_SPEED * modifier * TRACK_SPEED); cameraXform2.t.setY(cameraXform2.t.getY() + mouseDeltaY * MOUSE_SPEED * modifier * TRACK_SPEED); } }); scene.setOnKeyPressed(me -> { switch (me.getCode()) { case UP: camera.setTranslateZ(CAMERA_INITIAL_DISTANCE += 10); break; case DOWN: camera.setTranslateZ(CAMERA_INITIAL_DISTANCE -= 10); break; case LEFT: camera.setTranslateX(CAMERA_INITIAL_DISTANCE2 -= 5); break; case RIGHT: camera.setTranslateX(CAMERA_INITIAL_DISTANCE2 += 5); break; } }); scene.setOnMouseClicked(me -> { if (me.getButton() == MouseButton.SECONDARY) { CAMERA_INITIAL_DISTANCE = -1200; camera.setTranslateZ(CAMERA_INITIAL_DISTANCE); cameraXform.ry.setAngle(CAMERA_INITIAL_Y_ANGLE); cameraXform.rx.setAngle(CAMERA_INITIAL_X_ANGLE); } }); } Rotate rxBox = new Rotate(0, 0, 0, 0, Rotate.X_AXIS); Rotate ryBox = new Rotate(0, 0, 0, 0, Rotate.Y_AXIS); Rotate rzBox = new Rotate(0, 0, 0, 0, Rotate.Z_AXIS); private SubScene subScene; public Scene initScene(City city, PerspectiveCamera camera, String cityName) { Group root = new Group(); buildCamera(root, camera); root.getTransforms().addAll(rxBox, ryBox, rzBox); subScene = new SubScene(root, SCENE_WIDTH, SCENE_HEIGHT, true, SceneAntialiasing.BALANCED); subScene.setCamera(camera); root.getChildren().add(city); // 2D BorderPane pane = new BorderPane(); Pane toolBar = null; try { toolBar = new CityToolBarService().load(cityName); } catch (IOException e) { e.printStackTrace(); } pane.setBottom(subScene); pane.setTop(toolBar); Scene scene = new Scene(pane, SCENE_WIDTH, SCENE_HEIGHT); subScene.heightProperty().bind(scene.heightProperty()); subScene.widthProperty().bind(scene.widthProperty()); return scene; } public SubScene getSubScene() { return subScene; } private void buildCamera(Group root, PerspectiveCamera camera) { root.getChildren().add(cameraXform); cameraXform.getChildren().add(cameraXform2); cameraXform2.getChildren().add(cameraXform3); cameraXform3.getChildren().add(camera); camera.setNearClip(CAMERA_NEAR_CLIP); camera.setFarClip(CAMERA_FAR_CLIP); cameraXform.ry.setAngle(CAMERA_INITIAL_Y_ANGLE); cameraXform.rx.setAngle(CAMERA_INITIAL_X_ANGLE); } public void makeTimeLine(PerspectiveCamera camera) { // Timeline for Intro Timeline timeline = new Timeline(); timeline.setCycleCount(1); KeyValue kv = new KeyValue(camera.translateZProperty(), CAMERA_INITIAL_DISTANCE); KeyFrame kf = new KeyFrame(Duration.millis(5000), kv); timeline.getKeyFrames().add(kf); timeline.play(); } }