mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
Co-authored-by: sctanf <36978460+sctanf@users.noreply.github.com> Co-authored-by: Butterscotch! <bscotchvanilla@gmail.com> Co-authored-by: Aed <145398159+Aed-1@users.noreply.github.com>
521 lines
17 KiB
TypeScript
521 lines
17 KiB
TypeScript
import { Bone, Color, Matrix4, Object3D, Vector2, Vector3 } from 'three';
|
|
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2';
|
|
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';
|
|
import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';
|
|
import { BodyPart, BoneT } from 'solarxr-protocol';
|
|
import { Vector3FromVec3fT } from '@/maths/vector3';
|
|
import { QuaternionFromQuatT } from '@/maths/quaternion';
|
|
|
|
const _vector = new Vector3();
|
|
const _boneMatrix = new Matrix4();
|
|
const _matrixWorldInv = new Matrix4();
|
|
|
|
export class BasedSkeletonHelper extends LineSegments2 {
|
|
isSkeletonHelper: boolean;
|
|
root: Object3D;
|
|
bones: Bone[];
|
|
readonly type = 'SkeletonHelper';
|
|
|
|
get resolution() {
|
|
return this.material.resolution;
|
|
}
|
|
set resolution(v: Vector2) {
|
|
this.material.resolution = v;
|
|
}
|
|
|
|
constructor(object: Object3D) {
|
|
const bones = getBoneList(object);
|
|
|
|
const geometry = new LineSegmentsGeometry();
|
|
|
|
const vertices = [];
|
|
const colors = [];
|
|
|
|
const color1 = new Color(0, 0, 1);
|
|
const color2 = new Color(0, 1, 0);
|
|
|
|
for (let i = 0; i < bones.length; i++) {
|
|
const bone = bones[i];
|
|
|
|
if (bone.parent && (bone.parent as Bone).isBone) {
|
|
vertices.push(0, 0, 0);
|
|
vertices.push(0, 0, 0);
|
|
if (bone.parent instanceof BoneKind) {
|
|
const color = bone.parent.boneColor;
|
|
colors.push(color.r, color.g, color.b);
|
|
colors.push(color.r, color.g, color.b);
|
|
} else {
|
|
colors.push(color1.r, color1.g, color1.b);
|
|
colors.push(color2.r, color2.g, color2.b);
|
|
}
|
|
}
|
|
}
|
|
|
|
geometry.setPositions(vertices);
|
|
geometry.setColors(colors);
|
|
|
|
const material = new LineMaterial({
|
|
vertexColors: true,
|
|
// depthTest: false,
|
|
// depthWrite: false,
|
|
toneMapped: false,
|
|
transparent: true,
|
|
linewidth: 4,
|
|
});
|
|
|
|
super(geometry, material);
|
|
|
|
this.isSkeletonHelper = true;
|
|
|
|
this.root = object;
|
|
this.bones = bones;
|
|
|
|
this.matrix = object.matrixWorld;
|
|
this.matrixAutoUpdate = false;
|
|
}
|
|
|
|
updateMatrixWorld(force: boolean) {
|
|
const bones = this.bones;
|
|
|
|
const geometry = this.geometry;
|
|
const vertices = [];
|
|
|
|
_matrixWorldInv.copy(this.root.matrixWorld).invert();
|
|
|
|
for (let i = 0; i < bones.length; i++) {
|
|
const bone = bones[i];
|
|
|
|
if (bone.parent && (bone.parent as Bone).isBone) {
|
|
_boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld);
|
|
_vector.setFromMatrixPosition(_boneMatrix);
|
|
vertices.push(_vector.x, _vector.y, _vector.z);
|
|
|
|
_boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld);
|
|
_vector.setFromMatrixPosition(_boneMatrix);
|
|
vertices.push(_vector.x, _vector.y, _vector.z);
|
|
}
|
|
}
|
|
|
|
geometry.setPositions(vertices);
|
|
|
|
super.updateMatrixWorld(force);
|
|
}
|
|
|
|
dispose() {
|
|
this.geometry.dispose();
|
|
if (Array.isArray(this.material)) {
|
|
this.material.forEach((x) => x.dispose());
|
|
} else {
|
|
this.material.dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
function getBoneList(object: Object3D): Bone[] {
|
|
const boneList: Bone[] = [];
|
|
|
|
if ((object as Bone).isBone) {
|
|
boneList.push(object as Bone);
|
|
}
|
|
|
|
for (let i = 0; i < object.children.length; i++) {
|
|
boneList.push(...getBoneList(object.children[i]));
|
|
}
|
|
|
|
return boneList;
|
|
}
|
|
|
|
export class BoneKind extends Bone {
|
|
boneT: BoneT;
|
|
tail: boolean;
|
|
|
|
constructor(bones: Map<BodyPart, BoneT>, bodyPart: BodyPart, tail: boolean) {
|
|
super();
|
|
const bone = bones.get(bodyPart);
|
|
if (!bone) {
|
|
throw 'Couldnt find bone ' + BodyPart[bodyPart];
|
|
}
|
|
this.boneT = bone;
|
|
this.name = BodyPart[bodyPart];
|
|
this.tail = tail;
|
|
this.updateData(bones);
|
|
}
|
|
|
|
updateData(bones: Map<BodyPart, BoneT>) {
|
|
this.boneT = bones.get(this.boneT.bodyPart) ?? this.boneT;
|
|
const parent = BoneKind.parent(this.boneT.bodyPart);
|
|
const parentBone = parent === null ? undefined : bones.get(parent);
|
|
if (this.boneT.bodyPart === BoneKind.root) {
|
|
this.position.set(0, this.boneT.headPositionG?.y ?? 0, 0);
|
|
return;
|
|
}
|
|
|
|
if (!this.tail) {
|
|
const localPosition = Vector3FromVec3fT(this.boneT.headPositionG).sub(
|
|
Vector3FromVec3fT(
|
|
parentBone === undefined
|
|
? new Vector3(0, 0, 0)
|
|
: Vector3FromVec3fT(parentBone.headPositionG)
|
|
)
|
|
);
|
|
this.position.copy(localPosition);
|
|
return;
|
|
}
|
|
|
|
const quat = QuaternionFromQuatT(this.boneT.rotationG)
|
|
// .normalize()
|
|
// .multiply(
|
|
// parentBone === undefined
|
|
// ? new Quaternion().identity()
|
|
// : QuaternionFromQuatT(parentBone.rotationG).normalize().invert().normalize()
|
|
// )
|
|
.normalize();
|
|
|
|
// console.log(this.quaternion);
|
|
// console.log(
|
|
// parentBone === undefined
|
|
// ? new Vector3(0, 0, 0)
|
|
// : Vector3FromVec3fT(parentBone.headPositionG),
|
|
// Vector3FromVec3fT(this.boneT.headPositionG)
|
|
// );
|
|
this.position.set(0, -this.boneT.boneLength, 0);
|
|
this.position.applyQuaternion(quat);
|
|
// console.log(this.position);
|
|
}
|
|
|
|
get boneColor(): Color {
|
|
switch (this.boneT.bodyPart) {
|
|
case BodyPart.NONE:
|
|
throw 'Unexpected body part';
|
|
case BodyPart.HEAD:
|
|
return new Color('gold');
|
|
case BodyPart.NECK:
|
|
return new Color('silver');
|
|
case BodyPart.UPPER_CHEST:
|
|
return new Color('chartreuse');
|
|
case BodyPart.CHEST:
|
|
return new Color('purple');
|
|
case BodyPart.WAIST:
|
|
return new Color('red');
|
|
case BodyPart.HIP:
|
|
return new Color('orange');
|
|
case BodyPart.LEFT_UPPER_LEG:
|
|
case BodyPart.RIGHT_UPPER_LEG:
|
|
return new Color('chartreuse');
|
|
case BodyPart.LEFT_LOWER_LEG:
|
|
case BodyPart.RIGHT_LOWER_LEG:
|
|
return new Color('teal');
|
|
case BodyPart.LEFT_FOOT:
|
|
case BodyPart.RIGHT_FOOT:
|
|
return new Color('gold');
|
|
case BodyPart.LEFT_LOWER_ARM:
|
|
case BodyPart.RIGHT_LOWER_ARM:
|
|
return new Color('red');
|
|
case BodyPart.LEFT_UPPER_ARM:
|
|
case BodyPart.RIGHT_UPPER_ARM:
|
|
return new Color('indianred');
|
|
case BodyPart.LEFT_HAND:
|
|
case BodyPart.RIGHT_HAND:
|
|
return new Color('fuchsia');
|
|
case BodyPart.LEFT_SHOULDER:
|
|
case BodyPart.RIGHT_SHOULDER:
|
|
return new Color('#00ffff');
|
|
case BodyPart.LEFT_HIP:
|
|
case BodyPart.RIGHT_HIP:
|
|
return new Color('pink');
|
|
case BodyPart.LEFT_THUMB_METACARPAL:
|
|
case BodyPart.LEFT_THUMB_PROXIMAL:
|
|
case BodyPart.LEFT_THUMB_DISTAL:
|
|
case BodyPart.LEFT_INDEX_PROXIMAL:
|
|
case BodyPart.LEFT_INDEX_INTERMEDIATE:
|
|
case BodyPart.LEFT_INDEX_DISTAL:
|
|
case BodyPart.LEFT_MIDDLE_PROXIMAL:
|
|
case BodyPart.LEFT_MIDDLE_INTERMEDIATE:
|
|
case BodyPart.LEFT_MIDDLE_DISTAL:
|
|
case BodyPart.LEFT_RING_PROXIMAL:
|
|
case BodyPart.LEFT_RING_INTERMEDIATE:
|
|
case BodyPart.LEFT_RING_DISTAL:
|
|
case BodyPart.LEFT_LITTLE_PROXIMAL:
|
|
case BodyPart.LEFT_LITTLE_INTERMEDIATE:
|
|
case BodyPart.LEFT_LITTLE_DISTAL:
|
|
case BodyPart.RIGHT_THUMB_METACARPAL:
|
|
case BodyPart.RIGHT_THUMB_PROXIMAL:
|
|
case BodyPart.RIGHT_THUMB_DISTAL:
|
|
case BodyPart.RIGHT_INDEX_PROXIMAL:
|
|
case BodyPart.RIGHT_INDEX_INTERMEDIATE:
|
|
case BodyPart.RIGHT_INDEX_DISTAL:
|
|
case BodyPart.RIGHT_MIDDLE_PROXIMAL:
|
|
case BodyPart.RIGHT_MIDDLE_INTERMEDIATE:
|
|
case BodyPart.RIGHT_MIDDLE_DISTAL:
|
|
case BodyPart.RIGHT_RING_PROXIMAL:
|
|
case BodyPart.RIGHT_RING_INTERMEDIATE:
|
|
case BodyPart.RIGHT_RING_DISTAL:
|
|
case BodyPart.RIGHT_LITTLE_PROXIMAL:
|
|
case BodyPart.RIGHT_LITTLE_INTERMEDIATE:
|
|
case BodyPart.RIGHT_LITTLE_DISTAL:
|
|
return new Color('pink');
|
|
}
|
|
}
|
|
|
|
static root: BodyPart = BodyPart.HEAD;
|
|
|
|
static children(part: BodyPart): BodyPart[] {
|
|
switch (part) {
|
|
case BodyPart.NONE:
|
|
throw 'Unexpected body part';
|
|
case BodyPart.HEAD:
|
|
return [BodyPart.NECK];
|
|
case BodyPart.NECK:
|
|
return [BodyPart.UPPER_CHEST, BodyPart.LEFT_SHOULDER, BodyPart.RIGHT_SHOULDER];
|
|
case BodyPart.UPPER_CHEST:
|
|
return [BodyPart.CHEST];
|
|
case BodyPart.CHEST:
|
|
return [BodyPart.WAIST];
|
|
case BodyPart.WAIST:
|
|
return [BodyPart.HIP];
|
|
case BodyPart.HIP:
|
|
return [BodyPart.LEFT_HIP, BodyPart.RIGHT_HIP];
|
|
|
|
case BodyPart.LEFT_HIP:
|
|
return [BodyPart.LEFT_UPPER_LEG];
|
|
case BodyPart.RIGHT_HIP:
|
|
return [BodyPart.RIGHT_UPPER_LEG];
|
|
case BodyPart.LEFT_UPPER_LEG:
|
|
return [BodyPart.LEFT_LOWER_LEG];
|
|
case BodyPart.RIGHT_UPPER_LEG:
|
|
return [BodyPart.RIGHT_LOWER_LEG];
|
|
case BodyPart.LEFT_LOWER_LEG:
|
|
return [BodyPart.LEFT_FOOT];
|
|
case BodyPart.RIGHT_LOWER_LEG:
|
|
return [BodyPart.RIGHT_FOOT];
|
|
case BodyPart.LEFT_FOOT:
|
|
return [];
|
|
case BodyPart.RIGHT_FOOT:
|
|
return [];
|
|
|
|
case BodyPart.LEFT_SHOULDER:
|
|
return [BodyPart.LEFT_UPPER_ARM];
|
|
case BodyPart.RIGHT_SHOULDER:
|
|
return [BodyPart.RIGHT_UPPER_ARM];
|
|
case BodyPart.LEFT_UPPER_ARM:
|
|
return [BodyPart.LEFT_LOWER_ARM];
|
|
case BodyPart.RIGHT_UPPER_ARM:
|
|
return [BodyPart.RIGHT_LOWER_ARM];
|
|
case BodyPart.LEFT_LOWER_ARM:
|
|
return [BodyPart.LEFT_HAND];
|
|
case BodyPart.RIGHT_LOWER_ARM:
|
|
return [BodyPart.RIGHT_HAND];
|
|
case BodyPart.LEFT_HAND:
|
|
return [
|
|
BodyPart.LEFT_THUMB_METACARPAL,
|
|
BodyPart.LEFT_INDEX_PROXIMAL,
|
|
BodyPart.LEFT_MIDDLE_PROXIMAL,
|
|
BodyPart.LEFT_RING_PROXIMAL,
|
|
BodyPart.LEFT_LITTLE_PROXIMAL,
|
|
];
|
|
case BodyPart.RIGHT_HAND:
|
|
return [
|
|
BodyPart.RIGHT_THUMB_METACARPAL,
|
|
BodyPart.RIGHT_INDEX_PROXIMAL,
|
|
BodyPart.RIGHT_MIDDLE_PROXIMAL,
|
|
BodyPart.RIGHT_RING_PROXIMAL,
|
|
BodyPart.RIGHT_LITTLE_PROXIMAL,
|
|
];
|
|
|
|
case BodyPart.LEFT_THUMB_METACARPAL:
|
|
return [BodyPart.LEFT_THUMB_PROXIMAL];
|
|
case BodyPart.LEFT_THUMB_PROXIMAL:
|
|
return [BodyPart.LEFT_THUMB_DISTAL];
|
|
case BodyPart.LEFT_THUMB_DISTAL:
|
|
return [];
|
|
case BodyPart.LEFT_INDEX_PROXIMAL:
|
|
return [BodyPart.LEFT_INDEX_INTERMEDIATE];
|
|
case BodyPart.LEFT_INDEX_INTERMEDIATE:
|
|
return [BodyPart.LEFT_INDEX_DISTAL];
|
|
case BodyPart.LEFT_INDEX_DISTAL:
|
|
return [];
|
|
case BodyPart.LEFT_MIDDLE_PROXIMAL:
|
|
return [BodyPart.LEFT_MIDDLE_INTERMEDIATE];
|
|
case BodyPart.LEFT_MIDDLE_INTERMEDIATE:
|
|
return [BodyPart.LEFT_MIDDLE_DISTAL];
|
|
case BodyPart.LEFT_MIDDLE_DISTAL:
|
|
return [];
|
|
case BodyPart.LEFT_RING_PROXIMAL:
|
|
return [BodyPart.LEFT_RING_INTERMEDIATE];
|
|
case BodyPart.LEFT_RING_INTERMEDIATE:
|
|
return [BodyPart.LEFT_RING_DISTAL];
|
|
case BodyPart.LEFT_RING_DISTAL:
|
|
return [];
|
|
case BodyPart.LEFT_LITTLE_PROXIMAL:
|
|
return [BodyPart.LEFT_LITTLE_INTERMEDIATE];
|
|
case BodyPart.LEFT_LITTLE_INTERMEDIATE:
|
|
return [BodyPart.LEFT_LITTLE_DISTAL];
|
|
case BodyPart.LEFT_LITTLE_DISTAL:
|
|
return [];
|
|
case BodyPart.RIGHT_THUMB_METACARPAL:
|
|
return [BodyPart.RIGHT_THUMB_PROXIMAL];
|
|
case BodyPart.RIGHT_THUMB_PROXIMAL:
|
|
return [BodyPart.RIGHT_THUMB_DISTAL];
|
|
case BodyPart.RIGHT_THUMB_DISTAL:
|
|
return [];
|
|
case BodyPart.RIGHT_INDEX_PROXIMAL:
|
|
return [BodyPart.RIGHT_INDEX_INTERMEDIATE];
|
|
case BodyPart.RIGHT_INDEX_INTERMEDIATE:
|
|
return [BodyPart.RIGHT_INDEX_DISTAL];
|
|
case BodyPart.RIGHT_INDEX_DISTAL:
|
|
return [];
|
|
case BodyPart.RIGHT_MIDDLE_PROXIMAL:
|
|
return [BodyPart.RIGHT_MIDDLE_INTERMEDIATE];
|
|
case BodyPart.RIGHT_MIDDLE_INTERMEDIATE:
|
|
return [BodyPart.RIGHT_MIDDLE_DISTAL];
|
|
case BodyPart.RIGHT_MIDDLE_DISTAL:
|
|
return [];
|
|
case BodyPart.RIGHT_RING_PROXIMAL:
|
|
return [BodyPart.RIGHT_RING_INTERMEDIATE];
|
|
case BodyPart.RIGHT_RING_INTERMEDIATE:
|
|
return [BodyPart.RIGHT_RING_DISTAL];
|
|
case BodyPart.RIGHT_RING_DISTAL:
|
|
return [];
|
|
case BodyPart.RIGHT_LITTLE_PROXIMAL:
|
|
return [BodyPart.RIGHT_LITTLE_INTERMEDIATE];
|
|
case BodyPart.RIGHT_LITTLE_INTERMEDIATE:
|
|
return [BodyPart.RIGHT_LITTLE_DISTAL];
|
|
case BodyPart.RIGHT_LITTLE_DISTAL:
|
|
return [];
|
|
}
|
|
}
|
|
|
|
static parent(part: BodyPart): BodyPart | null {
|
|
switch (part) {
|
|
case BodyPart.NONE:
|
|
throw 'Unexpected body part';
|
|
case BodyPart.HEAD:
|
|
return null;
|
|
case BodyPart.NECK:
|
|
return BodyPart.HEAD;
|
|
case BodyPart.UPPER_CHEST:
|
|
return BodyPart.NECK;
|
|
case BodyPart.CHEST:
|
|
return BodyPart.UPPER_CHEST;
|
|
case BodyPart.WAIST:
|
|
return BodyPart.CHEST;
|
|
case BodyPart.HIP:
|
|
return BodyPart.WAIST;
|
|
|
|
case BodyPart.LEFT_HIP:
|
|
case BodyPart.RIGHT_HIP:
|
|
return BodyPart.HIP;
|
|
case BodyPart.LEFT_UPPER_LEG:
|
|
return BodyPart.LEFT_HIP;
|
|
case BodyPart.RIGHT_UPPER_LEG:
|
|
return BodyPart.RIGHT_HIP;
|
|
case BodyPart.LEFT_LOWER_LEG:
|
|
return BodyPart.LEFT_UPPER_LEG;
|
|
case BodyPart.RIGHT_LOWER_LEG:
|
|
return BodyPart.RIGHT_UPPER_LEG;
|
|
case BodyPart.LEFT_FOOT:
|
|
return BodyPart.LEFT_LOWER_LEG;
|
|
case BodyPart.RIGHT_FOOT:
|
|
return BodyPart.RIGHT_LOWER_LEG;
|
|
|
|
case BodyPart.LEFT_SHOULDER:
|
|
case BodyPart.RIGHT_SHOULDER:
|
|
return BodyPart.NECK;
|
|
case BodyPart.LEFT_UPPER_ARM:
|
|
return BodyPart.LEFT_SHOULDER;
|
|
case BodyPart.RIGHT_UPPER_ARM:
|
|
return BodyPart.RIGHT_SHOULDER;
|
|
case BodyPart.LEFT_LOWER_ARM:
|
|
return BodyPart.LEFT_UPPER_ARM;
|
|
case BodyPart.RIGHT_LOWER_ARM:
|
|
return BodyPart.RIGHT_UPPER_ARM;
|
|
case BodyPart.LEFT_HAND:
|
|
return BodyPart.LEFT_LOWER_ARM;
|
|
case BodyPart.RIGHT_HAND:
|
|
return BodyPart.RIGHT_LOWER_ARM;
|
|
|
|
case BodyPart.LEFT_THUMB_METACARPAL:
|
|
return BodyPart.LEFT_HAND;
|
|
case BodyPart.LEFT_THUMB_PROXIMAL:
|
|
return BodyPart.LEFT_THUMB_METACARPAL;
|
|
case BodyPart.LEFT_THUMB_DISTAL:
|
|
return BodyPart.LEFT_THUMB_PROXIMAL;
|
|
case BodyPart.LEFT_INDEX_PROXIMAL:
|
|
return BodyPart.LEFT_HAND;
|
|
case BodyPart.LEFT_INDEX_INTERMEDIATE:
|
|
return BodyPart.LEFT_INDEX_PROXIMAL;
|
|
case BodyPart.LEFT_INDEX_DISTAL:
|
|
return BodyPart.LEFT_INDEX_INTERMEDIATE;
|
|
case BodyPart.LEFT_MIDDLE_PROXIMAL:
|
|
return BodyPart.LEFT_HAND;
|
|
case BodyPart.LEFT_MIDDLE_INTERMEDIATE:
|
|
return BodyPart.LEFT_MIDDLE_PROXIMAL;
|
|
case BodyPart.LEFT_MIDDLE_DISTAL:
|
|
return BodyPart.LEFT_MIDDLE_INTERMEDIATE;
|
|
case BodyPart.LEFT_RING_PROXIMAL:
|
|
return BodyPart.LEFT_HAND;
|
|
case BodyPart.LEFT_RING_INTERMEDIATE:
|
|
return BodyPart.LEFT_RING_PROXIMAL;
|
|
case BodyPart.LEFT_RING_DISTAL:
|
|
return BodyPart.LEFT_RING_INTERMEDIATE;
|
|
case BodyPart.LEFT_LITTLE_PROXIMAL:
|
|
return BodyPart.LEFT_HAND;
|
|
case BodyPart.LEFT_LITTLE_INTERMEDIATE:
|
|
return BodyPart.LEFT_LITTLE_PROXIMAL;
|
|
case BodyPart.LEFT_LITTLE_DISTAL:
|
|
return BodyPart.LEFT_LITTLE_INTERMEDIATE;
|
|
case BodyPart.RIGHT_THUMB_METACARPAL:
|
|
return BodyPart.RIGHT_HAND;
|
|
case BodyPart.RIGHT_THUMB_PROXIMAL:
|
|
return BodyPart.RIGHT_THUMB_METACARPAL;
|
|
case BodyPart.RIGHT_THUMB_DISTAL:
|
|
return BodyPart.RIGHT_THUMB_PROXIMAL;
|
|
case BodyPart.RIGHT_INDEX_PROXIMAL:
|
|
return BodyPart.RIGHT_HAND;
|
|
case BodyPart.RIGHT_INDEX_INTERMEDIATE:
|
|
return BodyPart.RIGHT_INDEX_PROXIMAL;
|
|
case BodyPart.RIGHT_INDEX_DISTAL:
|
|
return BodyPart.RIGHT_INDEX_INTERMEDIATE;
|
|
case BodyPart.RIGHT_MIDDLE_PROXIMAL:
|
|
return BodyPart.RIGHT_HAND;
|
|
case BodyPart.RIGHT_MIDDLE_INTERMEDIATE:
|
|
return BodyPart.RIGHT_MIDDLE_PROXIMAL;
|
|
case BodyPart.RIGHT_MIDDLE_DISTAL:
|
|
return BodyPart.RIGHT_MIDDLE_INTERMEDIATE;
|
|
case BodyPart.RIGHT_RING_PROXIMAL:
|
|
return BodyPart.RIGHT_HAND;
|
|
case BodyPart.RIGHT_RING_INTERMEDIATE:
|
|
return BodyPart.RIGHT_RING_PROXIMAL;
|
|
case BodyPart.RIGHT_RING_DISTAL:
|
|
return BodyPart.RIGHT_RING_INTERMEDIATE;
|
|
case BodyPart.RIGHT_LITTLE_PROXIMAL:
|
|
return BodyPart.RIGHT_HAND;
|
|
case BodyPart.RIGHT_LITTLE_INTERMEDIATE:
|
|
return BodyPart.RIGHT_LITTLE_PROXIMAL;
|
|
case BodyPart.RIGHT_LITTLE_DISTAL:
|
|
return BodyPart.RIGHT_LITTLE_INTERMEDIATE;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function createChildren(
|
|
bones: Map<BodyPart, BoneT>,
|
|
body: BodyPart,
|
|
parentBone?: BoneKind
|
|
): (BoneKind | Bone)[] {
|
|
if (bones.size === 0) return [new Bone()];
|
|
const childrenBodies = BoneKind.children(body);
|
|
const parent = new BoneKind(bones, body, false);
|
|
parentBone?.add(parent);
|
|
if (childrenBodies.length === 0) {
|
|
const tail = new BoneKind(bones, body, true);
|
|
parent.add(tail);
|
|
return [parent, tail];
|
|
}
|
|
|
|
const children = childrenBodies.flatMap((bodyPart) =>
|
|
createChildren(bones, bodyPart, parent)
|
|
);
|
|
return [parent, ...children];
|
|
}
|