import * as THREE from 'three'; import { shapeToPoints } from '../shape/shapeToPoints.js' import { getPointsBounds } from '../shape/shapeDataUtils.js' import { Vector } from 'cal'; import arrayMemoizer from './arrayMemoizer.js'; import memoize from 'memoizee'; // import createDebug from 'debug'; // const debug = createDebug('d3d:util:selection'); // Memoized selector that returns the same array of shapeSata's when // - the selection array didn't change // - the objects in the resulting array didn't change // enables memoization of utils that use this array export const getSelectedObjectsSelector = arrayMemoizer(getSelectedObjects); // export const getSelectedObjectsSelector = getSelectedObjects; function getSelectedObjects(selectedObjects, objectsById) { return selectedObjects.map(({ id }) => objectsById[id]); } /* * Get the boundingbox of (multiple) shape data's * This generally can by two types of boundingboxes. * 1) The boundingbox of the shapes without the selection transformations (rotation, scale etc). * Requires a selectionTransform matrix. * This is used to display the transformations that are applied to a selection, * showing a for example rotated boundingbox. * Views like the 2D SelectionView will place it over the selection * by applying the selection transformations (translating it to Objects Container Space). * 2) The boundingbox of the shapes with the selection transformations. * This is used to get the axis-aligned bounding box of shapes, this is * usually used to contain the shapes within the drawing area. */ export const getBoundingBox = memoize(getBoundingBoxRaw, { max: 1 }); // export const getBoundingBox = getBoundingBoxRaw; function getBoundingBoxRaw(shapeDatas, selectionTransform) { if (selectionTransform !== undefined) { // To show a rectangle / box around the selection that has the // selection transforms (rotation, scale) of the selection we first have to // remove the selection transforms so we can get the boundingbox of // all the selected shapes as if they where not transformed yet. // We do this by multiplying it by the inverse of the selection transforms. // In the case of one shape the selection transforms equals it's own // transform, but in the case of multiple shapes this only contains the // transformations since the last selection change. const selectionTransformInverse = selectionTransform.inverseMatrix(); shapeDatas = shapeDatas.map(shapeData => ({ ...shapeData, transform: shapeData.transform.multiplyMatrix(selectionTransformInverse) })); } let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; let minZ = Infinity; let maxZ = -Infinity; for (const shapeData of shapeDatas) { const compoundPath = shapeToPoints(shapeData); const { min, max } = getPointsBounds(compoundPath, shapeData.transform); minX = (min.x < minX) ? min.x : minX; minY = (min.y < minY) ? min.y : minY; maxX = (max.x > maxX) ? max.x : maxX; maxY = (max.y > maxY) ? max.y : maxY; const { z, height } = shapeData; minZ = (z < minZ) ? z : minZ; maxZ = (z + height > maxZ) ? z + height : maxZ; } const min = new THREE.Vector3(minX, minZ, minY); const max = new THREE.Vector3(maxX, maxZ, maxY); const center = new Vector(minX, minY).add(new Vector(maxX, maxY)).scale(0.5); return { min, max, center }; }