[WIP] Improve SceneEditor

This commit is contained in:
Florian Rival
2017-01-11 22:49:28 +01:00
parent 88f0bbca8b
commit e30e3cd705
5 changed files with 191 additions and 9 deletions

View File

@@ -77,6 +77,7 @@ class App extends Component {
{
currentProject && currentProject.hasLayoutNamed(sceneOpened) && (
<SceneEditorContainer
key={sceneOpened}
project={currentProject}
layout={currentProject.getLayout(sceneOpened)}
initialInstances={currentProject.getLayout(sceneOpened).getInitialInstances()}

View File

@@ -7,12 +7,20 @@ export default class KeyboardShortcuts {
shouldMultiSelect() { return this.shiftPressed; }
shouldScrollHorizontally() { return this.altPressed; }
shouldZoom() { return this.metaPressed; }
_onKeyDown = (evt) => {
if ( evt.metaKey ) this.metaPressed = true;
if ( evt.altKey ) this.altPressed = true;
if ( evt.which === 17 ) this.ctrlPressed = true;
if ( evt.which === 16 ) this.shiftPressed = true;
}
_onKeyUp = (evt) => {
if ( !evt.metaKey ) this.metaPressed = false;
if ( !evt.altKey ) this.altPressed = false;
if ( evt.which === 17 ) this.ctrlPressed = false;
if ( evt.which === 16 ) this.shiftPressed = false;
}
@@ -27,7 +35,7 @@ export default class KeyboardShortcuts {
unmount() {
if (!document) return;
document.removeEventListener(this._onKeyDown);
document.removeEventListener(this._onKeyUp);
document.removeEventListener('keydown', this._onKeyDown);
document.removeEventListener('keyup', this._onKeyUp);
}
}

View File

@@ -1,12 +1,14 @@
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import gesture from 'pixi-simple-gesture';
import SceneRenderer from './SceneRenderer';
import ViewPosition from './ViewPosition';
import InstancesSelection from './InstancesSelection';
import KeyboardShortcuts from './KeyboardShortcuts';
import HighlightedInstance from './HighlightedInstance';
import SelectionRectangle from './SelectionRectangle';
const gd = global.gd;
const PIXI = global.PIXI;
@@ -26,12 +28,30 @@ export default class SceneEditorContainer extends Component {
}
componentDidMount() {
this.pixiRenderer = PIXI.autoDetectRenderer(500, 500);
this.pixiRenderer = PIXI.autoDetectRenderer(800, 800);
this.refs.canvasArea.appendChild(this.pixiRenderer.view);
this.pixiRenderer.view.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
this.backgroundArea = new PIXI.Container();
this.backgroundArea.hitArea = new PIXI.Rectangle(0, 0, 800, 800); //TODO:Resize
gesture.panable(this.backgroundArea);
this.backgroundArea.on('mousedown', this._onBackgroundClicked);
this.backgroundArea.on('panmove', (event) => this._onMakeSelectionRectangle(event.data.global.x, event.data.global.y));
this.backgroundArea.on('panend', (event) => this._onEndSelectionRectangle());
this.pixiRenderer.view.onmousewheel = (event) => {
if (this.keyboardShortcuts.shouldZoom()) {
this.viewPosition.zoomBy(event.wheelDelta / 5000);
} else if (this.keyboardShortcuts.shouldScrollHorizontally()) {
this.viewPosition.scrollBy(-event.wheelDelta / 20, 0);
} else {
this.viewPosition.scrollBy(0, -event.wheelDelta / 20);
}
event.preventDefault();
};
this.pixiContainer = new PIXI.Container();
this.viewPosition = new ViewPosition({
onPanMoveView: this._onPanMoveView,
@@ -46,6 +66,12 @@ export default class SceneEditorContainer extends Component {
onOutInstance: this._onOutInstance,
onInstanceClicked: this._onInstanceClicked,
});
this.selectionRectangle = new SelectionRectangle({
instances: this.props.initialInstances,
getInstanceWidth: this.sceneRenderer.getInstanceWidth,
getInstanceHeight: this.sceneRenderer.getInstanceHeight,
toSceneCoordinates: this.viewPosition.toSceneCoordinates,
});
this.instancesSelection = new InstancesSelection({
getInstanceWidth: this.sceneRenderer.getInstanceWidth,
getInstanceHeight: this.sceneRenderer.getInstanceHeight,
@@ -56,7 +82,9 @@ export default class SceneEditorContainer extends Component {
});
this.keyboardShortcuts = new KeyboardShortcuts();
this.pixiContainer.addChild(this.backgroundArea);
this.pixiContainer.addChild(this.viewPosition.getPixiContainer());
this.pixiContainer.addChild(this.selectionRectangle.getPixiObject());
this.viewPosition.getPixiContainer().addChild(this.sceneRenderer.getPixiContainer());
this.viewPosition.getPixiContainer().addChild(this.highlightedInstance.getPixiObject());
this.viewPosition.getPixiContainer().addChild(this.instancesSelection.getPixiContainer());
@@ -76,6 +104,21 @@ export default class SceneEditorContainer extends Component {
throw new Error("Changing project/layout/initialInstances is not supported yet")
}
_onBackgroundClicked = () => {
console.log("Background clicked");
if (!this.keyboardShortcuts.shouldMultiSelect())
this.instancesSelection.clearSelection();
}
_onMakeSelectionRectangle = (x, y) => {
this.selectionRectangle.makeSelectionRectangle(x, y);
}
_onEndSelectionRectangle = () => {
const instancesSelected = this.selectionRectangle.endSelectionRectangle();
instancesSelected.forEach(instance => this.instancesSelection.selectInstance(instance));
}
_onInstanceClicked = (instance) => {
if (!this.keyboardShortcuts.shouldMultiSelect())
this.instancesSelection.clearSelection();
@@ -139,6 +182,7 @@ export default class SceneEditorContainer extends Component {
this.sceneRenderer.render();
this.highlightedInstance.render();
this.instancesSelection.render();
this.selectionRectangle.render();
this.pixiRenderer.render(this.pixiContainer);
this.nextFrame = requestAnimationFrame(this.renderScene);
}
@@ -150,6 +194,7 @@ export default class SceneEditorContainer extends Component {
return (
<div>
<RaisedButton label="Delete selection" onClick={this.deleteSelection} />
<RaisedButton label="Move" onClick={() => this.viewPosition.scrollBy(50, 40)} />
<div ref="canvasArea" />
</div>
)

View File

@@ -0,0 +1,87 @@
const gd = global.gd;
const PIXI = global.PIXI;
import gesture from 'pixi-simple-gesture';
export default class SelectionRectangle {
constructor({instances, getInstanceWidth, getInstanceHeight, toSceneCoordinates, toCanvasCoordinates}) {
this.instances = instances;
this.getInstanceWidth = getInstanceWidth;
this.getInstanceHeight = getInstanceHeight;
this.toSceneCoordinates = toSceneCoordinates;
this.pixiRectangle = new PIXI.Graphics();
this.pixiRectangle.hitArea = new PIXI.Rectangle(0, 0, 0, 0);
this.selectionRectangleStart = null;
this.selectionRectangleEnd = null;
this._instancesInSelectionRectangle = [];
this.selector = new gd.InitialInstanceJSFunctor();
this.selector.invoke = (instancePtr) => {
const instance = gd.wrapPointer(instancePtr, gd.InitialInstance);
const x = instance.getX();
const y = instance.getY();
const instanceHeight = this.getInstanceHeight(instance);
const instanceWidth = this.getInstanceWidth(instance);
const selectionSceneStart = toSceneCoordinates(this.selectionRectangleStart.x, this.selectionRectangleStart.y);
const selectionSceneEnd = toSceneCoordinates(this.selectionRectangleEnd.x, this.selectionRectangleEnd.y);
if (selectionSceneStart[0] <= x && x + instanceWidth <= selectionSceneEnd[0] &&
selectionSceneStart[1] <= y && y + instanceHeight <= selectionSceneEnd[1]) {
this._instancesInSelectionRectangle.push(instance);
}
};
}
makeSelectionRectangle = (lastX, lastY) => {
if (!this.selectionRectangleStart)
this.selectionRectangleStart = {x: lastX, y: lastY};
this.selectionRectangleEnd = {x: lastX, y: lastY};
}
endSelectionRectangle = () => {
this._instancesInSelectionRectangle.length = 0;
if (this.selectionRectangleStart.x > this.selectionRectangleEnd.x) {
const tmp = this.selectionRectangleStart.x;
this.selectionRectangleStart.x = this.selectionRectangleEnd.x;
this.selectionRectangleEnd.x = tmp;
}
if (this.selectionRectangleStart.y > this.selectionRectangleEnd.y) {
const tmp = this.selectionRectangleStart.y;
this.selectionRectangleStart.y = this.selectionRectangleEnd.y;
this.selectionRectangleEnd.y = tmp;
}
this.instances.iterateOverInstances(this.selector);
this.selectionRectangleStart = null;
return this._instancesInSelectionRectangle;
}
getPixiObject() {
return this.pixiRectangle;
}
render() {
if (!this.selectionRectangleStart) {
this.pixiRectangle.visible = false;
return;
}
let x1 = this.selectionRectangleStart.x;
let y1 = this.selectionRectangleStart.y;
let x2 = this.selectionRectangleEnd.x;
let y2 = this.selectionRectangleEnd.y;
this.pixiRectangle.visible = true;
this.pixiRectangle.clear();
this.pixiRectangle.beginFill(0x6868E8);
this.pixiRectangle.lineStyle(1, 0x6868E8, 1);
this.pixiRectangle.fillAlpha = 0.1;
this.pixiRectangle.alpha = 0.8;
this.pixiRectangle.drawRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x2 - x1), Math.abs(y2 - y1));
this.pixiRectangle.endFill();
}
}

View File

@@ -5,9 +5,44 @@ export default class ViewPosition {
constructor({onPanMoveView}) {
this.viewX = 0;
this.viewY = 0;
this.pixiContainer = new PIXI.Container();
gesture.panable(this.pixiContainer);
this.pixiContainer.on('panmove', (event) => onPanMoveView(event.deltaX, event.deltaY));
this._zoomFactor = 1;
this._pixiContainer = new PIXI.Container();
// gesture.panable(this._pixiContainer);
// this._pixiContainer.on('panmove', (event) => onPanMoveView(event.deltaX, event.deltaY));
}
/**
* Convert a point from the canvas coordinates (For example, the mouse position) to the
* "world" coordinates.
*/
toSceneCoordinates = (x, y) => {
x /= Math.abs(this._pixiContainer.scale.x);
y /= Math.abs(this._pixiContainer.scale.y);
var viewRotation = 0;
var tmp = x;
x = Math.cos(viewRotation/180*3.14159)*x - Math.sin(viewRotation/180*3.14159)*y;
y = Math.sin(viewRotation/180*3.14159)*tmp + Math.cos(viewRotation/180*3.14159)*y;
return [x+this.viewX, y+this.viewY];
}
/**
* Convert a point from the "world" coordinates (For example, an object position) to the
* canvas coordinates.
*/
toCanvasCoordinates = (x, y) => {
var viewRotation = -0;
var tmp = x;
x = Math.cos(viewRotation/180*3.14159)*x - Math.sin(viewRotation/180*3.14159)*y;
y = Math.sin(viewRotation/180*3.14159)*tmp + Math.cos(viewRotation/180*3.14159)*y;
x *= Math.abs(this._pixiContainer.scale.x);
y *= Math.abs(this._pixiContainer.scale.y);
return [x-this.viewX, y-this.viewY];
}
scrollBy(x, y) {
@@ -15,12 +50,18 @@ export default class ViewPosition {
this.viewY += y;
}
zoomBy(value) {
this._zoomFactor = Math.max(Math.min(this._zoomFactor + value, 10), 0.05);
}
getPixiContainer() {
return this.pixiContainer;
return this._pixiContainer;
}
render() {
this.pixiContainer.position.x = -this.viewX;
this.pixiContainer.position.y = -this.viewY;
this._pixiContainer.position.x = -this.viewX;
this._pixiContainer.position.y = -this.viewY;
this._pixiContainer.scale.x = this._zoomFactor;
this._pixiContainer.scale.y = this._zoomFactor;
}
}