2017-11-14 15:27:48 +01:00
|
|
|
import update from 'react-addons-update';
|
|
|
|
import { Utils } from 'cal';
|
|
|
|
import * as THREE from 'three';
|
2017-11-14 16:01:04 +01:00
|
|
|
import { SHAPE_TYPE_PROPERTIES } from '../../../constants/shapeTypeProperties.js';
|
|
|
|
import * as d3Tools from '../../../constants/d3Tools.js';
|
2017-11-14 15:27:48 +01:00
|
|
|
import { getSelectedObjectsSelector, getBoundingBox } from '../../../utils/selectionUtils.js';
|
|
|
|
import * as actions from '../../../actions/index.js';
|
|
|
|
// import createDebug from 'debug';
|
|
|
|
// const debug = createDebug('d3d:reducer:height');
|
|
|
|
|
|
|
|
const { MathExtended } = Utils;
|
|
|
|
|
|
|
|
const MIN_HEIGHT = 1;
|
|
|
|
const MAX_HEIGHT = 600;
|
|
|
|
const CHANGE_FACTOR = 0.5;
|
|
|
|
|
|
|
|
export default function heightReducer(state, action) {
|
|
|
|
// if (action.log !== false) debug(action.type);
|
|
|
|
|
|
|
|
switch (action.type) {
|
|
|
|
case actions.HEIGHT_START:
|
|
|
|
case actions.HEIGHT_END: {
|
|
|
|
state = update(state, {
|
|
|
|
d3: {
|
|
|
|
height: {
|
|
|
|
active: { $set: action.type === actions.HEIGHT_START },
|
|
|
|
handle: { $set: action.type === actions.HEIGHT_START ? action.handle : '' }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
case actions.HEIGHT: {
|
|
|
|
const cameraAngle = new THREE.Vector3()
|
|
|
|
.set(action.delta.x, -action.delta.y, 0)
|
|
|
|
.applyMatrix3(new THREE.Matrix3().getNormalMatrix(state.d3.camera.object.matrix));
|
|
|
|
|
|
|
|
const sceneUp = new THREE.Vector3(0, 1, 0)
|
|
|
|
.applyMatrix4(new THREE.Matrix4().extractRotation(state.spaces[state.activeSpace].matrix));
|
|
|
|
|
|
|
|
let delta = cameraAngle.dot(sceneUp) * CHANGE_FACTOR;
|
|
|
|
|
|
|
|
const selectedShapeDatas = getSelectedObjectsSelector(state.selection.objects, state.objectsById);
|
|
|
|
const { min, max } = getBoundingBox(selectedShapeDatas, state.selection.transform);
|
|
|
|
const totalHeight = max.y - min.y;
|
|
|
|
|
|
|
|
state = update(state, {
|
|
|
|
objectsById: state.selection.objects
|
|
|
|
.map(({ id }) => state.objectsById[id])
|
|
|
|
.filter(shapeData => SHAPE_TYPE_PROPERTIES[shapeData.type].tools[d3Tools.HEIGHT])
|
|
|
|
.reduce((updateObject, shapeData) => {
|
|
|
|
let height;
|
|
|
|
let z;
|
|
|
|
switch (state.d3.height.handle) {
|
|
|
|
case 'top': {
|
|
|
|
delta -= Math.max(max.y + delta - MAX_HEIGHT, 0);
|
|
|
|
const scale = (totalHeight + delta) / totalHeight;
|
|
|
|
|
|
|
|
height = shapeData.height * scale;
|
|
|
|
z = (shapeData.z - min.y) * scale + min.y;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'bottom': {
|
|
|
|
delta -= Math.min(min.y + delta, 0);
|
|
|
|
const scale = (totalHeight - delta) / totalHeight;
|
|
|
|
|
|
|
|
height = shapeData.height * scale;
|
|
|
|
z = (shapeData.z - min.y) * scale + min.y + delta;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'translate':
|
|
|
|
height = shapeData.height;
|
|
|
|
z = shapeData.z + delta;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return updateObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
height = MathExtended.clamb(height, MIN_HEIGHT, MAX_HEIGHT - MIN_HEIGHT);
|
|
|
|
z = MathExtended.clamb(z, 0, MAX_HEIGHT - height);
|
|
|
|
|
|
|
|
updateObject[shapeData.UID] = {
|
|
|
|
height: { $set: height },
|
|
|
|
z: { $set: z }
|
|
|
|
};
|
|
|
|
return updateObject;
|
|
|
|
}, {})
|
|
|
|
});
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
}
|