UNPKG

polygonjs-engine

Version:

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

303 lines (277 loc) 10.3 kB
/** * Delete parts of the input geometry * * @remarks * This can be used in many ways to delete points or objects from the input. * */ import {TypedSopNode} from './_Base'; import { AttribClass, AttribClassMenuEntries, ObjectType, ObjectTypeMenuEntries, ObjectTypes, objectTypeFromConstructor, AttribType, AttribTypeMenuEntries, ATTRIBUTE_TYPES, AttribSize, ATTRIBUTE_CLASSES, ATTRIBUTE_SIZE_RANGE, } from '../../../core/geometry/Constant'; import {CoreGroup, Object3DWithGeometry} from '../../../core/geometry/Group'; import {CoreGeometry} from '../../../core/geometry/Geometry'; import {InputCloneMode} from '../../poly/InputCloneMode'; import {CorePoint} from '../../../core/geometry/Point'; import {CoreObject} from '../../../core/geometry/Object'; import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig'; import {EntitySelectionHelper} from './utils/delete/EntitySelectionHelper'; import { ByAttributeHelper, ComparisonOperatorMenuEntries, ComparisonOperator, COMPARISON_OPERATORS, } from './utils/delete/ByAttributeHelper'; import {ByExpressionHelper} from './utils/delete/ByExpressionHelper'; import {ByBboxHelper} from './utils/delete/ByBboxHelper'; import {Object3D} from 'three/src/core/Object3D'; import {ByObjectTypeHelper} from './utils/delete/ByObjectTypeHelper'; class DeleteSopParamsConfig extends NodeParamsConfig { /** @param defines the class that should be deleted (objects or vertices) */ class = ParamConfig.INTEGER(ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX), { menu: { entries: AttribClassMenuEntries, }, }); /** @param invert the selection created in the parameters below */ invert = ParamConfig.BOOLEAN(0); // hide_objects = ParamConfig.BOOLEAN(0, { // visibleIf: {class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT)}, // }); // byObjectType /** @param deletes objects by object type */ byObjectType = ParamConfig.BOOLEAN(0, { visibleIf: {class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT)}, }); /** @param sets which object types should be deleted */ objectType = ParamConfig.INTEGER(ObjectTypes.indexOf(ObjectType.MESH), { menu: { entries: ObjectTypeMenuEntries, }, visibleIf: { class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT), byObjectType: true, }, }); separatorObjectType = ParamConfig.SEPARATOR(null, { visibleIf: {class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT)}, }); // byExpression /** @param deletes objects by an expression */ byExpression = ParamConfig.BOOLEAN(0); /** @param sets the expression to select what should be deleted */ expression = ParamConfig.BOOLEAN('@ptnum==0', { visibleIf: {byExpression: true}, expression: {forEntities: true}, }); separatorExpression = ParamConfig.SEPARATOR(); // byAttrib /** @param deletes objects by an attribute */ byAttrib = ParamConfig.BOOLEAN(0); /** @param sets the type of the attribute for which items should be deleted */ attribType = ParamConfig.INTEGER(ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), { menu: { entries: AttribTypeMenuEntries, }, visibleIf: {byAttrib: 1}, }); /** @param name of the attribute used */ attribName = ParamConfig.STRING('', { visibleIf: {byAttrib: 1}, }); /** @param size of the attribute used */ attribSize = ParamConfig.INTEGER(1, { range: ATTRIBUTE_SIZE_RANGE, rangeLocked: [true, true], visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC)}, }); /** @param comparison operator */ attribComparisonOperator = ParamConfig.INTEGER(COMPARISON_OPERATORS.indexOf(ComparisonOperator.EQUAL), { menu: { entries: ComparisonOperatorMenuEntries, }, visibleIf: { byAttrib: true, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), attribSize: AttribSize.FLOAT, }, }); /** @param value of the attribute to compare with (when using float attribute) */ attribValue1 = ParamConfig.FLOAT(0, { visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), attribSize: 1}, }); /** @param value of the attribute to compare with (when using vector2 attribute) */ attribValue2 = ParamConfig.VECTOR2([0, 0], { visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), attribSize: 2}, }); /** @param value of the attribute to compare with (when using vector3 attribute) */ attribValue3 = ParamConfig.VECTOR3([0, 0, 0], { visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), attribSize: 3}, }); /** @param value of the attribute to compare with (when using vector4 attribute) */ attribValue4 = ParamConfig.VECTOR4([0, 0, 0, 0], { visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), attribSize: 4}, }); /** @param value of the attribute to compare with (when using string attribute) */ attribString = ParamConfig.STRING('', { visibleIf: {byAttrib: 1, attribType: ATTRIBUTE_TYPES.indexOf(AttribType.STRING)}, }); separatorAttrib = ParamConfig.SEPARATOR(); // byBbox /** @param deletes objects that are inside a bounding box */ byBbox = ParamConfig.BOOLEAN(0, { visibleIf: { class: ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX), }, }); /** @param the bounding box size */ bboxSize = ParamConfig.VECTOR3([1, 1, 1], { visibleIf: { class: ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX), byBbox: true, }, }); /** @param the bounding box center */ bboxCenter = ParamConfig.VECTOR3([0, 0, 0], { visibleIf: { class: ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX), byBbox: true, }, }); separatorBbox = ParamConfig.SEPARATOR(null, { visibleIf: { class: ATTRIBUTE_CLASSES.indexOf(AttribClass.VERTEX), }, }); //this.add_param( ParamType.STRING, 'index_mode', Core.Geometry.Geometry.INDEX_MODE_FACES ) // by_visible // by_visible = ParamConfig.BOOLEAN(0, { // visibleIf: {class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT)}, // }); /** @param keeps points */ keepPoints = ParamConfig.BOOLEAN(0, { visibleIf: {class: ATTRIBUTE_CLASSES.indexOf(AttribClass.OBJECT)}, }); } const ParamsConfig = new DeleteSopParamsConfig(); export class DeleteSopNode extends TypedSopNode<DeleteSopParamsConfig> { params_config = ParamsConfig; static type() { return 'delete'; } private _marked_for_deletion_per_object_index: Map<number, boolean> = new Map(); public readonly entity_selection_helper = new EntitySelectionHelper(this); public readonly byBbox_helper = new ByBboxHelper(this); public readonly byExpression_helper = new ByExpressionHelper(this); public readonly byAttribute_helper = new ByAttributeHelper(this); public readonly byObjectType_helper = new ByObjectTypeHelper(this); static displayedInputNames(): string[] { return ['geometry to delete from']; } initializeNode() { this.io.inputs.setCount(1); this.io.inputs.initInputsClonedState(InputCloneMode.FROM_NODE); } async cook(input_contents: CoreGroup[]) { const core_group = input_contents[0]; switch (this.pv.class) { case AttribClass.VERTEX: await this._eval_for_points(core_group); break; case AttribClass.OBJECT: await this._eval_for_objects(core_group); break; } } set_class(attrib_class: AttribClass) { this.p.class.set(attrib_class); } private async _eval_for_objects(core_group: CoreGroup) { const core_objects = core_group.coreObjects(); this.entity_selection_helper.init(core_objects); this._marked_for_deletion_per_object_index = new Map(); for (let core_object of core_objects) { this._marked_for_deletion_per_object_index.set(core_object.index(), false); } if (this.pv.byExpression) { await this.byExpression_helper.eval_for_entities(core_objects); } if (this.pv.byObjectType) { this.byObjectType_helper.eval_for_objects(core_objects); } if (this.pv.byAttrib && this.pv.attribName != '') { this.byAttribute_helper.eval_for_entities(core_objects); } const core_objects_to_keep = this.entity_selection_helper.entities_to_keep() as CoreObject[]; const objects_to_keep = core_objects_to_keep.map((co) => co.object()); if (this.pv.keepPoints) { const core_objects_to_delete = this.entity_selection_helper.entities_to_delete() as CoreObject[]; for (let core_object_to_delete of core_objects_to_delete) { const point_object = this._point_object(core_object_to_delete); if (point_object) { objects_to_keep.push(point_object); } } } this.setObjects(objects_to_keep); } private async _eval_for_points(core_group: CoreGroup) { const core_objects = core_group.coreObjects(); let core_object; let objects: Object3D[] = []; for (let i = 0; i < core_objects.length; i++) { core_object = core_objects[i]; let core_geometry = core_object.coreGeometry(); if (core_geometry) { const object = core_object.object() as Object3DWithGeometry; const points = core_geometry.pointsFromGeometry(); this.entity_selection_helper.init(points); const init_points_count = points.length; if (this.pv.byExpression) { await this.byExpression_helper.eval_for_entities(points); } // TODO: the helpers do not yet take into account if an entity has been selected or not. // This could really speed up iterating through them, as I could skip the ones that have already been if (this.pv.byAttrib && this.pv.attribName != '') { this.byAttribute_helper.eval_for_entities(points); } if (this.pv.byBbox) { this.byBbox_helper.eval_for_points(points); } const kept_points = this.entity_selection_helper.entities_to_keep() as CorePoint[]; if (kept_points.length == init_points_count) { objects.push(object); } else { core_geometry.geometry().dispose(); if (kept_points.length > 0) { const new_geo = CoreGeometry.geometryFromPoints( kept_points, objectTypeFromConstructor(object.constructor) ); if (new_geo) { object.geometry = new_geo; objects.push(object); } } } } } this.setObjects(objects); } private _point_object(core_object: CoreObject) { const core_points = core_object.points(); const geometry = CoreGeometry.geometryFromPoints(core_points, ObjectType.POINTS); if (geometry) return this.create_object(geometry, ObjectType.POINTS); } }