/*
* Copyright (C) 2013-2015 F(X)yz,
* Sean Phillips, Jason Pollastrini and Jose Pereda
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fxyz.cameras.controllers;
import org.fxyz.utils.AnimationPreference;
import javafx.animation.Animation;
import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.Transition;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.SubScene;
import javafx.scene.input.KeyEvent;
import static javafx.scene.input.MouseButton.MIDDLE;
import static javafx.scene.input.MouseButton.PRIMARY;
import static javafx.scene.input.MouseButton.SECONDARY;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.util.Duration;
import org.fxyz.cameras.AdvancedCamera;
import org.fxyz.extras.Transformable;
/**
*
* @author Dub
*/
public abstract class CameraController implements Transformable {
public AdvancedCamera camera;
private Scene scene;
private SubScene subScene;
private double previousX, previousY, speed = 1.0;
private AnimationTimer timer;
private Timeline timeline;
private Transition transition;
private boolean enable;
private AnimationPreference animPref;
public CameraController(boolean enableTransforms, AnimationPreference movementType) {
enable = enableTransforms;
animPref = movementType;
switch (animPref) {
case TIMELINE:
timeline = new Timeline();
timeline.setCycleCount(Animation.INDEFINITE);
break;
case TIMER:
timer = new AnimationTimer() {
@Override
public void handle(long l) {
if (enable) {
initialize();
enable = false;
}
update();
}
};
break;
case TRANSITION:
transition = new Transition() {
{setCycleDuration(Duration.seconds(1));}
@Override
protected void interpolate(double frac) {
updateTransition(frac);
}
};
transition.setCycleCount(Animation.INDEFINITE);
break;
case ANIMATION:
break;
}
}
//Abstract Methods
protected abstract void update(); // called each frame handle movement/ button clicks here
protected abstract void updateTransition(double now);
// Following methods should update values for use in update method etc...
protected abstract void handleKeyEvent(KeyEvent event, boolean handle);
protected abstract void handlePrimaryMouseDrag(MouseEvent event, Point2D dragDelta, double modifier);
protected abstract void handleMiddleMouseDrag(MouseEvent event, Point2D dragDelta, double modifier);
protected abstract void handleSecondaryMouseDrag(MouseEvent event, Point2D dragDelta, double modifier);
protected abstract void handlePrimaryMouseClick(MouseEvent e);
protected abstract void handleSecondaryMouseClick(MouseEvent e);
protected abstract void handleMiddleMouseClick(MouseEvent e);
protected abstract void handlePrimaryMousePress(MouseEvent e);
protected abstract void handleSecondaryMousePress(MouseEvent e);
protected abstract void handleMiddleMousePress(MouseEvent e);
protected abstract void handleMouseMoved(MouseEvent event, Point2D moveDelta, double modifier);
protected abstract void handleScrollEvent(ScrollEvent event);
protected abstract double getSpeedModifier(KeyEvent event);
//Self contained Methods
private void handleKeyEvent(KeyEvent t) {
if (t.getEventType() == KeyEvent.KEY_PRESSED) {
handleKeyEvent(t, true);
} else if (t.getEventType() == KeyEvent.KEY_RELEASED) {
handleKeyEvent(t, true);
}
speed = getSpeedModifier(t);
}
private void handleMouseEvent(MouseEvent t) {
if (t.getEventType() == MouseEvent.MOUSE_PRESSED) {
switch (t.getButton()) {
case PRIMARY:
handlePrimaryMousePress(t);
break;
case MIDDLE:
handleMiddleMousePress(t);
break;
case SECONDARY:
handleSecondaryMousePress(t);
break;
default:
throw new AssertionError();
}
handleMousePress(t);
} else if (t.getEventType() == MouseEvent.MOUSE_DRAGGED) {
Point2D d = getMouseDelta(t);
switch (t.getButton()) {
case PRIMARY:
handlePrimaryMouseDrag(t, d, speed);
break;
case MIDDLE:
handleMiddleMouseDrag(t, d, speed);
break;
case SECONDARY:
handleSecondaryMouseDrag(t, d, speed);
break;
default:
throw new AssertionError();
}
} else if (t.getEventType() == MouseEvent.MOUSE_MOVED) {
handleMouseMoved(t, getMouseDelta(t), speed);
} else if (t.getEventType() == MouseEvent.MOUSE_CLICKED) {
switch (t.getButton()) {
case PRIMARY:
handlePrimaryMouseClick(t);
break;
case MIDDLE:
handleMiddleMouseClick(t);
break;
case SECONDARY:
handleSecondaryMouseClick(t);
break;
default:
throw new AssertionError();
}
}
}
private void setEventHandlers(Scene scene) {
scene.addEventHandler(KeyEvent.ANY, k -> handleKeyEvent(k));
scene.addEventHandler(MouseEvent.ANY, m -> handleMouseEvent(m));
scene.addEventHandler(ScrollEvent.ANY, s -> handleScrollEvent(s));
}
private void setEventHandlers(SubScene scene) {
scene.addEventHandler(KeyEvent.ANY, k -> handleKeyEvent(k));
scene.addEventHandler(MouseEvent.ANY, m -> handleMouseEvent(m));
scene.addEventHandler(ScrollEvent.ANY, s -> handleScrollEvent(s));
}
private void handleMousePress(MouseEvent event) {
previousX = event.getSceneX();
previousY = event.getSceneY();
event.consume();
}
private Point2D getMouseDelta(MouseEvent event) {
Point2D res = new Point2D(event.getSceneX() - previousX, event.getSceneY() - previousY);
previousX = event.getSceneX();
previousY = event.getSceneY();
return res;
}
public AdvancedCamera getCamera() {
return camera;
}
public void setCamera(AdvancedCamera camera) {
this.camera = camera;
switch (animPref) {
case TIMELINE:
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.millis(15), e -> {
new Timeline(new KeyFrame[]{
new KeyFrame(Duration.ONE, ev ->{
update();
})
}).play();
})
);
timeline.play();
break;
case TIMER:
timer.start();
break;
case TRANSITION:
transition.play();
break;
case ANIMATION:
break;
}
}
public void setScene(Scene scene) {
this.scene = scene;
setEventHandlers(scene);
}
public void setSubScene(SubScene subScene) {
this.subScene = subScene;
setEventHandlers(subScene);
}
protected Scene getScene() {
return scene;
}
protected SubScene getSubScene() {
return subScene;
}
@Override
public RotateOrder getRotateOrder() {
return RotateOrder.USE_AFFINE;
}
}