Doodle3D-Core/src/reducer/selectionReducer.js

150 lines
3.7 KiB
JavaScript

import update from 'react-addons-update';
import * as actions from '../actions/index.js';
import { Vector } from '@doodle3d/cal';
import { shapeToPoints } from '../shape/shapeToPoints.js';
import createDebug from 'debug';
const debug = createDebug('d3d:reducer:selection');
export default function selectionReducer(state, action) {
// if (action.log !== false) debug(action.type);
switch (action.type) {
case actions.SELECT: {
const { shapeID } = action;
return selectObject(state, shapeID);
}
case actions.BED_SELECT: {
return update(state, {
activeSpace: { $set: 'world' }
});
}
case actions.DESELECT: {
const { shapeID } = action;
return deselectObject(state, shapeID);
}
case actions.TOGGLE_SELECT: {
const { shapeID } = action;
return toggleSelectObject(state, shapeID);
}
case actions.SELECT_ALL: {
return state.spaces.world.objectIds.reduce((_state, id) => selectObject(_state, id), state);
}
case actions.D2_CHANGE_TOOL:
case actions.DESELECT_ALL:
return deselectAll(state);
case actions.DRAG_SELECT:
const { start, end } = state.d2.transform.dragSelect;
const matrix = action.screenMatrixZoom.inverseMatrix();
const min = new Vector(Math.min(start.x, end.x), Math.min(start.y, end.y)).applyMatrix(matrix);
const max = new Vector(Math.max(start.x, end.x), Math.max(start.y, end.y)).applyMatrix(matrix);
return addSelectFromBoundingBox(state, min, max);
default:
return state;
}
}
function addSelectFromBoundingBox(state, min, max) {
for (const id of state.spaces[state.activeSpace].objectIds) {
if (isSelected(state, id)) continue;
const shapeData = state.objectsById[id];
const compoundPaths = shapeToPoints(shapeData);
const points = compoundPaths.reduce((a, { points: b }) => a.concat(b), []);
for (let point of points) {
point = point.applyMatrix(shapeData.transform);
if (point.x > min.x && point.y > min.y && point.x < max.x && point.y < max.y) {
state = selectObject(state, id);
break;
}
}
}
return state;
}
function isSelected(state, shapeUID) {
return state.selection.objects
.map(({ id }) => id)
.indexOf(shapeUID) !== -1;
}
function selectObject(state, id) {
debug('select: ', id);
if (isSelected(state, id)) return state;
const { space } = state.objectsById[id];
const objects = state.selection.objects
.filter(object => state.objectsById[object.id].space === space);
objects.push({ id });
return update(state, {
activeSpace: { $set: space },
selection: {
objects: { $set: objects }
}
});
}
function deselectObject(state, id) {
debug('deselect: ', id);
if (!isSelected(state, id)) return state;
const index = state.selection.objects.map((object) => object.id).indexOf(id);
return update(state, {
selection: {
objects: {
$splice: [[index, 1]]
}
}
});
}
function toggleSelectObject(state, id) {
if (isSelected(state, id)) {
return deselectObject(state, id);
} else {
return selectObject(state, id);
}
}
function deselectAll(state) {
debug('deselect all');
if (state.selection.objects.length === 0) {
return state;
} else {
return update(state, {
selection: {
objects: { $set: [] }
}
});
}
}
export function updateColor(state, color) {
return update(state, {
objectsById: state.selection.objects.reduce((updateObject, { id }) => {
updateObject[id] = {
color: { $set: color },
solid: { $set: true }
};
return updateObject;
}, {}),
context: {
solid: { $set: true },
color: { $set: color }
}
});
}