@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
169 lines (158 loc) • 5.14 kB
text/typescript
import {
CoreEntitySelectionState,
selectedIndicesFromSelectionStates,
} from '../../engine/nodes/sop/utils/group/GroupCommon';
import {TypeAssert} from '../../engine/poly/Assert';
import {arrayToSet} from '../ArrayUtils';
import {setToArray, setDifference, setIntersection, setUnion} from '../SetUtils';
import {CoreObjectType, ObjectContent} from './ObjectContent';
const _currentIndicesSet: Set<number> = new Set();
const _newIndicesSet: Set<number> = new Set();
export enum GroupOperation {
SET = 'replace existing',
UNION = 'add to existing',
SUBTRACT = 'subtract from existing',
INTERSECT = 'intersect with existing',
}
export const GROUP_OPERATIONS: GroupOperation[] = [
GroupOperation.SET,
GroupOperation.UNION,
GroupOperation.SUBTRACT,
GroupOperation.INTERSECT,
];
export interface GroupData {
name: string;
entitiesCount: number;
}
type GroupsDataForType = GroupData[];
export type GroupCollectionData = Record<string, GroupsDataForType>;
export enum EntityGroupType {
POINT = 'point',
OBJECT = 'object',
EDGE = 'edge',
FACE = 'face',
}
export interface UpdateGroupOptions {
type: EntityGroupType;
groupName: string;
operation: GroupOperation;
invert: boolean;
}
const USER_DATA_KEY_GROUPS = 'groups';
export type GroupsDictionary = Record<string, Record<string, number[]>>;
export class EntityGroupCollection {
// private _groupsByNameByType: Map<GroupType, Map<string, EntityGroup>> = new Map();
constructor(private _object: ObjectContent<CoreObjectType>) {}
attributesDictionary() {
return EntityGroupCollection.attributesDictionary(this._object);
}
static attributesDictionary<T extends CoreObjectType>(object: ObjectContent<T>) {
return (
(object.userData[USER_DATA_KEY_GROUPS] as GroupsDictionary) ||
this._createAttributesDictionaryIfNone(object)
);
}
private static _createAttributesDictionaryIfNone<T extends CoreObjectType>(object: ObjectContent<T>) {
if (!object.userData[USER_DATA_KEY_GROUPS]) {
return (object.userData[USER_DATA_KEY_GROUPS] = {});
}
}
findOrCreateGroup(type: EntityGroupType, groupName: string) {
const dict = this.attributesDictionary();
let groupsByName = dict[type];
if (!groupsByName) {
groupsByName = {};
dict[type] = groupsByName;
}
let group = groupsByName[groupName];
if (!group) {
group = [];
groupsByName[groupName] = group;
}
return group;
}
deleteGroup(type: EntityGroupType, groupName: string) {
const dict = this.attributesDictionary();
const groupsByName = dict[type];
if (groupsByName) {
delete groupsByName[groupName];
// clean if possible
if (Object.keys(groupsByName).length == 0) {
delete dict[type];
}
}
}
static data<T extends CoreObjectType>(object: ObjectContent<T>): GroupCollectionData {
const dict = this.attributesDictionary(object);
const types = Object.keys(dict);
const data: GroupCollectionData = {};
for (const type of types) {
const dataForType: GroupsDataForType = [];
data[type] = dataForType;
const groupsForType = dict[type];
const groupNames = Object.keys(groupsForType);
for (const groupName of groupNames) {
const indices = groupsForType[groupName];
const groupData: GroupData = {
name: groupName,
entitiesCount: indices.length,
};
dataForType.push(groupData);
}
}
return data;
}
indicesSet(type: EntityGroupType, groupName: string, target: Set<number>) {
const dict = this.attributesDictionary();
const groupsByName = dict[type];
target.clear();
if (groupsByName) {
const indices = groupsByName[groupName];
if (indices) {
arrayToSet(indices, target);
}
}
}
private selectedIndices: Set<number> = new Set();
updateGroup(options: UpdateGroupOptions, selectionStates: CoreEntitySelectionState) {
const {type, groupName, operation, invert} = options;
const currentIndices = this.findOrCreateGroup(type, groupName);
const _updateGroup = (newIndicesSet: Set<number>) => {
const dict = this.attributesDictionary();
let groupsByName = dict[type];
if (!groupsByName) {
groupsByName = {};
dict[type] = groupsByName;
}
const newIndices: number[] = [];
groupsByName[groupName] = setToArray(newIndicesSet, newIndices);
};
this.selectedIndices.clear();
selectedIndicesFromSelectionStates(selectionStates, this.selectedIndices, invert);
switch (operation) {
case GroupOperation.SET: {
_updateGroup(this.selectedIndices);
return;
}
case GroupOperation.UNION: {
arrayToSet(currentIndices, _currentIndicesSet);
setUnion(_currentIndicesSet, this.selectedIndices, _newIndicesSet);
_updateGroup(_newIndicesSet);
return;
}
case GroupOperation.SUBTRACT: {
arrayToSet(currentIndices, _currentIndicesSet);
setDifference(_currentIndicesSet, this.selectedIndices, _newIndicesSet);
_updateGroup(_newIndicesSet);
return;
}
case GroupOperation.INTERSECT: {
arrayToSet(currentIndices, _currentIndicesSet);
setIntersection(_currentIndicesSet, this.selectedIndices, _newIndicesSet);
_updateGroup(_newIndicesSet);
return;
}
}
TypeAssert.unreachable(operation);
}
}