UNPKG

@polygonjs/polygonjs

Version:

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

623 lines (594 loc) 16.8 kB
import {AttribValue, NumericAttribValue} from './../../types/GlobalTypes'; import {PolyDictionary} from '../../types/GlobalTypes'; import {Box3, BufferGeometry, LineSegments, Mesh, Points, Object3D, Vector2, Vector3, Vector4} from 'three'; import {CoreAttribute} from './Attribute'; import {CoreString} from '../String'; import {AttribSize, ObjectData, AttribType, GroupString, AttribClass} from './Constant'; import {isString} from '../Type'; import {arraySum, arrayCompact, arrayPushItems, arrayCopy} from '../ArrayUtils'; import {Poly} from '../../engine/Poly'; import {CoreObjectType, ObjectBuilder, ObjectContent, isObject3D} from './ObjectContent'; import {coreObjectClassFactory, coreObjectInstanceFactory} from './CoreObjectFactory'; import {BaseCoreObject} from './entities/object/BaseCoreObject'; import { coreObjectAttributeTypesByName, coreObjectsAttribNames, coreObjectsAttribSizesByName, } from './entities/object/BaseCoreObjectUtils'; import {attribValueNonPrimitive, cloneAttribValue} from './entities/utils/Common'; import {object3DHasGeometry} from './GeometryUtils'; // entities import {CoreEntity} from './CoreEntity'; import { pointsCountFromObject, pointAttributeNames, hasPointAttribute, pointAttributeType, pointAttributeSizes, pointAttributeSize, pointsFromObjects, } from './entities/point/CorePointUtils'; import type {CorePoint} from './entities/point/CorePoint'; // CAD import type {CadGeometryType, CadGeometryTypeShape} from './modules/cad/CadCommon'; import type {CadObject} from './modules/cad/CadObject'; import {CoreCadType, isCADObject} from './modules/cad/CadCoreType'; // // CSG import type {CsgGeometryType} from './modules/csg/CsgCommon'; import type {CsgObject} from './modules/csg/CsgObject'; import {isCSGObject} from './modules/csg/CsgCoreType'; // // QUAD import type {QuadObject} from './modules/quad/QuadObject'; import {isQuadObject, isQuadOrThreejsObject} from './modules/quad/QuadCoreType'; // // SDF // import type {SDFObjectType} from './sdf/SDFCommon'; // import {SDF_OBJECT_TYPES_SET} from './sdf/SDFCommon'; // import type {SDFObject} from './sdf/SDFObject'; // // TET import {isTetObject} from './modules/tet/TetCoreType'; import {TetObject} from './modules/tet/TetObject'; import {TypeAssert} from '../../engine/poly/Assert'; // THREEJS import {ThreejsCoreObject} from './modules/three/ThreejsCoreObject'; import {uniqRelatedEntities} from './entities/utils/Common'; import {CoreVertex} from './entities/vertex/CoreVertex'; import {CorePrimitive} from './entities/primitive/CorePrimitive'; import {TraversedRelatedEntityData} from './entities/utils/TraversedRelatedEntities'; type AttributeDictionary = PolyDictionary<AttribValue>; const tmpBox3 = new Box3(); const tmpPos = new Vector3(); const _indices: number[] = []; const _points: CorePoint<CoreObjectType>[] = []; const _relatedPoints: CorePoint<CoreObjectType>[] = []; const _relatedVertices: CoreVertex<CoreObjectType>[] = []; const _relatedPrimitives: CorePrimitive<CoreObjectType>[] = []; const _relatedPrimitivesForObject: CorePrimitive<CoreObjectType>[] = []; export interface Object3DWithGeometry extends Object3D { geometry: BufferGeometry; } function objectTotalPointsCount(object: ObjectContent<CoreObjectType>) { let sum = 0; object.traverse((child) => { // const geometry = (child as Mesh).geometry as BufferGeometry; // if (geometry) { sum += pointsCountFromObject(child); // } }); return sum; } export class CoreGroup extends CoreEntity { private _timestamp: number | undefined; private _allObjects: ObjectContent<CoreObjectType>[] = []; constructor() { super(undefined, 0); this.touch(); } dispose() { if (this._allObjects) { for (const object of this._allObjects) { if (object.dispose) { object.dispose(); } } } this._allObjects.length = 0; } geometry() { return null; } builder<T extends CoreObjectType>(): ObjectBuilder<T> | undefined { return undefined; } // // // TIMESTAMP // // timestamp() { return this._timestamp; } touch() { const performance = Poly.performance.performanceManager(); this._timestamp = performance.now(); // this.reset(); } // reset() { // // this.resetBoundingBox(); // // this._bounding_sphere = undefined; // // this._coreGeometries = undefined; // // this._coreObjects = undefined; // } resetBoundingBox() { // this._boundingBox = undefined; } // // // CLONE // // clone() { const coreGroup = new CoreGroup(); // all if (this._allObjects) { const allCoreObjects = this.allCoreObjects(); const clonedObjects: ObjectContent<CoreObjectType>[] = []; for (const coreObject of allCoreObjects) { const clonedObject = coreObject.clone().object(); if (clonedObject) { clonedObjects.push(clonedObject); } } // for (let object of this._allObjects) { // allObjects.push(object.clone()); // } coreGroup.setAllObjects(clonedObjects); } const attribNames = this.attribNames(); for (const attribName of attribNames) { const value = this.attribValue(attribName); coreGroup.addAttribute(attribName, value); } return coreGroup; } // // // ALL OBJECTS // // setAllObjects(objects: ObjectContent<CoreObjectType>[]) { this._allObjects = objects; this.touch(); } allObjects() { return this._allObjects; } allCoreObjects() { return this.allObjects()?.map((o, i) => coreObjectInstanceFactory(o, i)); } // // // CAD OBJECTS // // cadObjects() { const list = this._allObjects?.filter(isCADObject) || undefined; return list as CadObject<CadGeometryType>[] | undefined; } cadObjectsWithShape() { return this.cadObjects()?.filter((o) => CoreCadType.isShape(o)) as | CadObject<CadGeometryTypeShape>[] | undefined; } cadCoreObjects() { return this.cadObjects()?.map((o, i) => coreObjectInstanceFactory(o, i)); } // // // CSG OBJECTS // // csgObjects() { const list = this._allObjects?.filter(isCSGObject) || undefined; return list as CsgObject<CsgGeometryType>[] | undefined; } csgCoreObjects() { return this.csgObjects()?.map((o, i) => coreObjectInstanceFactory(o, i)); } // // // QUAD OBJECTS // // quadObjects() { const list = this._allObjects?.filter(isQuadObject) || undefined; return list as QuadObject[] | undefined; } quadCoreObjects() { return this.quadObjects()?.map((o, i) => coreObjectInstanceFactory(o, i)); } threejsOrQuadObjects(): Array<Object3D | QuadObject> { return (this._allObjects ? this._allObjects.filter(isQuadOrThreejsObject) : []) as Array<Object3D | QuadObject>; } threejsOrQuadCoreObjects() { return this.threejsOrQuadObjects().map((o, i) => coreObjectInstanceFactory(o, i)); } // // // SDF OBJECTS // // // SDFObjects() { // const list = this._allObjects?.filter((o) => SDF_OBJECT_TYPES_SET.has(o.type as SDFObjectType)) || undefined; // return list as SDFObject[] | undefined; // } // SDFCoreObjects() { // return this.csgObjects()?.map((o, i) => coreObjectInstanceFactory(o, i)); // } // // // TET OBJECTS // // tetObjects() { const list = this._allObjects?.filter(isTetObject) || undefined; return list as TetObject[] | undefined; } tetCoreObjects() { return this.tetObjects()?.map((o, i) => coreObjectInstanceFactory(o, i)); } // // // THREEJS OBJECTS // // threejsObjects(): Object3D[] { return this._allObjects ? this._allObjects.filter(isObject3D) : []; } threejsObjectsWithGeo(): Object3DWithGeometry[] { return this.threejsObjects().filter(object3DHasGeometry); } threejsCoreObjects() { return this.threejsObjects().map((o, i) => new ThreejsCoreObject(o, i)); } geometries(): BufferGeometry[] { return this.threejsObjectsWithGeo().map((o) => o.geometry); } // coreGeometries(): CoreGeometry[] { // return this.geometries().map((g) => new CoreGeometry(g)); // } // // // POINTS // // points(target: CorePoint<CoreObjectType>[]) { return pointsFromObjects(this.allObjects(), target); // return this.allObjects() // .map((o) => pointsFromObject(o)) // .flat(); // .map((g) => g.points()) // .flat(); } pointsCount() { return arraySum(this.allObjects().map((g) => pointsCountFromObject(g))); } totalPointsCount() { const threejsObjects = this.threejsObjects(); let sum = 0; for (const object of threejsObjects) { sum += objectTotalPointsCount(object); } return sum; } pointsFromGroup(group: GroupString, target: CorePoint<CoreObjectType>[]) { if (group) { CoreString.indices(group, _indices); this.points(_points); const compactPoints: CorePoint<CoreObjectType>[] = []; const pointsInGroup = arrayCompact( _indices.map((i) => _points[i]), compactPoints ); target.length = 0; arrayPushItems(pointsInGroup, target); return target; } else { return this.points(target); } } pointAttribNames(): string[] { const firstObject = this.allObjects()[0]; if (firstObject) { return pointAttributeNames(firstObject); } else { return []; } } hasPointAttrib(attribName: string): boolean { const firstObject = this.allObjects()[0]; if (firstObject) { return hasPointAttribute(firstObject, attribName); } else { return false; } } pointAttribType(attribName: string): AttribType { const firstObject = this.allObjects()[0]; if (firstObject) { return pointAttributeType(firstObject, attribName); } else { return AttribType.NUMERIC; } } pointAttribNamesMatchingMask(masksString: GroupString) { return CoreAttribute.attribNamesMatchingMask(masksString, this.pointAttribNames()); } pointAttribSizes(): Record<string, number> { const firstObject = this.allObjects()[0]; if (firstObject) { return pointAttributeSizes(firstObject); } else { return {}; } } pointAttribSize(attribName: string): number { const firstObject = this.allObjects()[0]; if (firstObject) { return pointAttributeSize(firstObject, attribName); } else { return 0; } } // // // OBJECTS // // static _fromObjects(objects: Object3D[]): CoreGroup { const coreGroup = new CoreGroup(); coreGroup.setAllObjects(objects); return coreGroup; } objectAttribTypesByName() { return coreObjectAttributeTypesByName(this.allCoreObjects()); } objectAttribNames() { return coreObjectsAttribNames(this.allCoreObjects()); } objectAttribNamesMatchingMask(masksString: GroupString) { return CoreAttribute.attribNamesMatchingMask(masksString, this.objectAttribNames()); } objectAttribSizesByName(): PolyDictionary<AttribSize[]> { return coreObjectsAttribSizesByName(this.allCoreObjects()); } // // // // // renameAttribute(oldName: string, newName: string) { const attribValue = this.attribValue(oldName); if (attribValue == null) { return; } this.addAttribute(newName, attribValue); this.deleteAttribute(oldName); } attribNamesMatchingMask(masksString: GroupString) { return CoreAttribute.attribNamesMatchingMask(masksString, this.attribNames()); } hasAttribute(attribName: string): boolean { return this.attribValue(attribName) != null; } private _attributes: AttributeDictionary = {}; addAttribute(attribName: string, attribValue: AttribValue) { this.attributes()[attribName] = attribValue; } addNumericAttribute<T extends CoreObjectType>( attribName: string, size: AttribSize = 1, defaultValue: NumericAttribValue = 0 ) { const attributes = this.attributes(); if (defaultValue != null) { if (attribValueNonPrimitive(defaultValue)) { const clonedDefaultValue = cloneAttribValue(defaultValue); if (clonedDefaultValue != null) { attributes[attribName] = clonedDefaultValue; } } else { attributes[attribName] = defaultValue; } } else { switch (size) { case 1: { return (this.attributes()[attribName] = 0); } case 2: { return (this.attributes()[attribName] = new Vector2(0, 0)); } case 3: { return (this.attributes()[attribName] = new Vector3(0, 0, 0)); } case 4: { return (this.attributes()[attribName] = new Vector4(0, 0, 0, 0)); } } } } deleteAttribute(name: string) { delete this.attributes()[name]; } attribValue(attribName: string) { return this._attributes && this._attributes[attribName]; } attribNames(): string[] { return this._attributes ? Object.keys(this._attributes) : []; } attribType(name: string) { const val = this.attribValue(name); if (isString(val)) { return AttribType.STRING; } else { return AttribType.NUMERIC; } } attribSizes() { const h: PolyDictionary<AttribSize> = {}; for (const attrib_name of this.attribNames()) { const size = this.attribSize(attrib_name); if (size != null) { h[attrib_name] = size; } } return h; } attribSize(name: string): AttribSize | null { const val = this.attribValue(name); if (val == null) { return null; } return CoreAttribute.attribSizeFromValue(val); } attributes() { return this._attributes || this._createAttributesDictionaryIfNone(); } private _createAttributesDictionaryIfNone() { if (!this._attributes) { this._attributes = {}; } return this._attributes; } // override setAttribValue(attribName: string, attribValue: AttribValue | string) { this.addAttribute(attribName, attribValue); } stringAttribValue(attribName: string) { return this.attribValue(attribName) as string | null; } position(target: Vector3): Vector3 { const objectsCount = this._allObjects.length; target.set(0, 0, 0); for (const object of this._allObjects) { coreObjectClassFactory(object).position(object, tmpPos); target.add(tmpPos); } target.divideScalar(objectsCount); return target; } attributeNames(): string[] { const attributes = this.attributes(); if (!attributes) { return []; } return Object.keys(attributes); } attributeNamesMatchingMask(masksString: GroupString) { return CoreAttribute.attribNamesMatchingMask(masksString, this.attributeNames()); } // // // RELATED ENTITIES // // relatedObjects( target: BaseCoreObject<CoreObjectType>[], traversedRelatedEntityData?: TraversedRelatedEntityData ): void { arrayCopy(this.allCoreObjects(), target); } relatedPrimitives( target: CorePrimitive<CoreObjectType>[], traversedRelatedEntityData?: TraversedRelatedEntityData ): void { target.length = 0; const objects = this.allObjects(); let i = 0; for (const object of objects) { coreObjectClassFactory(object).relatedPrimitives(object, i, _relatedPrimitivesForObject); for (const _relatedPrimitiveForObject of _relatedPrimitivesForObject) { target.push(_relatedPrimitiveForObject); } i++; } } relatedVertices(target: CoreVertex<CoreObjectType>[], traversedRelatedEntityData?: TraversedRelatedEntityData) { this.relatedPrimitives(_relatedPrimitives); uniqRelatedEntities( _relatedPrimitives, (primitive) => { primitive.relatedVertices(_relatedVertices); return _relatedVertices; }, target ); } relatedPoints(target: CorePoint<CoreObjectType>[], traversedRelatedEntityData?: TraversedRelatedEntityData) { this.relatedVertices(_relatedVertices); return uniqRelatedEntities( _relatedVertices, (vertex) => { vertex.relatedPoints(_relatedPoints); return _relatedPoints; }, target ); } relatedEntities( attribClass: AttribClass, coreGroup: CoreGroup, target: CoreEntity[], traversedRelatedEntityData?: TraversedRelatedEntityData ): void { switch (attribClass) { case AttribClass.POINT: { this.relatedPoints(target as CorePoint<CoreObjectType>[], traversedRelatedEntityData); return; } case AttribClass.VERTEX: { this.relatedVertices(target as CoreVertex<CoreObjectType>[], traversedRelatedEntityData); return; } case AttribClass.PRIMITIVE: { this.relatedPrimitives(target as CorePrimitive<CoreObjectType>[], traversedRelatedEntityData); return; } case AttribClass.OBJECT: { this.relatedObjects(target as BaseCoreObject<CoreObjectType>[], traversedRelatedEntityData); return; } case AttribClass.CORE_GROUP: { target.length = 1; target[0] = coreGroup; return; } } TypeAssert.unreachable(attribClass); } // // // UTILS // // objectsData(): ObjectData[] { return this._allObjects?.map((o) => coreObjectClassFactory(o).objectData(o)) || []; } boundingBox(target: Box3) { target.makeEmpty(); const coreObjects = this.allCoreObjects(); for (const coreObject of coreObjects) { coreObject.boundingBox(tmpBox3); target.union(tmpBox3); } } static geometryFromObject(object: Object3D): BufferGeometry | null { if ((object as Mesh).isMesh || (object as LineSegments).isLine || (object as Points).isPoints) { return (object as Mesh).geometry as BufferGeometry; } return null; } }