@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
139 lines (125 loc) • 4.23 kB
text/typescript
import {pushOnArrayAtEntry} from './../../../core/MapUtils';
import {Vector2, Vector3, Box3, Object3D} from 'three';
import {BaseSopOperation} from './_Base';
import {CoreGroup} from '../../../core/geometry/Group';
import {InputCloneMode} from '../../../engine/poly/InputCloneMode';
import {DefaultOperationParams} from '../../../core/operations/_Base';
import {isBooleanTrue} from '../../../core/Type';
import {coreObjectClassFactory} from '../../../core/geometry/CoreObjectFactory';
interface ObjectsLayoutSopParams extends DefaultOperationParams {
maxLayoutWidth: number;
rowHeight: number;
padding: Vector2;
addAttribs: boolean;
addRowAttrib: boolean;
addRowWidthInner: boolean;
addRowWidthOuter: boolean;
}
const currentPos = new Vector2(0, 0);
const maxPos = new Vector2(0, 0);
const box = new Box3();
const boxSize = new Vector3(0, 0);
const objectsByRow: Map<number, Object3D[]> = new Map();
export class ObjectsLayoutSopOperation extends BaseSopOperation {
static override readonly DEFAULT_PARAMS: ObjectsLayoutSopParams = {
maxLayoutWidth: 10,
rowHeight: 1,
padding: new Vector2(0, 0),
addAttribs: false,
addRowAttrib: false,
addRowWidthInner: false,
addRowWidthOuter: false,
};
static override readonly INPUT_CLONED_STATE = InputCloneMode.FROM_NODE;
static override type(): Readonly<'objectsLayout'> {
return 'objectsLayout';
}
override cook(inputCoreGroups: CoreGroup[], params: ObjectsLayoutSopParams) {
const objects = inputCoreGroups[0].threejsObjects();
currentPos.set(0, 0);
maxPos.set(0, 0);
objectsByRow.clear();
let rowIndex = 0;
const addRowWidth = isBooleanTrue(params.addRowWidthInner) || isBooleanTrue(params.addRowWidthOuter);
for (let object of objects) {
// get size before scale adjustment
object.updateMatrix();
box.setFromObject(object);
box.getSize(boxSize);
// scale adjustment
const scaleFactor = params.rowHeight / boxSize.y;
object.scale.multiplyScalar(scaleFactor);
object.updateMatrix();
// get size after scale adjustment
box.setFromObject(object);
box.getSize(boxSize);
// apply padding
boxSize.x += params.padding.x;
boxSize.y += params.padding.y;
// move
currentPos.x += boxSize.x;
// change row if needed
if (currentPos.x > params.maxLayoutWidth) {
currentPos.x = boxSize.x;
currentPos.y -= boxSize.y;
rowIndex++;
}
// move current object
object.position.x = currentPos.x - boxSize.x * 0.5;
object.position.y = currentPos.y - boxSize.y * 0.5;
if (isBooleanTrue(params.addAttribs)) {
if (addRowWidth) {
pushOnArrayAtEntry(objectsByRow, rowIndex, object);
}
if (isBooleanTrue(params.addRowAttrib)) {
coreObjectClassFactory(object).addAttribute(object, 'row', rowIndex);
}
}
maxPos.x = Math.max(maxPos.x, object.position.x + boxSize.x * 0.5);
maxPos.y = Math.min(maxPos.y, object.position.y - boxSize.y * 0.5);
}
// center all objects together
for (let object of objects) {
object.position.x -= maxPos.x * 0.5;
object.position.y -= maxPos.y * 0.5;
object.updateMatrix();
}
// add attribs
if (isBooleanTrue(params.addAttribs)) {
if (addRowWidth) {
objectsByRow.forEach((objects, _) => {
if (isBooleanTrue(params.addRowWidthInner)) {
const xs = objects.map((o) => o.position.x);
const minX = Math.min(...xs);
const maxX = Math.max(...xs);
const width = maxX - minX;
for (let object of objects) {
coreObjectClassFactory(object).addAttribute(object, 'rowWidthInner', width);
}
}
if (isBooleanTrue(params.addRowWidthOuter)) {
const minX = Math.min(
...objects.map((o) => {
box.setFromObject(o);
box.getSize(boxSize);
return o.position.x - boxSize.x * 0.5;
})
);
const maxX = Math.max(
...objects.map((o) => {
box.setFromObject(o);
box.getSize(boxSize);
return o.position.x + boxSize.x * 0.5;
})
);
const width = maxX - minX;
for (let object of objects) {
coreObjectClassFactory(object).addAttribute(object, 'rowWidthOuter', width);
}
}
});
}
}
return this.createCoreGroupFromObjects(objects);
}
}