@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
285 lines (264 loc) • 8.94 kB
text/typescript
import {BaseSopOperation} from './_Base';
import {CoreGroup} from '../../../core/geometry/Group';
import {BufferAttribute, Vector2, Vector3, Vector4} from 'three';
import {ATTRIBUTE_CLASSES, AttribClass, AttribType, ATTRIBUTE_TYPES} from '../../../core/geometry/Constant';
import {InputCloneMode} from '../../../engine/poly/InputCloneMode';
import {TypeAssert} from '../../../engine/poly/Assert';
import {BaseCoreObject} from '../../../core/geometry/entities/object/BaseCoreObject';
import {CoreAttribute} from '../../../core/geometry/Attribute';
import {DefaultOperationParams} from '../../../core/operations/_Base';
import {CoreObjectType, ObjectContent} from '../../../core/geometry/ObjectContent';
import {corePointClassFactory} from '../../../core/geometry/CoreObjectFactory';
import {pointsFromObject} from '../../../core/geometry/entities/point/CorePointUtils';
import {CorePoint} from '../../../core/geometry/entities/point/CorePoint';
interface AttribSetAtIndexSopParams extends DefaultOperationParams {
index: number;
class: number;
type: number;
name: string;
size: number;
value1: number;
value2: Vector2;
value3: Vector3;
value4: Vector4;
string: string;
}
const _points: CorePoint<CoreObjectType>[] = [];
export class AttribSetAtIndexSopOperation extends BaseSopOperation {
static override readonly DEFAULT_PARAMS: AttribSetAtIndexSopParams = {
index: 0,
class: ATTRIBUTE_CLASSES.indexOf(AttribClass.POINT),
type: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC),
name: 'new_attrib',
size: 1,
value1: 0,
value2: new Vector2(0, 0),
value3: new Vector3(0, 0, 0),
value4: new Vector4(0, 0, 0, 0),
string: '',
};
static override readonly INPUT_CLONED_STATE = InputCloneMode.FROM_NODE;
static override type(): Readonly<'attribSetAtIndex'> {
return 'attribSetAtIndex';
}
override cook(inputCoreGroups: CoreGroup[], params: AttribSetAtIndexSopParams) {
const coreGroup = inputCoreGroups[0];
const attribName = params.name;
if (attribName && attribName.trim() != '') {
this._addAttribute(ATTRIBUTE_CLASSES[params.class], coreGroup, params);
} else {
this.states?.error.set('attribute name is not valid');
}
return coreGroup;
}
private _addAttribute(attribClass: AttribClass, coreGroup: CoreGroup, params: AttribSetAtIndexSopParams) {
const attribType = ATTRIBUTE_TYPES[params.type];
switch (attribClass) {
case AttribClass.POINT:
this._addPointAttribute(attribType, coreGroup, params);
return;
case AttribClass.VERTEX:
this.states?.error.set('vertex not supported yet');
return;
case AttribClass.PRIMITIVE:
this.states?.error.set('primitive not supported yet');
return;
case AttribClass.OBJECT:
this._addObjectAttribute(attribType, coreGroup, params);
return;
case AttribClass.CORE_GROUP:
this._addCoreGroupAttribute(attribType, coreGroup, params);
return;
}
TypeAssert.unreachable(attribClass);
}
private _addPointAttribute(attribType: AttribType, coreGroup: CoreGroup, params: AttribSetAtIndexSopParams) {
const objects = coreGroup.allObjects();
switch (attribType) {
case AttribType.NUMERIC: {
for (let object of objects) {
this._addNumericAttributeToPoints(object, params);
}
return;
}
case AttribType.STRING: {
for (let object of objects) {
this._addStringAttributeToPoints(object, params);
}
return;
}
}
TypeAssert.unreachable(attribType);
}
private _addObjectAttribute(attribType: AttribType, coreGroup: CoreGroup, params: AttribSetAtIndexSopParams) {
const allCoreObjects = coreGroup.allCoreObjects();
// add attrib if non existent
const attribName = params.name;
const defaultValue = AttribSetAtIndexSopOperation.defaultAttribValue(params);
if (defaultValue != null) {
for (let coreObject of allCoreObjects) {
if (!coreObject.hasAttribute(attribName)) {
coreObject.setAttribValue(attribName, defaultValue);
}
}
}
const coreObject = allCoreObjects[params.index];
if (!coreObject) {
return;
}
switch (attribType) {
case AttribType.NUMERIC:
this._addNumericAttributeToObject(coreObject, params);
return;
case AttribType.STRING:
this._addStringAttributeToObject(coreObject, params);
return;
}
TypeAssert.unreachable(attribType);
}
private _addCoreGroupAttribute(attribType: AttribType, coreGroup: CoreGroup, params: AttribSetAtIndexSopParams) {
switch (attribType) {
case AttribType.NUMERIC:
this._addNumericAttributeToCoreGroup(coreGroup, params);
return;
case AttribType.STRING:
this._addStringAttributeToCoreGroup(coreGroup, params);
return;
}
TypeAssert.unreachable(attribType);
}
private _addNumericAttributeToPoints<T extends CoreObjectType>(
object: ObjectContent<T>,
params: AttribSetAtIndexSopParams
) {
const corePointClass = corePointClassFactory(object);
const attribName = CoreAttribute.remapName(params.name);
if (!corePointClass.hasAttribute(object, attribName)) {
corePointClass.addNumericAttribute(object, attribName, params.size, 0);
}
const attrib = corePointClass.attribute(object, attribName) as BufferAttribute;
const array = attrib.array;
const {index, size} = params;
switch (size) {
case 1: {
if (index < array.length) {
array[index] = params.value1;
attrib.needsUpdate = true;
}
break;
}
case 2: {
const i2 = index * 2;
if (i2 < array.length) {
params.value2.toArray(array, i2);
attrib.needsUpdate = true;
}
break;
}
case 3: {
const i3 = index * 3;
if (i3 < array.length) {
params.value3.toArray(array, i3);
attrib.needsUpdate = true;
}
break;
}
case 4: {
const i4 = index * 4;
if (i4 < array.length) {
params.value4.toArray(array, i4);
attrib.needsUpdate = true;
}
break;
}
}
}
private _addNumericAttributeToObject(
coreObject: BaseCoreObject<CoreObjectType>,
params: AttribSetAtIndexSopParams
) {
const value = [params.value1, params.value2, params.value3, params.value4][params.size - 1];
const attribName = params.name;
coreObject.setAttribValue(attribName, value);
}
private _addNumericAttributeToCoreGroup(coreGroup: CoreGroup, params: AttribSetAtIndexSopParams) {
const value = [params.value1, params.value2, params.value3, params.value4][params.size - 1];
const attribName = params.name;
coreGroup.setAttribValue(attribName, value);
}
private _addStringAttributeToPoints<T extends CoreObjectType>(
object: ObjectContent<T>,
params: AttribSetAtIndexSopParams
) {
const corePointClass = corePointClassFactory(object);
const attribName = params.name;
// create attrib if non existent
if (!corePointClass.hasAttribute(object, attribName)) {
const tmpIndexData = CoreAttribute.arrayToIndexedArrays(['']);
corePointClass.setIndexedAttribute(object, attribName, tmpIndexData['values'], tmpIndexData['indices']);
}
const value = params.string;
pointsFromObject(object, _points);
const indexPoint = _points[params.index];
let stringValues: string[] = new Array(_points.length);
// We prefill the existing stringValues
// const allPoints = coreObject.points();
stringValues = stringValues.length != _points.length ? new Array(_points.length) : stringValues;
for (const point of _points) {
let currentValue = point.stringAttribValue(attribName);
if (currentValue == null) {
currentValue = '';
}
stringValues[point.index()] = currentValue;
}
if (indexPoint) {
stringValues[indexPoint.index()] = value;
}
const indexData = CoreAttribute.arrayToIndexedArrays(stringValues);
corePointClass.setIndexedAttribute(object, attribName, indexData['values'], indexData['indices']);
}
private _addStringAttributeToObject(coreObject: BaseCoreObject<CoreObjectType>, params: AttribSetAtIndexSopParams) {
const value = params.string;
coreObject.setAttribValue(params.name, value);
}
private _addStringAttributeToCoreGroup(coreGroup: CoreGroup, params: AttribSetAtIndexSopParams) {
const value = params.string;
coreGroup.setAttribValue(params.name, value);
}
//
//
// INTERNAL UTILS
//
//
private static _attribType(params: AttribSetAtIndexSopParams) {
return ATTRIBUTE_TYPES[params.type];
}
static defaultAttribValue(params: AttribSetAtIndexSopParams) {
const attribType = this._attribType(params);
switch (attribType) {
case AttribType.NUMERIC: {
return this._defaultNumericValue(params);
}
case AttribType.STRING: {
return this._defaultStringValue();
}
}
TypeAssert.unreachable(attribType);
}
private static _defaultStringValue() {
return '';
}
private static _defaultNumericValue(params: AttribSetAtIndexSopParams) {
const size = params.size;
switch (size) {
case 1:
return 0;
case 2:
return new Vector2(0, 0);
case 3:
return new Vector3(0, 0, 0);
case 4:
return new Vector4(0, 0, 0, 0);
}
}
}