@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
573 lines (548 loc) • 17.3 kB
text/typescript
import {
AttribValue,
ColorLike,
NumericAttribValue,
Vector2Like,
Vector3Like,
Vector4Like,
} from '../../../../types/GlobalTypes';
import {BufferAttribute, Vector4, Vector3, Vector2, InterleavedBufferAttribute} from 'three';
import {Attribute, CoreAttribute} from '../../Attribute';
import {isArray} from '../../../Type';
import {CoreEntity, CoreEntityWithObject} from '../../CoreEntity';
import {
DOT,
ComponentName,
COMPONENT_INDICES,
AttribType,
GroupString,
AttribClass,
AttribSize,
ComponentIndex,
} from '../../Constant';
import {ObjectContent, CoreObjectType, ObjectBuilder} from '../../ObjectContent';
import {PointAttributesDict} from './Common';
import {CoreAttributeData} from '../../AttributeData';
import {coreObjectInstanceFactory} from '../../CoreObjectFactory';
import {TypeAssert} from '../../../../engine/poly/Assert';
import {uniqRelatedEntityIds} from '../utils/Common';
import type {CoreGroup} from '../../Group';
import {TraversedRelatedEntityData} from '../utils/TraversedRelatedEntities';
const _relatedPrimitiveIds: number[] = [];
function _warnOverloadRequired(functionName: string) {
console.warn(`CorePoint.${functionName} needs to be overloaded`);
}
export abstract class CorePoint<T extends CoreObjectType> extends CoreEntityWithObject<T> {
// protected _object?: ObjectContent<T>;
// constructor(object?: ObjectContent<T>, index?: number) {
// super(object, index);
// this._object = object;
// }
builder<T extends CoreObjectType>(): ObjectBuilder<T> | undefined {
return undefined;
}
static addAttribute<T extends CoreObjectType>(
object: ObjectContent<T>,
attribName: string,
attribute: BufferAttribute
) {
_warnOverloadRequired('addAttribute');
}
static entitiesCount<T extends CoreObjectType>(object: ObjectContent<T>): number {
return 0;
}
static attributes<T extends CoreObjectType>(object?: ObjectContent<T>): PointAttributesDict | undefined {
_warnOverloadRequired('attributes');
return;
}
attributes(): PointAttributesDict | undefined {
// if (!this._object) {
// return;
// }
return (this.constructor as typeof CorePoint<T>).attributes(this._object);
}
static attribute<T extends CoreObjectType>(
object: ObjectContent<T>,
attribName: string
): BufferAttribute | InterleavedBufferAttribute | undefined {
const attributes = this.attributes(object);
if (!attributes) {
return;
}
return attributes[attribName];
}
attribute(attribName: string): BufferAttribute | InterleavedBufferAttribute | undefined {
// if (!this._object) {
// return;
// }
return (this.constructor as typeof CorePoint<T>).attribute(this._object, 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 CorePoint<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 CorePoint<T>).hasAttribute(this._object, attribName);
}
//
//
// INDEXED ATTRIBUTES
//
//
static userDataAttribs<T extends CoreObjectType>(object?: ObjectContent<T>): Record<string, string[]> {
_warnOverloadRequired('userDataAttribs');
return {};
}
userDataAttribs(): Record<string, string[]> {
return this._object ? (this.constructor as typeof CorePoint).userDataAttribs(this._object) : {};
}
static userDataAttrib<T extends CoreObjectType>(
object: ObjectContent<T> | undefined,
attribName: string
): string[] | undefined {
attribName = CoreAttribute.remapName(attribName);
return this.userDataAttribs(object)[attribName];
}
userDataAttrib(name: string) {
name = CoreAttribute.remapName(name);
return this.userDataAttribs()[name];
}
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 indexedAttributeNames<T extends CoreObjectType>(object?: ObjectContent<T>): string[] {
return object ? Object.keys(this.userDataAttribs(object) || {}) : [];
}
indexedAttributeNames(): string[] {
return this._object ? (this.constructor as typeof CorePoint).indexedAttributeNames(this._object) : [];
// return Object.keys(this.userDataAttribs() || {});
}
static isAttribIndexed<T extends CoreObjectType>(
object: ObjectContent<T> | undefined,
attribName: string
): boolean {
attribName = CoreAttribute.remapName(attribName);
return this.userDataAttrib(object, attribName) != null;
}
isAttribIndexed(name: string): boolean {
name = CoreAttribute.remapName(name);
return this.userDataAttrib(name) != null;
}
static setIndexedAttributeValues<T extends CoreObjectType>(
object: ObjectContent<T> | undefined,
attribName: string,
values: string[]
) {
this.userDataAttribs(object)[attribName] = values;
}
setIndexedAttributeValues(attribName: string, values: string[]) {
return (this.constructor as typeof CorePoint).setIndexedAttributeValues(this._object, attribName, values);
}
static setIndexedAttribute<T extends CoreObjectType>(
object: ObjectContent<T> | undefined,
attribName: string,
values: string[],
indices: number[]
) {
_warnOverloadRequired('setIndexedAttribute');
}
setIndexedAttribute(attribName: string, values: string[], indices: number[]) {
return (this.constructor as typeof CorePoint).setIndexedAttribute(this._object, attribName, values, indices);
}
//
static indexedAttribValue<T extends CoreObjectType>(
object: ObjectContent<T> | undefined,
index: number,
attribName: string
): string | null {
const valueIndex = this.attribValueIndex(object, index, attribName); //attrib.value()
const values = this.userDataAttrib(object, attribName);
return values ? values[valueIndex] : null;
}
indexedAttribValue(attribName: string): string | null {
return (this.constructor as typeof CorePoint).indexedAttribValue(this._object, this._index, attribName);
}
static stringAttribValue<T extends CoreObjectType>(object: ObjectContent<T>, index: number, attribName: string) {
return this.indexedAttribValue(object, index, attribName);
}
stringAttribValue(attribName: string) {
return this.indexedAttribValue(attribName);
}
static attribValueIndex<T extends CoreObjectType>(
object: ObjectContent<T> | undefined,
index: number,
attribName: string
): number {
_warnOverloadRequired('attribValueIndex');
return 0;
}
attribValueIndex(attribName: string): number {
return (this.constructor as typeof CorePoint).attribValueIndex(this._object, this._index, attribName);
}
static attribType<T extends CoreObjectType>(object: ObjectContent<T> | undefined, attribName: string): AttribType {
if (this.isAttribIndexed(object, attribName)) {
return AttribType.STRING;
} else {
return AttribType.NUMERIC;
}
}
attribType(attribName: string): AttribType {
return (this.constructor as typeof CorePoint).attribType(this._object, attribName);
}
isStringAttribute(attribName: string): boolean {
return this.attribType(attribName) == AttribType.STRING;
}
setAttribIndex(attribName: string, newValueIndex: number) {
const attribute = this.attribute(attribName);
if (!attribute) {
return;
}
const array = (attribute as BufferAttribute).array;
array[this._index] = newValueIndex;
return;
}
//
//
//
//
//
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 attribValue<T extends CoreObjectType>(
object: ObjectContent<T>,
index: number,
attribName: string,
target?: Vector2 | Vector3 | Vector4
): AttribValue {
if (attribName === Attribute.POINT_INDEX) {
return index;
} else {
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);
const attrib = this.attribute(object, remapedName);
if (attrib) {
const {array} = attrib;
if (this.isAttribIndexed(object, remapedName)) {
return this.indexedAttribValue(object, index, remapedName)!;
} else {
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, startIndex);
return target;
break;
case 3:
target = target || new Vector3();
target.fromArray(array, startIndex);
return target;
break;
case 4:
target = target || new Vector4();
target.fromArray(array, 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() || {};
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 CorePoint<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, this._index * 2);
return target;
}
attribValueVector3(attribName: string, target: Vector3) {
const attrib = this.attribute(attribName);
if (!attrib) {
return;
}
target.fromArray(attrib.array, this._index * 3);
return target;
}
attribValueVector4(attribName: string, target: Vector4) {
const attrib = this.attribute(attribName);
if (!attrib) {
return;
}
target.fromArray(attrib.array, this._index * 4);
return target;
}
position(target: Vector3): Vector3 {
_warnOverloadRequired('position');
return target;
}
setPosition(newPosition: Vector3) {
this.setAttribValueFromVector3(Attribute.POSITION, newPosition);
}
normal(target: Vector3): Vector3 {
_warnOverloadRequired('normal');
return target;
}
setNormal(newNormal: Vector3) {
return this.setAttribValueFromVector3(Attribute.NORMAL, newNormal);
}
static computeNormals<T extends CoreObjectType>(object: ObjectContent<T>) {
_warnOverloadRequired('computeNormals');
}
setAttribValue(attribName: string, value: NumericAttribValue | string) {
const attrib = this.attribute(attribName);
if (!attrib) {
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;
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(`CorePoint.setAttribValue does not yet allow attrib size ${attribSize}`);
throw `attrib size ${attribSize} not implemented`;
}
}
setAttribValueFromNumber(attribName: string, value: number) {
const attrib = this.attribute(attribName);
if (!attrib) {
return;
}
const array = attrib.array;
array[this._index] = value;
}
setAttribValueFromVector2(attribName: string, value: Vector2) {
const attrib = this.attribute(attribName);
if (!attrib || this.isStringAttribute(attribName)) {
return;
}
value.toArray(attrib.array, this._index * 2);
}
setAttribValueFromVector3(attribName: string, value: Vector3) {
const attrib = this.attribute(attribName);
if (!attrib || this.isStringAttribute(attribName)) {
return;
}
value.toArray(attrib.array, this._index * 3);
}
setAttribValueFromVector4(attribName: string, value: Vector4) {
const attrib = this.attribute(attribName);
if (!attrib || this.isStringAttribute(attribName)) {
return;
}
value.toArray(attrib.array, this._index * 4);
}
//
static addAttributeFromAttribData<T extends CoreObjectType>(
object: ObjectContent<T>,
attribName: string,
attribData: CoreAttributeData
) {
switch (attribData.type()) {
case AttribType.STRING:
return console.log('TODO: to implement');
case AttribType.NUMERIC:
return this.addNumericAttribute(object, attribName, attribData.size());
}
}
static addNumericAttribute<T extends CoreObjectType>(
object: ObjectContent<T>,
attribName: string,
size: AttribSize = 1,
defaultValue: NumericAttribValue = 0
) {
_warnOverloadRequired('addNumericAttribute');
}
//
static markAttribAsNeedsUpdate<T extends CoreObjectType>(object: ObjectContent<T>, attribName: string) {
_warnOverloadRequired('markAttribAsNeedsUpdate');
}
//
//
// RELATED ENTITIES
//
//
static override relatedPrimitiveIds<T extends CoreObjectType>(
object: ObjectContent<T>,
pointIndex: number,
target: number[],
traversedRelatedEntityData?: TraversedRelatedEntityData
): void {
const ids = traversedRelatedEntityData
? traversedRelatedEntityData[AttribClass.VERTEX].ids
: _relatedPrimitiveIds;
this.relatedVertexIds(object, pointIndex, ids);
uniqRelatedEntityIds(
ids,
(vertexId, relatedEntityIds) => {
this.relatedVertexClass(object).relatedPrimitiveIds(object, vertexId, relatedEntityIds);
},
target
);
}
static override relatedPrimitiveClass<T extends CoreObjectType>(object: ObjectContent<T>) {
return this.relatedVertexClass(object).relatedPrimitiveClass(object);
}
static override relatedObjectClass<T extends CoreObjectType>(object: ObjectContent<T>) {
return this.relatedPrimitiveClass(object).relatedObjectClass(object);
}
relatedEntities(
attribClass: AttribClass,
coreGroup: CoreGroup,
target: CoreEntity[],
traversedRelatedEntityData?: TraversedRelatedEntityData
): void {
switch (attribClass) {
case AttribClass.POINT: {
target.length = 1;
target[0] = this;
return;
}
case AttribClass.VERTEX: {
return this.relatedVertices(target as CoreEntityWithObject<T>[], traversedRelatedEntityData);
}
case AttribClass.PRIMITIVE: {
return this.relatedPrimitives(target as CoreEntityWithObject<T>[], traversedRelatedEntityData);
}
case AttribClass.OBJECT: {
if (this._object) {
target.length = 1;
target[0] = coreObjectInstanceFactory(this._object) as any as CoreEntityWithObject<T>;
} else {
target.length = 0;
}
return;
}
case AttribClass.CORE_GROUP: {
target.length = 1;
target[0] = coreGroup;
return;
}
}
TypeAssert.unreachable(attribClass);
}
}
export type BaseCorePoint = CorePoint<CoreObjectType>;