UNPKG

@polygonjs/polygonjs

Version:

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

447 lines (427 loc) 13.3 kB
import { AttribValue, ColorLike, NumericAttribValue, Vector2Like, Vector3Like, Vector4Like, } from '../../../../types/GlobalTypes'; import {Vector4, Vector3, Vector2} from 'three'; import {Attribute, CoreAttribute} from '../../Attribute'; import {CoreEntity, CoreEntityWithObject} from '../../CoreEntity'; import {isArray} from '../../../Type'; import {BasePrimitiveAttribute} from './PrimitiveAttribute'; import { DOT, ComponentName, COMPONENT_INDICES, GroupString, AttribClass, AttribSize, AttribType, ComponentIndex, } from '../../Constant'; import {PrimitiveAttributesDict} from './Common'; import {CoreObjectType, ObjectContent, ObjectBuilder} from '../../ObjectContent'; import {TypeAssert} from '../../../../engine/poly/Assert'; import {uniqRelatedEntityIds} from '../utils/Common'; import type {CoreVertex} from '../vertex/CoreVertex'; import type {CoreGroup} from '../../Group'; import type {PrimitiveGraph} from './PrimitiveGraph'; import {CorePoint} from '../point/CorePoint'; import {TraversedRelatedEntityData} from '../utils/TraversedRelatedEntities'; function _warnOverloadRequired(functionName: string) { console.warn(`CorePrimitive.${functionName} needs to be overloaded`); } const _ids: number[] = []; export abstract class CorePrimitive<T extends CoreObjectType> extends CoreEntityWithObject<T> { builder<T extends CoreObjectType>(): ObjectBuilder<T> | undefined { return undefined; } static entitiesCount<T extends CoreObjectType>(object: ObjectContent<T>) { return 0; } static addAttribute<T extends CoreObjectType>( object: ObjectContent<T>, attribName: string, attribute: BasePrimitiveAttribute ) { _warnOverloadRequired('addAttribute'); } static addNumericAttribute<T extends CoreObjectType>( object: ObjectContent<T>, attribName: string, size: AttribSize = 1, defaultValue: NumericAttribValue = 0 ) { _warnOverloadRequired('addNumericAttribute'); } static attributes<T extends CoreObjectType>(object?: ObjectContent<T>): PrimitiveAttributesDict | undefined { _warnOverloadRequired('attributes'); return; } attributes(): PrimitiveAttributesDict | undefined { if (!this._object) { return; } return (this.constructor as typeof CorePrimitive<T>).attributes(this._object); } static attribute<T extends CoreObjectType>( object: ObjectContent<T>, attribName: string ): BasePrimitiveAttribute | undefined { const attributes = this.attributes(object); if (!attributes) { return; } return attributes[attribName]; } attribute(attribName: string): BasePrimitiveAttribute | undefined { if (!this._object) { return; } return (this.constructor as typeof CorePrimitive<T>).attribute(this._object, attribName); } static renameAttribute<T extends CoreObjectType>(object: ObjectContent<T>, oldName: string, newName: string) { const attributes = this.attributes(object); if (!attributes) { return; } const attribute = this.attribute(object, oldName); if (!attribute) { return; } attributes[newName] = attribute; delete attributes[oldName]; } static deleteAttribute<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string) { const attributes = this.attributes(object); if (!attributes) { return; } delete attributes[attribName]; } static attribSize<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string): number { const attributes = this.attributes(object); if (!attributes) { return -1; } attribName = CoreAttribute.remapName(attribName); return attributes[attribName].itemSize || 0; } attribSize(attribName: string): number { if (!this._object) { return 0; } return (this.constructor as typeof CorePrimitive<T>).attribSize(this._object, attribName); } static hasAttribute<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string): boolean { const remappedName = CoreAttribute.remapName(attribName); return this.attributes(object) ? this.attributes(object)![remappedName] != null : false; } hasAttribute(attribName: string): boolean { if (!this._object) { return false; } return (this.constructor as typeof CorePrimitive<T>).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)); } static attribValue<T extends CoreObjectType>( object: ObjectContent<T>, index: number, attribName: string, target?: Vector2 | Vector3 | Vector4 ): AttribValue { if (attribName === Attribute.PRIMITIVE_INDEX) { return index; } let componentName: ComponentName | null = null; let componentIndex: ComponentIndex | null = null; if (attribName[attribName.length - 2] === DOT) { componentName = attribName[attribName.length - 1] as ComponentName; componentIndex = COMPONENT_INDICES[componentName]; attribName = attribName.substring(0, attribName.length - 2); } const remapedName = CoreAttribute.remapName(attribName); if (remapedName == Attribute.POSITION) { return this.position(object, index, target as Vector3); } if (remapedName == Attribute.NORMAL) { return this.normal(object, index, target as Vector3); } const attrib = this.attribute(object, remapedName); if (attrib) { const {array} = attrib; const itemSize = attrib.itemSize; const startIndex = index * itemSize; if (componentIndex == null) { switch (itemSize) { case 1: return array[startIndex]; break; case 2: target = target || new Vector2(); target.fromArray(array as number[], startIndex); return target; break; case 3: target = target || new Vector3(); target.fromArray(array as number[], startIndex); return target; break; case 4: target = target || new Vector4(); target.fromArray(array as number[], startIndex); return target; break; default: throw `size not valid (${itemSize})`; } } else { switch (itemSize) { case 1: return array[startIndex]; break; default: return array[startIndex + componentIndex]; } } // } } else { const attributesDict = this.attributes(object) || {}; const attribNames: string[] = Object.keys(attributesDict); const message = `attrib ${attribName} not found. availables are: ${attribNames.join(',')}`; console.warn(message); throw message; } } attribValue(attribName: string, target?: Vector2 | Vector3 | Vector4): AttribValue { if (!this._object) { return 0; } return (this.constructor as typeof CorePrimitive<T>).attribValue(this._object, this._index, attribName, target); } attribValueNumber(attribName: string) { const attrib = this.attribute(attribName); if (!attrib) { return 0; } return attrib.array[this._index]; } attribValueVector2(attribName: string, target: Vector2) { const attrib = this.attribute(attribName); if (!attrib) { return; } target.fromArray(attrib.array as number[], this._index * 2); return target; } attribValueVector3(attribName: string, target: Vector3) { const attrib = this.attribute(attribName); if (!attrib) { return; } target.fromArray(attrib.array as number[], this._index * 3); return target; } attribValueVector4(attribName: string, target: Vector4) { const attrib = this.attribute(attribName); if (!attrib) { return; } target.fromArray(attrib.array as number[], this._index * 4); return target; } static attribType<T extends CoreObjectType>(object: ObjectContent<T> | undefined, attribName: string): AttribType { const attribute = object ? this.attribute(object, attribName) : null; if (attribute && attribute?.isString == true) { return AttribType.STRING; } else { return AttribType.NUMERIC; } } attribType(attribName: string): AttribType { return (this.constructor as typeof CorePrimitive).attribType(this._object, attribName); } static stringAttribValue<T extends CoreObjectType>(object: ObjectContent<T>, index: number, attribName: string) { return this.attribValue(object, index, attribName); } stringAttribValue(attribName: string) { return this.attribValue(attribName) as string; } // setPosition(newPosition: Vector3) { // this.setAttribValueFromVector3(Attribute.POSITION, newPosition); // } // setNormal(newNormal: Vector3) { // return this.setAttribValueFromVector3(Attribute.NORMAL, newNormal); // } static position<T extends CoreObjectType>( object: ObjectContent<T> | undefined, primitiveIndex: number, target: Vector3 ): Vector3 { _warnOverloadRequired('position'); return target; } static normal<T extends CoreObjectType>( object: ObjectContent<T> | undefined, primitiveIndex: number, target: Vector3 ): Vector3 { _warnOverloadRequired('normal'); return target; } static computeVertexNormalsIfAttributeVersionChanged<T extends CoreObjectType>(object: ObjectContent<T>) { _warnOverloadRequired('computeVertexNormalsIfAttributeVersionChanged'); } setAttribValue(attribName: string, value: NumericAttribValue | string) { const attrib = this.attribute(attribName); if (!attrib) { console.warn(`no attribute ${attribName}`); return; } const array = attrib.array; const attribSize = attrib.itemSize; if (isArray(value)) { for (let i = 0; i < attribSize; i++) { array[this._index * attribSize + i] = value[i]; } return; } switch (attribSize) { case 1: array[this._index] = value as number | string; break; case 2: const v2 = value as Vector2Like; const i2 = this._index * 2; array[i2 + 0] = v2.x; array[i2 + 1] = v2.y; break; case 3: const isColor = (value as ColorLike).r != null; const i3 = this._index * 3; if (isColor) { const col = value as ColorLike; array[i3 + 0] = col.r; array[i3 + 1] = col.g; array[i3 + 2] = col.b; } else { const v3 = value as Vector3Like; array[i3 + 0] = v3.x; array[i3 + 1] = v3.y; array[i3 + 2] = v3.z; } break; case 4: const v4 = value as Vector4Like; const i4 = this._index * 4; array[i4 + 0] = v4.x; array[i4 + 1] = v4.y; array[i4 + 2] = v4.z; array[i4 + 3] = v4.w; break; default: console.warn(`CorePrimitive.setAttribValue does not yet allow attribSize ${attribSize}`); throw `attrib size ${attribSize} not implemented`; } } setAttribValueFromNumber(attribName: string, value: number) { const attrib = this.attribute(attribName); if (!attrib) { return; } const array = attrib.array as number[]; array[this._index] = value; } setAttribValueFromVector2(attribName: string, value: Vector2) { const attrib = this.attribute(attribName); if (!attrib || attrib.isString == true) { return; } value.toArray(attrib.array as number[], this._index * 2); } setAttribValueFromVector3(attribName: string, value: Vector3) { const attrib = this.attribute(attribName); if (!attrib || attrib.isString == true) { return; } value.toArray(attrib.array as number[], this._index * 3); } setAttribValueFromVector4(attribName: string, value: Vector4) { const attrib = this.attribute(attribName); if (!attrib || attrib.isString == true) { return; } value.toArray(attrib.array as number[], this._index * 4); } // // // RELATED ENTITIES // // static override relatedPointIds<T extends CoreObjectType>( object: ObjectContent<T>, pointIndex: number, target: number[], traversedRelatedEntityData?: TraversedRelatedEntityData ): void { const ids = traversedRelatedEntityData ? traversedRelatedEntityData[AttribClass.VERTEX].ids : _ids; this.relatedVertexIds(object, pointIndex, ids); uniqRelatedEntityIds( ids, (vertexId, relatedEntityIds) => { this.relatedVertexClass(object).relatedPointIds(object, vertexId, relatedEntityIds); }, target ); } static override relatedPointClass<T extends CoreObjectType>(object: ObjectContent<T>) { return this.relatedVertexClass(object).relatedPointClass(object); } 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: { target.length = 1; target[0] = this; return; } case AttribClass.OBJECT: { this.relatedObjects(target as CoreEntityWithObject<T>[], traversedRelatedEntityData); return; } case AttribClass.CORE_GROUP: { target.length = 1; target[0] = coreGroup; return; } } TypeAssert.unreachable(attribClass); } static graph(object: ObjectContent<CoreObjectType>): PrimitiveGraph | undefined { console.warn('CorePrimitive.graph needs to be overriden'); return undefined; } }