UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

661 lines (632 loc) 21.2 kB
import {AttribValue, Number3, NumericAttribValue, PolyDictionary} from '../../../../types/GlobalTypes'; import {Box3, Color, Matrix4, Sphere, Vector2, Vector3, Vector4} from 'three'; import {Attribute, CoreAttribute} from '../../Attribute'; import {AttribType, AttribSize, ObjectData, GroupString, AttribClass} from '../../Constant'; import {CoreEntity, CoreEntityWithObject} from '../../CoreEntity'; import {isArray,isVector,isColor,isString} from '../../../Type'; import { ObjectContent, CoreObjectType, ObjectGeometryMap, MergeCompactOptions, ObjectBuilder, } from '../../ObjectContent'; import {TransformTargetType} from '../../../Transform'; import {ObjectTransformMode, ObjectTransformSpace} from '../../../TransformSpace'; import {EntityGroupCollection} from '../../EntityGroupCollection'; import {_updateObjectAttribRef} from '../../../reactivity/ObjectAttributeReactivityUpdateRef'; import {AttributeDictionary} from '../object/BaseCoreObjectUtils'; import {attribValueNonPrimitive, copyAttribValue, cloneAttribValue, uniqRelatedEntityIds} from '../utils/Common'; import {getOrCreateObjectAttributeRef} from '../../../reactivity/ObjectAttributeReactivityCreateRef'; import { JsIConnectionPointTypeToDataTypeMap, ParamConvertibleJsType, } from '../../../../engine/nodes/utils/io/connections/Js'; import {watch} from '../../../reactivity/CoreReactivity'; import {objectData} from './BaseCoreObjectUtils'; import {TypeAssert} from '../../../../engine/poly/Assert'; // import {uniqRelatedEntityIds} from '../utils/Common'; import type {CoreGroup} from '../../Group'; import type {CorePrimitive} from '../primitive/CorePrimitive'; import {CoreVertex} from '../vertex/CoreVertex'; import {CorePoint} from '../point/CorePoint'; import {TraversedRelatedEntityData} from '../utils/TraversedRelatedEntities'; enum PropertyName { NAME = 'name', POSITION = 'position', } const ATTRIBUTES = 'attributes'; // const _relatedPoints: CorePoint<CoreObjectType>[] = []; // const _relatedVertices: CoreVertex<CoreObjectType>[] = []; // const _relatedPrimitives: CorePrimitive<CoreObjectType>[] = []; // const ATTRIBUTES_PREVIOUS_VALUES = 'attributesPreviousValues'; const ORIGIN = new Vector3(0, 0, 0); function _convertArrayToVector(value: number[]) { switch (value.length) { case 1: return value[0]; case 2: return new Vector2(value[0], value[1]); case 3: return new Vector3(value[0], value[1], value[2]); case 4: return new Vector4(value[0], value[1], value[2], value[3]); } } const tmpVec3 = new Vector3(); const tmpN3: Number3 = [0, 0, 0]; type OnAttribChange<T extends ParamConvertibleJsType> = ( newValue: JsIConnectionPointTypeToDataTypeMap[T], oldValue: JsIConnectionPointTypeToDataTypeMap[T] ) => void; const _relatedPrimitiveIds: number[] = []; const _relatedVertexIds: number[] = []; export abstract class BaseCoreObject<T extends CoreObjectType> extends CoreEntityWithObject<T> { dispose() {} // set_index(i: number) { // this._index = i; // } geometry(): ObjectGeometryMap[T] | null { return this._object?.geometry || null; //(this._object as Mesh).geometry as BufferGeometry | null; } builder<T extends CoreObjectType>(): ObjectBuilder<T> | undefined { return undefined; } static attributeRef<OT extends CoreObjectType, T extends ParamConvertibleJsType>( object: ObjectContent<OT>, attribName: string, type: T, defaultValue: JsIConnectionPointTypeToDataTypeMap[T] ) { return getOrCreateObjectAttributeRef(object, attribName, type, defaultValue); } attributeRef<T extends ParamConvertibleJsType>( attribName: string, type: T, defaultValue: JsIConnectionPointTypeToDataTypeMap[T] ) { if (!this._object) { return; } return (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).attributeRef( this._object, attribName, type, defaultValue ); } static onAttribChange<OT extends CoreObjectType, T extends ParamConvertibleJsType>( object: ObjectContent<OT>, attribName: string, type: T, defaultValue: JsIConnectionPointTypeToDataTypeMap[T], callback: OnAttribChange<T> ) { const ref = this.attributeRef(object, attribName, type, defaultValue); return watch(ref.current, callback); } onAttribChange<T extends ParamConvertibleJsType>( attribName: string, type: T, defaultValue: JsIConnectionPointTypeToDataTypeMap[T], callback: OnAttribChange<T> ) { if (!this._object) { return; } return (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).onAttribChange( this._object, attribName, type, defaultValue, callback ); } static setAttribute<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string, value: AttribValue) { this.addAttribute(object, attribName, value); } static addAttribute<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string, value: AttribValue) { if (isArray(value)) { const convertedValue = _convertArrayToVector(value); if (!convertedValue) { const message = `value invalid`; console.error(message, value); throw new Error(message); } } const dict = this._attributesDictionary(object); const currentValue = dict[attribName]; if (attribValueNonPrimitive(value)) { if (currentValue == null) { const cloned = cloneAttribValue(value); if (cloned) { dict[attribName] = cloned; } } else { if (attribValueNonPrimitive(currentValue)) { copyAttribValue(value, currentValue); } } } else { dict[attribName] = value; } _updateObjectAttribRef(object, attribName, value); } static addNumericAttribute<T extends CoreObjectType>( object: ObjectContent<T>, attribName: string, size: AttribSize = 1, defaultValue: NumericAttribValue = 0 ) { this.addAttribute(object, attribName, defaultValue); } addAttribute(name: string, value: AttribValue) { if (!this._object) { return; } (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).addAttribute(this._object, name, value); } addNumericAttrib(name: string, value: NumericAttribValue) { if (!this._object) { return; } (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).addNumericAttribute( this._object, name, 1, value ); } setAttribValue(name: string, value: AttribValue) { this.addAttribute(name, value); } // addNumericVertexAttrib(name: string, size: number, defaultValue: NumericAttribValue) { // // if (defaultValue == null) { // // defaultValue = CoreAttribute.default_value(size); // // } // // this.coreGeometry()?.addNumericAttrib(name, size, defaultValue); // } protected static _attributesDictionary<T extends CoreObjectType>(object: ObjectContent<T>) { return (object.userData[ATTRIBUTES] as AttributeDictionary) || this._createAttributesDictionaryIfNone(object); } static attributesDictionaryEntry<T extends CoreObjectType>( object: ObjectContent<T>, attribName: string, defaultValue?: AttribValue ) { const dict = (object.userData[ATTRIBUTES] as AttributeDictionary) || this._createAttributesDictionaryIfNone(object); let entry: AttribValue | undefined = dict[attribName]; if (entry == null && defaultValue != null) { entry = defaultValue; dict[attribName] = entry; } return entry; } // static attributesPreviousValuesDictionary<T extends CoreObjectType>(object: ObjectContent<T>) { // return ( // (object.userData[ATTRIBUTES_PREVIOUS_VALUES] as AttributeDictionary) || // this._createAttributesPreviousValuesDictionaryIfNone(object) // ); // } private static _createAttributesDictionaryIfNone<T extends CoreObjectType>(object: ObjectContent<T>) { if (!object.userData[ATTRIBUTES]) { return (object.userData[ATTRIBUTES] = {}); } } // private static _createAttributesPreviousValuesDictionaryIfNone<T extends CoreObjectType>(object: ObjectContent<T>) { // if (!object.userData[ATTRIBUTES_PREVIOUS_VALUES]) { // return (object.userData[ATTRIBUTES_PREVIOUS_VALUES] = {}); // } // } private _attributesDictionary() { return (this.constructor as typeof BaseCoreObject<CoreObjectType>)._attributesDictionary(this._object); } static attributes<T extends CoreObjectType>(object: ObjectContent<T>): AttributeDictionary | undefined { return this._attributesDictionary(object); } attributes(): AttributeDictionary | undefined { if (!this._object) { return; } return (this.constructor as typeof BaseCoreObject<T>).attributes(this._object); } attributeNames(): string[] { return this.attribNames(); } static attribNames<T extends CoreObjectType>(object: ObjectContent<T>): string[] { return Object.keys(this._attributesDictionary(object)); } attribNames(): string[] { return (this.constructor as typeof BaseCoreObject<CoreObjectType>).attribNames(this._object); } static hasAttribute<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string) { return attribName in this._attributesDictionary(object); } hasAttribute(attribName: string): boolean { return (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).hasAttribute( this._object, attribName ); } static attributeNames<T extends CoreObjectType>(object: ObjectContent<T>): string[] { const attributes = this.attributes(object); if (!attributes) { return []; } return Object.keys(attributes); } static attributeNamesMatchingMask<T extends CoreObjectType>(object: ObjectContent<T>, masksString: GroupString) { return CoreAttribute.attribNamesMatchingMask(masksString, this.attributeNames(object)); } renameAttribute(oldName: string, newName: string) { return (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).renameAttribute( this._object, oldName, newName ); } static renameAttribute<T extends CoreObjectType>(object: ObjectContent<T>, oldName: string, newName: string) { const currentValue = this.attribValue(object, oldName); if (currentValue != null) { this.addAttribute(object, newName, currentValue); this.deleteAttribute(object, oldName); } else { console.warn(`attribute ${oldName} not found`); } } deleteAttribute(name: string) { delete this._attributesDictionary()[name]; } static deleteAttribute<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string) { delete this._attributesDictionary(object)[attribName]; } // static position:PositionStaticMethod<CoreObjectType> = DEFAULT_POSITION_STATIC_METHOD static position(object: ObjectContent<CoreObjectType>, target: Vector3) { target.copy(ORIGIN); } position(target: Vector3): Vector3 { (this.constructor as typeof BaseCoreObject<CoreObjectType>).position(this._object, target); return target; } static boundingBox(object: ObjectContent<CoreObjectType>, target: Box3) { target.makeEmpty(); } boundingBox(target: Box3) { (this.constructor as typeof BaseCoreObject<CoreObjectType>).boundingBox(this._object, target); } static geometryBoundingBox(object: ObjectContent<CoreObjectType>, target: Box3) { this.boundingBox(object, target); } geometryBoundingBox(target: Box3) { (this.constructor as typeof BaseCoreObject<CoreObjectType>).geometryBoundingBox(this._object, target); } static boundingSphere(object: ObjectContent<CoreObjectType>, target: Sphere) { target.makeEmpty(); } boundingSphere(target: Sphere) { (this.constructor as typeof BaseCoreObject<CoreObjectType>).boundingSphere(this._object, target); } static geometryBoundingSphere(object: ObjectContent<CoreObjectType>, target: Sphere) { target.makeEmpty(); } geometryBoundingSphere(target: Sphere) { (this.constructor as typeof BaseCoreObject<CoreObjectType>).geometryBoundingSphere(this._object, target); } static attribValue<T extends CoreObjectType>( object: ObjectContent<T>, attribName: string, index: number = 0, target?: Color | Vector2 | Vector3 | Vector4 ): AttribValue | undefined { const _attribFromProperty = () => { if (attribName == PropertyName.NAME) { return object.name; } if (attribName == PropertyName.POSITION) { const _target = target instanceof Vector3 ? target : tmpVec3; this.position(object, _target); _target.toArray(tmpN3); return tmpN3; } }; if (attribName === Attribute.OBJECT_INDEX) { return index; } if (attribName === Attribute.OBJECT_NAME) { return object.name; } if (object.userData) { const val = this.attributesDictionaryEntry(object, attribName); // const val = attribRef.value; //dict[attribName]; if (val == null) { return _attribFromProperty(); } else { // const val = _ref.value; if (isVector(val) && target) { if (val instanceof Vector2 && target instanceof Vector2) { return target.copy(val); } if (val instanceof Vector3 && target instanceof Vector3) { return target.copy(val); } if (val instanceof Vector4 && target instanceof Vector4) { return target.copy(val); } } if (isColor(val) && target) { if (val instanceof Color && target instanceof Color) { return target.copy(val); } } if (isArray(val) && target) { target.fromArray(val); return target; } } // console.log(attribName, _ref, _ref.value); return val; //_ref.value; } return _attribFromProperty(); } // static previousAttribValue<T extends CoreObjectType>( // object: ObjectContent<T>, // attribName: string // ): AttribValue | undefined { // const dict = this.attributesPreviousValuesDictionary(object); // return dict[attribName]; // } static stringAttribValue<T extends CoreObjectType>( object: ObjectContent<T>, attribName: string, index: number = 0 ): string | null { const str = this.attribValue(object, attribName, index); if (str != null) { if (isString(str)) { return str; } else { return `${str}`; } } return null; } // static makeAttribReactive<V extends AttribValue, T extends CoreObjectType>( // object: ObjectContent<T>, // attribName: string, // callback: AttributeReactiveCallback<V> // ) { // const attributesDict = this.attributesDictionary(object); // // const attributesPreviousValuesDict = this.attributesPreviousValuesDictionary(object); // const currentValue = attributesDict[attribName]; // if (currentValue instanceof Vector4) { // return makeAttribReactiveVector4( // object, // attribName, // (<unknown>callback) as AttributeReactiveCallback<Vector4> // ); // } // if (currentValue instanceof Vector3) { // return makeAttribReactiveVector3( // object, // attribName, // (<unknown>callback) as AttributeReactiveCallback<Vector3> // ); // } // if (currentValue instanceof Vector2) { // return makeAttribReactiveVector2( // object, // attribName, // (<unknown>callback) as AttributeReactiveCallback<Vector2> // ); // } // return makeAttribReactiveSimple( // object, // attribName, // (<unknown>callback) as AttributeReactiveCallback<string | number> // ); // // // create a dummy val in case there is no attribute yet // // if (attributesDict[attribName] == null) { // // attributesDict[attribName] = 0; // // } // // const proxy: AttributeProxy<V> = { // // value: attributesDict[attribName] as V, // // previousValue: attributesDict[attribName] as V, // // }; // // Object.defineProperties(attributesDict, { // // [attribName]: { // // get: function () { // // return proxy.value; // // }, // // set: function (x) { // // if (x != proxy.value) { // // proxy.previousValue = proxy.value; // // proxy.value = x; // // callback(proxy.value, proxy.previousValue); // // } // // return proxy.value; // // }, // // configurable: true, // // }, // // }); // // Object.defineProperties(attributesPreviousValuesDict, { // // [attribName]: { // // get: function () { // // return proxy.previousValue; // // }, // // configurable: true, // // }, // // }); // } attribValue(attribName: string, target?: Vector2 | Vector3 | Vector4): AttribValue | undefined { return (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).attribValue( this._object, attribName, this._index, target ); } stringAttribValue(name: string) { return (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).stringAttribValue( this._object, name, this._index ); } name(): string { return this.attribValue(PropertyName.NAME) as string; } humanType(): string { return this._object.type; } attribTypes() { const h: PolyDictionary<AttribType> = {}; for (const attrib_name of this.attribNames()) { const type = this.attribType(attrib_name); if (type != null) { h[attrib_name] = type; } } return h; } static attribType<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string) { const val = this.attribValue(object, attribName); if (isString(val)) { return AttribType.STRING; } else { return AttribType.NUMERIC; } } attribType(attribName: string) { return (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).attribType(this._object, attribName); } attribSizes() { const h: PolyDictionary<AttribSize> = {}; const attribNames = this.attribNames(); for (const attribName of attribNames) { const size = this.attribSize(attribName); if (size != null) { h[attribName] = size; } } return h; } static attribSize<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string): AttribSize | null { const val = this.attribValue(object, attribName); if (val == null) { return null; } return CoreAttribute.attribSizeFromValue(val); } attribSize(attribName: string) { return (this.constructor as any as typeof BaseCoreObject<CoreObjectType>).attribSize(this._object, attribName); } static objectData<T extends CoreObjectType>(object: ObjectContent<T>): ObjectData { return objectData(object); } clone(): BaseCoreObject<T> { const clonedObject = (this.constructor as typeof BaseCoreObject<CoreObjectType>).clone(this._object); const cloned = new (this.constructor as any)(clonedObject, this._index); return cloned; } static clone<T extends CoreObjectType>(srcObject: ObjectContent<T>): ObjectContent<T> { return srcObject.clone() as ObjectContent<T>; } static applyMatrix( object: ObjectContent<CoreObjectType>, matrix: Matrix4, transformTargetType: TransformTargetType, transformSpace: ObjectTransformSpace, transformMode: ObjectTransformMode ) { console.warn('applyMatrix.override required', this); } static mergeCompact(options: MergeCompactOptions) { console.warn('mergeCompact.override required', this); } // // // ENTITY GROUPS // // groupCollection() { return new EntityGroupCollection(this._object); } // // // RELATED ENTITIES // // static override relatedVertexIds<T extends CoreObjectType>( object: ObjectContent<T>, objectIndex: number, target: number[], traversedRelatedEntityData?: TraversedRelatedEntityData ): void { const ids = traversedRelatedEntityData ? traversedRelatedEntityData[AttribClass.PRIMITIVE].ids : _relatedPrimitiveIds; this.relatedPrimitiveIds(object, objectIndex, ids, traversedRelatedEntityData); uniqRelatedEntityIds( ids, (primitiveId, relatedEntityIds) => { this.relatedPrimitiveClass(object).relatedVertexIds(object, primitiveId, relatedEntityIds); }, target ); } static override relatedPointIds<T extends CoreObjectType>( object: ObjectContent<T>, objectIndex: number, target: number[], traversedRelatedEntityData?: TraversedRelatedEntityData ): void { const ids = traversedRelatedEntityData ? traversedRelatedEntityData[AttribClass.VERTEX].ids : _relatedVertexIds; this.relatedVertexIds(object, objectIndex, ids, traversedRelatedEntityData); uniqRelatedEntityIds( ids, (vertexId, relatedEntityIds) => { this.relatedVertexClass(object).relatedPointIds(object, vertexId, relatedEntityIds); }, target ); } relatedEntities( attribClass: AttribClass, coreGroup: CoreGroup, target: CoreEntity[], traversedRelatedEntityData?: TraversedRelatedEntityData ): void { switch (attribClass) { case AttribClass.POINT: { this.relatedPoints(target as CorePoint<T>[], traversedRelatedEntityData); return; } case AttribClass.VERTEX: { this.relatedVertices(target as CoreVertex<T>[], traversedRelatedEntityData); return; } case AttribClass.PRIMITIVE: { this.relatedPrimitives(target as CorePrimitive<T>[], traversedRelatedEntityData); return; } case AttribClass.OBJECT: { target.length = 1; target[0] = this; return; } case AttribClass.CORE_GROUP: { target.length = 1; target[0] = coreGroup; return; } } TypeAssert.unreachable(attribClass); } static override relatedPointClass<T extends CoreObjectType>(object: ObjectContent<T>) { return this.relatedVertexClass(object).relatedPointClass(object); } static override relatedVertexClass<T extends CoreObjectType>(object: ObjectContent<T>) { return this.relatedPrimitiveClass(object).relatedVertexClass(object); } }