@judah-silva/rnacanvas 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -129,3 +129,7 @@ const props: MotifProps = {
129
129
  locked: false,
130
130
  };
131
131
  ```
132
+
133
+
134
+
135
+ test
package/dist/index.d.mts CHANGED
@@ -8,7 +8,16 @@ import { Mesh, TransformNode, Scene, Matrix, Nullable, Quaternion, Observer, Eng
8
8
  declare class Residue extends Group<MeshObject> {
9
9
  constructor(name: string);
10
10
  addChild(child: MeshObject): void;
11
+ /**
12
+ * Checks if the residue has a mesh with the given uuid
13
+ * @param uuid
14
+ */
11
15
  hasMesh(uuid: string): boolean;
16
+ /**
17
+ * Cretes a material with the given color and sets it to the mesh
18
+ * @param color String in the format of #RRGGBB
19
+ */
20
+ colorResidue(color: string): void;
12
21
  }
13
22
 
14
23
  /**
@@ -297,6 +306,7 @@ declare class RenderScene {
297
306
  dispose(): void;
298
307
  add(motif: Motif): void;
299
308
  remove(motif: Motif): void;
309
+ removeAll(): void;
300
310
  setBackgroundColor(hexColor: string): void;
301
311
  private _reattachToScene;
302
312
  private _handleResize;
@@ -460,8 +470,8 @@ declare class CanvasDataManager {
460
470
  private static _listeners;
461
471
  static get selectedMotifIds(): Set<string>;
462
472
  static setSelectedMotifIds(selectedMotifIds: Set<string>): void;
463
- static get lockedMotifIds(): string[];
464
- static setLockedMotifIds(lockedMotifIds: string[]): void;
473
+ static get lockedMotifIds(): Set<string>;
474
+ static setLockedMotifIds(lockedMotifIds: Set<string>): void;
465
475
  static get hardLockedMotifIds(): string[];
466
476
  static setHardLockedMotifIds(hardLockedMotifIds: string[]): void;
467
477
  static get scoreRMSD(): ScoreInfo[][];
package/dist/index.d.ts CHANGED
@@ -8,7 +8,16 @@ import { Mesh, TransformNode, Scene, Matrix, Nullable, Quaternion, Observer, Eng
8
8
  declare class Residue extends Group<MeshObject> {
9
9
  constructor(name: string);
10
10
  addChild(child: MeshObject): void;
11
+ /**
12
+ * Checks if the residue has a mesh with the given uuid
13
+ * @param uuid
14
+ */
11
15
  hasMesh(uuid: string): boolean;
16
+ /**
17
+ * Cretes a material with the given color and sets it to the mesh
18
+ * @param color String in the format of #RRGGBB
19
+ */
20
+ colorResidue(color: string): void;
12
21
  }
13
22
 
14
23
  /**
@@ -297,6 +306,7 @@ declare class RenderScene {
297
306
  dispose(): void;
298
307
  add(motif: Motif): void;
299
308
  remove(motif: Motif): void;
309
+ removeAll(): void;
300
310
  setBackgroundColor(hexColor: string): void;
301
311
  private _reattachToScene;
302
312
  private _handleResize;
@@ -460,8 +470,8 @@ declare class CanvasDataManager {
460
470
  private static _listeners;
461
471
  static get selectedMotifIds(): Set<string>;
462
472
  static setSelectedMotifIds(selectedMotifIds: Set<string>): void;
463
- static get lockedMotifIds(): string[];
464
- static setLockedMotifIds(lockedMotifIds: string[]): void;
473
+ static get lockedMotifIds(): Set<string>;
474
+ static setLockedMotifIds(lockedMotifIds: Set<string>): void;
465
475
  static get hardLockedMotifIds(): string[];
466
476
  static setHardLockedMotifIds(hardLockedMotifIds: string[]): void;
467
477
  static get scoreRMSD(): ScoreInfo[][];
package/dist/index.js CHANGED
@@ -312,7 +312,10 @@ var Residue = class extends Group {
312
312
  child.setParent(this);
313
313
  this._children.add(child);
314
314
  }
315
- // Temporary function to find if Residue contains mesh with uuid
315
+ /**
316
+ * Checks if the residue has a mesh with the given uuid
317
+ * @param uuid
318
+ */
316
319
  hasMesh(uuid) {
317
320
  let found = false;
318
321
  this._children.forEach((child) => {
@@ -322,6 +325,15 @@ var Residue = class extends Group {
322
325
  });
323
326
  return found;
324
327
  }
328
+ /**
329
+ * Cretes a material with the given color and sets it to the mesh
330
+ * @param color String in the format of #RRGGBB
331
+ */
332
+ colorResidue(color) {
333
+ this._children.forEach((child) => {
334
+ child.createAndSetMaterial(color);
335
+ });
336
+ }
325
337
  };
326
338
 
327
339
  // src/3D/MeshObject.ts
@@ -366,6 +378,7 @@ var MeshObject = class {
366
378
  const color3 = import_core7.Color3.FromHexString(`#${color.replace(/^0x/, "")}`);
367
379
  mat.diffuseColor = color3;
368
380
  mat.specularColor = color3;
381
+ mat.backFaceCulling = false;
369
382
  this._mesh.material = mat;
370
383
  }
371
384
  setNewMesh(mesh) {
@@ -591,9 +604,19 @@ var EventManager = class {
591
604
  canceled: false,
592
605
  timestamp: performance.now()
593
606
  };
594
- if (keyboardInfo.type === import_core8.KeyboardEventTypes.KEYDOWN) {
607
+ if (keyboardInfo.type === import_core8.KeyboardEventTypes.KEYDOWN && /^[wasdqeWASD]$/.test(event.key)) {
608
+ if (event.key.charCodeAt(0) >= 97) {
609
+ this._activeKeys.delete(event.key.toUpperCase());
610
+ } else {
611
+ this._activeKeys.delete(event.key.toLowerCase());
612
+ }
595
613
  this._activeKeys.add(event.key);
596
614
  } else if (keyboardInfo.type === import_core8.KeyboardEventTypes.KEYUP) {
615
+ if (event.key.charCodeAt(0) >= 97) {
616
+ this._activeKeys.delete(event.key.toUpperCase());
617
+ } else {
618
+ this._activeKeys.delete(event.key.toLowerCase());
619
+ }
597
620
  this._activeKeys.delete(event.key);
598
621
  }
599
622
  if (this._activeKeys.has("w")) keyboardEvent.rotationAxis.add(new Vec3(-1, 0, 0));
@@ -733,7 +756,15 @@ var RenderScene = class {
733
756
  return;
734
757
  }
735
758
  this._children.delete(motif.uuid);
736
- this._scene.removeTransformNode(motif.node);
759
+ motif.node.dispose(false, true);
760
+ }
761
+ removeAll() {
762
+ this._scene.meshes.slice().forEach((mesh) => {
763
+ if (mesh && this._scene.getMeshById(mesh.id)) {
764
+ this._scene.removeMesh(mesh);
765
+ }
766
+ });
767
+ this._children.clear();
737
768
  }
738
769
  setBackgroundColor(hexColor) {
739
770
  this._scene.clearColor = import_core9.Color4.FromHexString(`${hexColor.replace(/^0x/, "")}`);
@@ -750,7 +781,7 @@ var RenderScene = class {
750
781
  geo?.applyToMesh(mesh);
751
782
  mesh.material = mat;
752
783
  currObj.setNewMesh(mesh);
753
- node.dispose();
784
+ node.dispose(false, true);
754
785
  }
755
786
  if (!(currObj instanceof MeshObject)) {
756
787
  currObj.children.forEach((childObj) => {
@@ -883,7 +914,7 @@ var CanvasAttributeTypes = /* @__PURE__ */ ((CanvasAttributeTypes2) => {
883
914
  })(CanvasAttributeTypes || {});
884
915
  var CanvasDataManager = class {
885
916
  static _selectedMotifIds = /* @__PURE__ */ new Set();
886
- static _lockedMotifIds = [];
917
+ static _lockedMotifIds = /* @__PURE__ */ new Set();
887
918
  static _hardLockedMotifIds = [];
888
919
  static _scoreRMSD = [];
889
920
  static _kabschRMSD = [];
@@ -1140,28 +1171,27 @@ function Canvas({
1140
1171
  }) {
1141
1172
  const canvasRef = (0, import_react.useRef)(null);
1142
1173
  const scene = (0, import_react.useRef)(null);
1143
- const motifs = [];
1174
+ const motifs = (0, import_react.useRef)([]);
1144
1175
  let hardLockedMotifIds = [];
1145
1176
  motifProps.forEach((motifProp) => {
1146
- motifs.push(motifProp.motif);
1147
1177
  if (motifProp.locked) hardLockedMotifIds.push(motifProp.motif.uuid);
1148
1178
  });
1149
1179
  CanvasDataManager.setHardLockedMotifIds(hardLockedMotifIds);
1150
1180
  const selectedMotifMeshState = (0, import_react.useRef)(/* @__PURE__ */ new Set());
1151
- const lockedMotifIdState = (0, import_react.useRef)([]);
1181
+ const lockedMotifIdState = (0, import_react.useRef)(/* @__PURE__ */ new Set());
1152
1182
  const [cursorStyle, setCursorStyle] = (0, import_react.useState)("auto");
1153
1183
  const [selectedMotifIds, setSelectedmotifIds] = (0, import_react.useState)(/* @__PURE__ */ new Set());
1154
1184
  const [scoreRMSD, setScoreRMSD] = (0, import_react.useState)([]);
1155
1185
  const [kabschRMSD, setKabschRMSD] = (0, import_react.useState)([]);
1156
- const [lockedMotifIds, setLockedMotifIds] = (0, import_react.useState)([]);
1186
+ const [lockedMotifIds, setLockedMotifIds] = (0, import_react.useState)(/* @__PURE__ */ new Set());
1157
1187
  const addMotif = (motif) => {
1158
1188
  if (selectedMotifMeshState.current.has(motif)) {
1159
1189
  return;
1160
1190
  }
1161
1191
  const newSet = /* @__PURE__ */ new Set();
1162
- for (let i = 0; i < motifs.length; i += 1) {
1163
- if (selectedMotifMeshState.current.has(motifs[i]) || motifs[i].uuid === motif.uuid) {
1164
- newSet.add(motifs[i].uuid);
1192
+ for (let i = 0; i < motifs.current.length; i += 1) {
1193
+ if (selectedMotifMeshState.current.has(motifs.current[i]) || motifs.current[i].uuid === motif.uuid) {
1194
+ newSet.add(motifs.current[i].uuid);
1165
1195
  }
1166
1196
  }
1167
1197
  setSelectedmotifIds(newSet);
@@ -1179,7 +1209,7 @@ function Canvas({
1179
1209
  });
1180
1210
  };
1181
1211
  function updateGlow() {
1182
- motifs.forEach((motif) => {
1212
+ motifs.current.forEach((motif) => {
1183
1213
  motif.children.forEach((residue) => {
1184
1214
  residue.children.forEach((childMesh) => {
1185
1215
  if (selectedMotifIds.has(motif.uuid)) {
@@ -1196,7 +1226,7 @@ function Canvas({
1196
1226
  return;
1197
1227
  }
1198
1228
  const { motif } = event;
1199
- if (!motif || selectedMotifMeshState.current.has(motif) || lockedMotifIdState.current.includes(motif.uuid) || hardLockedMotifIds.includes(motif.uuid)) {
1229
+ if (!motif || selectedMotifMeshState.current.has(motif)) {
1200
1230
  return;
1201
1231
  }
1202
1232
  if (event.multiSelect && motif) {
@@ -1228,7 +1258,7 @@ function Canvas({
1228
1258
  const deltaX = rawDeltaX / renderWidth * canvasRef.current.width;
1229
1259
  const deltaY = rawDeltaY / renderHeight * canvasRef.current.height;
1230
1260
  selectedMotifMeshState.current.forEach((element) => {
1231
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1261
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1232
1262
  element.translate(-deltaX, -deltaY, 0);
1233
1263
  }
1234
1264
  });
@@ -1241,11 +1271,11 @@ function Canvas({
1241
1271
  if (scene.current) {
1242
1272
  const angle = directionVec.length() / scene.current.renderWidth * (3 * Math.PI);
1243
1273
  selectedMotifMeshState.current.forEach((element) => {
1244
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1274
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1245
1275
  element.rotate(axisVec, angle);
1246
1276
  }
1247
1277
  });
1248
- if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs));
1278
+ if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs.current));
1249
1279
  }
1250
1280
  }
1251
1281
  }
@@ -1258,7 +1288,7 @@ function Canvas({
1258
1288
  const zoomSpeed = 0.1;
1259
1289
  const zoomDirection = event.originalEvent.deltaY > 0 ? -1 : 1;
1260
1290
  selectedMotifMeshState.current.forEach((element) => {
1261
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid) && !(element.scale <= 1 && zoomDirection === -1) && !(element.scale >= 30 && zoomDirection === 1)) {
1291
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid) && !(element.scale <= 1 && zoomDirection === -1) && !(element.scale >= 30 && zoomDirection === 1)) {
1262
1292
  const scaleFactor = 1 + zoomDirection * zoomSpeed;
1263
1293
  element.multiplyScalar(scaleFactor);
1264
1294
  }
@@ -1286,19 +1316,19 @@ function Canvas({
1286
1316
  }
1287
1317
  const angle = event.rotationAxis.length() / 500 * (6 * Math.PI);
1288
1318
  selectedMotifMeshState.current.forEach((element) => {
1289
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1319
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1290
1320
  element.rotate(event.rotationAxis, angle);
1291
1321
  }
1292
1322
  });
1293
- if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs));
1323
+ if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs.current));
1294
1324
  }
1295
1325
  function onKeyboardTranslate(event) {
1296
1326
  if (event.translationDirection.equals(Vec3.Zero)) {
1297
1327
  return;
1298
1328
  }
1299
- event.translationDirection.multiplyScalar(0.5);
1329
+ event.translationDirection.multiplyScalar(4.5);
1300
1330
  selectedMotifMeshState.current.forEach((element) => {
1301
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1331
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1302
1332
  element.translate(
1303
1333
  event.translationDirection.x,
1304
1334
  event.translationDirection.y,
@@ -1311,10 +1341,10 @@ function Canvas({
1311
1341
  if (!event.rotationAxis.equals(Vec3.Zero) || !event.translationDirection.equals(Vec3.Zero)) {
1312
1342
  return;
1313
1343
  }
1314
- if (!/^[1-9]$/.test(event.key) || Number(event.key) > motifs.length) {
1344
+ if (!/^[1-9]$/.test(event.key) || Number(event.key) > motifs.current.length) {
1315
1345
  return;
1316
1346
  }
1317
- const motif = motifs[Number(event.key) - 1];
1347
+ const motif = motifs.current[Number(event.key) - 1];
1318
1348
  if (selectedMotifMeshState.current.has(motif)) {
1319
1349
  removeMotif(motif);
1320
1350
  } else {
@@ -1337,8 +1367,8 @@ function Canvas({
1337
1367
  return positions;
1338
1368
  };
1339
1369
  const updateMotifs = () => {
1340
- const positions = calculatePositions(motifs.length);
1341
- motifs.forEach((motifMesh, index) => {
1370
+ const positions = calculatePositions(motifs.current.length);
1371
+ motifs.current.forEach((motifMesh, index) => {
1342
1372
  if (!scene.current?.children.has(motifMesh.uuid)) return;
1343
1373
  if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
1344
1374
  motifMesh.setPosition(positions[index].x, positions[index].y, positions[index].z);
@@ -1348,9 +1378,6 @@ function Canvas({
1348
1378
  motifMesh.setScale(scale);
1349
1379
  });
1350
1380
  };
1351
- (0, import_react.useEffect)(() => {
1352
- updateMotifs();
1353
- }, [motifProps]);
1354
1381
  (0, import_react.useEffect)(() => {
1355
1382
  const unsubscribe = CanvasDataManager.subscribe("selectedMotifs" /* SELECTED_MOTIFS */, () => {
1356
1383
  setSelectedmotifIds(CanvasDataManager.selectedMotifIds);
@@ -1362,13 +1389,13 @@ function Canvas({
1362
1389
  CanvasDataManager.setSelectedMotifIds(selectedMotifIds);
1363
1390
  }
1364
1391
  selectedMotifMeshState.current.clear();
1365
- motifs.forEach((motif) => {
1392
+ motifs.current.forEach((motif) => {
1366
1393
  if (selectedMotifIds.has(motif.uuid)) {
1367
1394
  selectedMotifMeshState.current.add(motif);
1368
1395
  }
1369
1396
  });
1370
1397
  updateGlow();
1371
- if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs));
1398
+ if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs.current));
1372
1399
  }, [selectedMotifIds]);
1373
1400
  (0, import_react.useEffect)(() => {
1374
1401
  const unsubscribe = CanvasDataManager.subscribe("lockedMotifIds" /* LOCKED_MOTIF_IDS */, () => {
@@ -1409,8 +1436,8 @@ function Canvas({
1409
1436
  if (!showRMSD) return;
1410
1437
  if (CanvasDataManager.kabschRMSD !== kabschRMSD) {
1411
1438
  CanvasDataManager.setKabschRMSD(kabschRMSD);
1412
- } else if (CanvasDataManager.kabschRMSD.length !== motifs.length) {
1413
- CanvasDataManager.setKabschRMSD(calculateAllKabschRMSD(motifs));
1439
+ } else if (CanvasDataManager.kabschRMSD.length !== motifs.current.length) {
1440
+ CanvasDataManager.setKabschRMSD(calculateAllKabschRMSD(motifs.current));
1414
1441
  }
1415
1442
  }, [kabschRMSD]);
1416
1443
  (0, import_react.useEffect)(() => {
@@ -1429,84 +1456,91 @@ function Canvas({
1429
1456
  rendererSizeIsWindow ? window.innerWidth : rendererWidth,
1430
1457
  rendererSizeIsWindow ? window.innerHeight : rendererHeight
1431
1458
  );
1432
- }
1433
- if (motifs.length > 0) {
1434
- if (showRMSD) setKabschRMSD(calculateAllKabschRMSD(motifs));
1435
- if (scene.current.children.size !== motifs.length) {
1436
- const positions = calculatePositions(motifs.length);
1437
- motifs.forEach((motifMesh, index) => {
1438
- scene.current?.add(motifMesh);
1439
- if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
1440
- motifMesh.setPosition(positions[index].x, positions[index].y, positions[index].z);
1441
- if (motifProps[index].rotation) motifMesh.setQuaternion(motifProps[index].rotation);
1442
- let scale = canvasRef.current.width / 250;
1443
- if (motifProps[index].scale) scale = motifProps[index].scale;
1444
- motifMesh.multiplyScalar(scale);
1459
+ const eventManager = scene.current?.eventManager;
1460
+ eventManager.on(Events.EventType.OBJECT_SELECTED, onSelectMotif);
1461
+ eventManager.on(Events.EventType.OBJECT_DESELECTED, onDeselectMotif);
1462
+ eventManager.on(Events.EventType.POINTER_MOVE, onMouseMove);
1463
+ eventManager.on(Events.EventType.POINTER_WHEEL, onMouseScroll);
1464
+ eventManager.on(Events.EventType.POINTER_DOWN, onMouseDown);
1465
+ eventManager.on(Events.EventType.POINTER_UP, onMouseUp);
1466
+ eventManager.on(Events.EventType.KEY_DOWN, onKeyboardRotate);
1467
+ eventManager.on(Events.EventType.KEY_DOWN, onKeyboardTranslate);
1468
+ eventManager.on(Events.EventType.KEY_DOWN, onKeyboardSelect);
1469
+ if (customEventProps) {
1470
+ customEventProps.forEach((customEventProp) => {
1471
+ switch (customEventProp.eventType) {
1472
+ // Handle Pointer Events
1473
+ case Events.EventType.POINTER_DOWN:
1474
+ case Events.EventType.POINTER_UP:
1475
+ case Events.EventType.POINTER_MOVE:
1476
+ case Events.EventType.POINTER_WHEEL:
1477
+ case Events.EventType.TOUCH_END:
1478
+ case Events.EventType.TOUCH_MOVE:
1479
+ case Events.EventType.TOUCH_START:
1480
+ eventManager.on(
1481
+ customEventProp.eventType,
1482
+ customEventProp.callback
1483
+ );
1484
+ break;
1485
+ // Handle Keyboard Events
1486
+ case Events.EventType.KEY_DOWN:
1487
+ case Events.EventType.KEY_UP:
1488
+ eventManager.on(
1489
+ customEventProp.eventType,
1490
+ customEventProp.callback
1491
+ );
1492
+ break;
1493
+ // Handle Pinch Events
1494
+ case Events.EventType.PINCH:
1495
+ case Events.EventType.PINCH_END:
1496
+ case Events.EventType.PINCH_START:
1497
+ eventManager.on(
1498
+ customEventProp.eventType,
1499
+ customEventProp.callback
1500
+ );
1501
+ break;
1502
+ // Handle Selection Events
1503
+ case Events.EventType.OBJECT_SELECTED:
1504
+ case Events.EventType.OBJECT_DESELECTED:
1505
+ eventManager.on(
1506
+ customEventProp.eventType,
1507
+ customEventProp.callback
1508
+ );
1509
+ break;
1510
+ // Handle Events
1511
+ default:
1512
+ eventManager.on(
1513
+ customEventProp.eventType,
1514
+ customEventProp.callback
1515
+ );
1516
+ break;
1517
+ }
1445
1518
  });
1446
- const eventManager = scene.current?.eventManager;
1447
- eventManager.on(Events.EventType.OBJECT_SELECTED, onSelectMotif);
1448
- eventManager.on(Events.EventType.OBJECT_DESELECTED, onDeselectMotif);
1449
- eventManager.on(Events.EventType.POINTER_MOVE, onMouseMove);
1450
- eventManager.on(Events.EventType.POINTER_WHEEL, onMouseScroll);
1451
- eventManager.on(Events.EventType.POINTER_DOWN, onMouseDown);
1452
- eventManager.on(Events.EventType.POINTER_UP, onMouseUp);
1453
- eventManager.on(Events.EventType.KEY_DOWN, onKeyboardRotate);
1454
- eventManager.on(Events.EventType.KEY_DOWN, onKeyboardTranslate);
1455
- eventManager.on(Events.EventType.KEY_DOWN, onKeyboardSelect);
1456
- if (customEventProps) {
1457
- customEventProps.forEach((customEventProp) => {
1458
- switch (customEventProp.eventType) {
1459
- // Handle Pointer Events
1460
- case Events.EventType.POINTER_DOWN:
1461
- case Events.EventType.POINTER_UP:
1462
- case Events.EventType.POINTER_MOVE:
1463
- case Events.EventType.POINTER_WHEEL:
1464
- case Events.EventType.TOUCH_END:
1465
- case Events.EventType.TOUCH_MOVE:
1466
- case Events.EventType.TOUCH_START:
1467
- eventManager.on(
1468
- customEventProp.eventType,
1469
- customEventProp.callback
1470
- );
1471
- break;
1472
- // Handle Keyboard Events
1473
- case Events.EventType.KEY_DOWN:
1474
- case Events.EventType.KEY_UP:
1475
- eventManager.on(
1476
- customEventProp.eventType,
1477
- customEventProp.callback
1478
- );
1479
- break;
1480
- // Handle Pinch Events
1481
- case Events.EventType.PINCH:
1482
- case Events.EventType.PINCH_END:
1483
- case Events.EventType.PINCH_START:
1484
- eventManager.on(
1485
- customEventProp.eventType,
1486
- customEventProp.callback
1487
- );
1488
- break;
1489
- // Handle Selection Events
1490
- case Events.EventType.OBJECT_SELECTED:
1491
- case Events.EventType.OBJECT_DESELECTED:
1492
- eventManager.on(
1493
- customEventProp.eventType,
1494
- customEventProp.callback
1495
- );
1496
- break;
1497
- // Handle Events
1498
- default:
1499
- eventManager.on(
1500
- customEventProp.eventType,
1501
- customEventProp.callback
1502
- );
1503
- break;
1504
- }
1505
- });
1506
- }
1507
1519
  }
1520
+ scene.current.start();
1521
+ }
1522
+ }, []);
1523
+ (0, import_react.useEffect)(() => {
1524
+ if (!scene.current || !canvasRef.current) return;
1525
+ motifs.current.length = 0;
1526
+ motifProps.forEach((motifProp) => {
1527
+ motifs.current.push(motifProp.motif);
1528
+ });
1529
+ if (motifs.current.length > 0) {
1530
+ if (showRMSD) setKabschRMSD(calculateAllKabschRMSD(motifs.current));
1531
+ scene.current.removeAll();
1532
+ const positions = calculatePositions(motifs.current.length);
1533
+ motifs.current.forEach((motifMesh, index) => {
1534
+ scene.current?.add(motifMesh);
1535
+ if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
1536
+ motifMesh.setPosition(positions[index].x, positions[index].y, positions[index].z);
1537
+ if (motifProps[index].rotation) motifMesh.setQuaternion(motifProps[index].rotation);
1538
+ let scale = canvasRef.current.width / 250;
1539
+ if (motifProps[index].scale) scale = motifProps[index].scale;
1540
+ motifMesh.setScale(scale);
1541
+ if (motifProps[index].locked) lockedMotifIdState.current.add(motifMesh.uuid);
1542
+ });
1508
1543
  }
1509
- scene.current?.start();
1510
1544
  }, [rendererWidth, rendererHeight, rendererSizeIsWindow, motifProps]);
1511
1545
  return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("canvas", { ref: canvasRef }));
1512
1546
  }
package/dist/index.mjs CHANGED
@@ -255,7 +255,10 @@ var Residue = class extends Group {
255
255
  child.setParent(this);
256
256
  this._children.add(child);
257
257
  }
258
- // Temporary function to find if Residue contains mesh with uuid
258
+ /**
259
+ * Checks if the residue has a mesh with the given uuid
260
+ * @param uuid
261
+ */
259
262
  hasMesh(uuid) {
260
263
  let found = false;
261
264
  this._children.forEach((child) => {
@@ -265,6 +268,15 @@ var Residue = class extends Group {
265
268
  });
266
269
  return found;
267
270
  }
271
+ /**
272
+ * Cretes a material with the given color and sets it to the mesh
273
+ * @param color String in the format of #RRGGBB
274
+ */
275
+ colorResidue(color) {
276
+ this._children.forEach((child) => {
277
+ child.createAndSetMaterial(color);
278
+ });
279
+ }
268
280
  };
269
281
 
270
282
  // src/3D/MeshObject.ts
@@ -309,6 +321,7 @@ var MeshObject = class {
309
321
  const color3 = Color3.FromHexString(`#${color.replace(/^0x/, "")}`);
310
322
  mat.diffuseColor = color3;
311
323
  mat.specularColor = color3;
324
+ mat.backFaceCulling = false;
312
325
  this._mesh.material = mat;
313
326
  }
314
327
  setNewMesh(mesh) {
@@ -534,9 +547,19 @@ var EventManager = class {
534
547
  canceled: false,
535
548
  timestamp: performance.now()
536
549
  };
537
- if (keyboardInfo.type === KeyboardEventTypes.KEYDOWN) {
550
+ if (keyboardInfo.type === KeyboardEventTypes.KEYDOWN && /^[wasdqeWASD]$/.test(event.key)) {
551
+ if (event.key.charCodeAt(0) >= 97) {
552
+ this._activeKeys.delete(event.key.toUpperCase());
553
+ } else {
554
+ this._activeKeys.delete(event.key.toLowerCase());
555
+ }
538
556
  this._activeKeys.add(event.key);
539
557
  } else if (keyboardInfo.type === KeyboardEventTypes.KEYUP) {
558
+ if (event.key.charCodeAt(0) >= 97) {
559
+ this._activeKeys.delete(event.key.toUpperCase());
560
+ } else {
561
+ this._activeKeys.delete(event.key.toLowerCase());
562
+ }
540
563
  this._activeKeys.delete(event.key);
541
564
  }
542
565
  if (this._activeKeys.has("w")) keyboardEvent.rotationAxis.add(new Vec3(-1, 0, 0));
@@ -676,7 +699,15 @@ var RenderScene = class {
676
699
  return;
677
700
  }
678
701
  this._children.delete(motif.uuid);
679
- this._scene.removeTransformNode(motif.node);
702
+ motif.node.dispose(false, true);
703
+ }
704
+ removeAll() {
705
+ this._scene.meshes.slice().forEach((mesh) => {
706
+ if (mesh && this._scene.getMeshById(mesh.id)) {
707
+ this._scene.removeMesh(mesh);
708
+ }
709
+ });
710
+ this._children.clear();
680
711
  }
681
712
  setBackgroundColor(hexColor) {
682
713
  this._scene.clearColor = Color4.FromHexString(`${hexColor.replace(/^0x/, "")}`);
@@ -693,7 +724,7 @@ var RenderScene = class {
693
724
  geo?.applyToMesh(mesh);
694
725
  mesh.material = mat;
695
726
  currObj.setNewMesh(mesh);
696
- node.dispose();
727
+ node.dispose(false, true);
697
728
  }
698
729
  if (!(currObj instanceof MeshObject)) {
699
730
  currObj.children.forEach((childObj) => {
@@ -826,7 +857,7 @@ var CanvasAttributeTypes = /* @__PURE__ */ ((CanvasAttributeTypes2) => {
826
857
  })(CanvasAttributeTypes || {});
827
858
  var CanvasDataManager = class {
828
859
  static _selectedMotifIds = /* @__PURE__ */ new Set();
829
- static _lockedMotifIds = [];
860
+ static _lockedMotifIds = /* @__PURE__ */ new Set();
830
861
  static _hardLockedMotifIds = [];
831
862
  static _scoreRMSD = [];
832
863
  static _kabschRMSD = [];
@@ -1083,28 +1114,27 @@ function Canvas({
1083
1114
  }) {
1084
1115
  const canvasRef = useRef(null);
1085
1116
  const scene = useRef(null);
1086
- const motifs = [];
1117
+ const motifs = useRef([]);
1087
1118
  let hardLockedMotifIds = [];
1088
1119
  motifProps.forEach((motifProp) => {
1089
- motifs.push(motifProp.motif);
1090
1120
  if (motifProp.locked) hardLockedMotifIds.push(motifProp.motif.uuid);
1091
1121
  });
1092
1122
  CanvasDataManager.setHardLockedMotifIds(hardLockedMotifIds);
1093
1123
  const selectedMotifMeshState = useRef(/* @__PURE__ */ new Set());
1094
- const lockedMotifIdState = useRef([]);
1124
+ const lockedMotifIdState = useRef(/* @__PURE__ */ new Set());
1095
1125
  const [cursorStyle, setCursorStyle] = useState("auto");
1096
1126
  const [selectedMotifIds, setSelectedmotifIds] = useState(/* @__PURE__ */ new Set());
1097
1127
  const [scoreRMSD, setScoreRMSD] = useState([]);
1098
1128
  const [kabschRMSD, setKabschRMSD] = useState([]);
1099
- const [lockedMotifIds, setLockedMotifIds] = useState([]);
1129
+ const [lockedMotifIds, setLockedMotifIds] = useState(/* @__PURE__ */ new Set());
1100
1130
  const addMotif = (motif) => {
1101
1131
  if (selectedMotifMeshState.current.has(motif)) {
1102
1132
  return;
1103
1133
  }
1104
1134
  const newSet = /* @__PURE__ */ new Set();
1105
- for (let i = 0; i < motifs.length; i += 1) {
1106
- if (selectedMotifMeshState.current.has(motifs[i]) || motifs[i].uuid === motif.uuid) {
1107
- newSet.add(motifs[i].uuid);
1135
+ for (let i = 0; i < motifs.current.length; i += 1) {
1136
+ if (selectedMotifMeshState.current.has(motifs.current[i]) || motifs.current[i].uuid === motif.uuid) {
1137
+ newSet.add(motifs.current[i].uuid);
1108
1138
  }
1109
1139
  }
1110
1140
  setSelectedmotifIds(newSet);
@@ -1122,7 +1152,7 @@ function Canvas({
1122
1152
  });
1123
1153
  };
1124
1154
  function updateGlow() {
1125
- motifs.forEach((motif) => {
1155
+ motifs.current.forEach((motif) => {
1126
1156
  motif.children.forEach((residue) => {
1127
1157
  residue.children.forEach((childMesh) => {
1128
1158
  if (selectedMotifIds.has(motif.uuid)) {
@@ -1139,7 +1169,7 @@ function Canvas({
1139
1169
  return;
1140
1170
  }
1141
1171
  const { motif } = event;
1142
- if (!motif || selectedMotifMeshState.current.has(motif) || lockedMotifIdState.current.includes(motif.uuid) || hardLockedMotifIds.includes(motif.uuid)) {
1172
+ if (!motif || selectedMotifMeshState.current.has(motif)) {
1143
1173
  return;
1144
1174
  }
1145
1175
  if (event.multiSelect && motif) {
@@ -1171,7 +1201,7 @@ function Canvas({
1171
1201
  const deltaX = rawDeltaX / renderWidth * canvasRef.current.width;
1172
1202
  const deltaY = rawDeltaY / renderHeight * canvasRef.current.height;
1173
1203
  selectedMotifMeshState.current.forEach((element) => {
1174
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1204
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1175
1205
  element.translate(-deltaX, -deltaY, 0);
1176
1206
  }
1177
1207
  });
@@ -1184,11 +1214,11 @@ function Canvas({
1184
1214
  if (scene.current) {
1185
1215
  const angle = directionVec.length() / scene.current.renderWidth * (3 * Math.PI);
1186
1216
  selectedMotifMeshState.current.forEach((element) => {
1187
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1217
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1188
1218
  element.rotate(axisVec, angle);
1189
1219
  }
1190
1220
  });
1191
- if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs));
1221
+ if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs.current));
1192
1222
  }
1193
1223
  }
1194
1224
  }
@@ -1201,7 +1231,7 @@ function Canvas({
1201
1231
  const zoomSpeed = 0.1;
1202
1232
  const zoomDirection = event.originalEvent.deltaY > 0 ? -1 : 1;
1203
1233
  selectedMotifMeshState.current.forEach((element) => {
1204
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid) && !(element.scale <= 1 && zoomDirection === -1) && !(element.scale >= 30 && zoomDirection === 1)) {
1234
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid) && !(element.scale <= 1 && zoomDirection === -1) && !(element.scale >= 30 && zoomDirection === 1)) {
1205
1235
  const scaleFactor = 1 + zoomDirection * zoomSpeed;
1206
1236
  element.multiplyScalar(scaleFactor);
1207
1237
  }
@@ -1229,19 +1259,19 @@ function Canvas({
1229
1259
  }
1230
1260
  const angle = event.rotationAxis.length() / 500 * (6 * Math.PI);
1231
1261
  selectedMotifMeshState.current.forEach((element) => {
1232
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1262
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1233
1263
  element.rotate(event.rotationAxis, angle);
1234
1264
  }
1235
1265
  });
1236
- if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs));
1266
+ if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs.current));
1237
1267
  }
1238
1268
  function onKeyboardTranslate(event) {
1239
1269
  if (event.translationDirection.equals(Vec3.Zero)) {
1240
1270
  return;
1241
1271
  }
1242
- event.translationDirection.multiplyScalar(0.5);
1272
+ event.translationDirection.multiplyScalar(4.5);
1243
1273
  selectedMotifMeshState.current.forEach((element) => {
1244
- if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1274
+ if (!lockedMotifIdState.current.has(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
1245
1275
  element.translate(
1246
1276
  event.translationDirection.x,
1247
1277
  event.translationDirection.y,
@@ -1254,10 +1284,10 @@ function Canvas({
1254
1284
  if (!event.rotationAxis.equals(Vec3.Zero) || !event.translationDirection.equals(Vec3.Zero)) {
1255
1285
  return;
1256
1286
  }
1257
- if (!/^[1-9]$/.test(event.key) || Number(event.key) > motifs.length) {
1287
+ if (!/^[1-9]$/.test(event.key) || Number(event.key) > motifs.current.length) {
1258
1288
  return;
1259
1289
  }
1260
- const motif = motifs[Number(event.key) - 1];
1290
+ const motif = motifs.current[Number(event.key) - 1];
1261
1291
  if (selectedMotifMeshState.current.has(motif)) {
1262
1292
  removeMotif(motif);
1263
1293
  } else {
@@ -1280,8 +1310,8 @@ function Canvas({
1280
1310
  return positions;
1281
1311
  };
1282
1312
  const updateMotifs = () => {
1283
- const positions = calculatePositions(motifs.length);
1284
- motifs.forEach((motifMesh, index) => {
1313
+ const positions = calculatePositions(motifs.current.length);
1314
+ motifs.current.forEach((motifMesh, index) => {
1285
1315
  if (!scene.current?.children.has(motifMesh.uuid)) return;
1286
1316
  if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
1287
1317
  motifMesh.setPosition(positions[index].x, positions[index].y, positions[index].z);
@@ -1291,9 +1321,6 @@ function Canvas({
1291
1321
  motifMesh.setScale(scale);
1292
1322
  });
1293
1323
  };
1294
- useEffect(() => {
1295
- updateMotifs();
1296
- }, [motifProps]);
1297
1324
  useEffect(() => {
1298
1325
  const unsubscribe = CanvasDataManager.subscribe("selectedMotifs" /* SELECTED_MOTIFS */, () => {
1299
1326
  setSelectedmotifIds(CanvasDataManager.selectedMotifIds);
@@ -1305,13 +1332,13 @@ function Canvas({
1305
1332
  CanvasDataManager.setSelectedMotifIds(selectedMotifIds);
1306
1333
  }
1307
1334
  selectedMotifMeshState.current.clear();
1308
- motifs.forEach((motif) => {
1335
+ motifs.current.forEach((motif) => {
1309
1336
  if (selectedMotifIds.has(motif.uuid)) {
1310
1337
  selectedMotifMeshState.current.add(motif);
1311
1338
  }
1312
1339
  });
1313
1340
  updateGlow();
1314
- if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs));
1341
+ if (showRMSD) setScoreRMSD(calculateRMSD(Array.from(selectedMotifMeshState.current), motifs.current));
1315
1342
  }, [selectedMotifIds]);
1316
1343
  useEffect(() => {
1317
1344
  const unsubscribe = CanvasDataManager.subscribe("lockedMotifIds" /* LOCKED_MOTIF_IDS */, () => {
@@ -1352,8 +1379,8 @@ function Canvas({
1352
1379
  if (!showRMSD) return;
1353
1380
  if (CanvasDataManager.kabschRMSD !== kabschRMSD) {
1354
1381
  CanvasDataManager.setKabschRMSD(kabschRMSD);
1355
- } else if (CanvasDataManager.kabschRMSD.length !== motifs.length) {
1356
- CanvasDataManager.setKabschRMSD(calculateAllKabschRMSD(motifs));
1382
+ } else if (CanvasDataManager.kabschRMSD.length !== motifs.current.length) {
1383
+ CanvasDataManager.setKabschRMSD(calculateAllKabschRMSD(motifs.current));
1357
1384
  }
1358
1385
  }, [kabschRMSD]);
1359
1386
  useEffect(() => {
@@ -1372,84 +1399,91 @@ function Canvas({
1372
1399
  rendererSizeIsWindow ? window.innerWidth : rendererWidth,
1373
1400
  rendererSizeIsWindow ? window.innerHeight : rendererHeight
1374
1401
  );
1375
- }
1376
- if (motifs.length > 0) {
1377
- if (showRMSD) setKabschRMSD(calculateAllKabschRMSD(motifs));
1378
- if (scene.current.children.size !== motifs.length) {
1379
- const positions = calculatePositions(motifs.length);
1380
- motifs.forEach((motifMesh, index) => {
1381
- scene.current?.add(motifMesh);
1382
- if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
1383
- motifMesh.setPosition(positions[index].x, positions[index].y, positions[index].z);
1384
- if (motifProps[index].rotation) motifMesh.setQuaternion(motifProps[index].rotation);
1385
- let scale = canvasRef.current.width / 250;
1386
- if (motifProps[index].scale) scale = motifProps[index].scale;
1387
- motifMesh.multiplyScalar(scale);
1402
+ const eventManager = scene.current?.eventManager;
1403
+ eventManager.on(Events.EventType.OBJECT_SELECTED, onSelectMotif);
1404
+ eventManager.on(Events.EventType.OBJECT_DESELECTED, onDeselectMotif);
1405
+ eventManager.on(Events.EventType.POINTER_MOVE, onMouseMove);
1406
+ eventManager.on(Events.EventType.POINTER_WHEEL, onMouseScroll);
1407
+ eventManager.on(Events.EventType.POINTER_DOWN, onMouseDown);
1408
+ eventManager.on(Events.EventType.POINTER_UP, onMouseUp);
1409
+ eventManager.on(Events.EventType.KEY_DOWN, onKeyboardRotate);
1410
+ eventManager.on(Events.EventType.KEY_DOWN, onKeyboardTranslate);
1411
+ eventManager.on(Events.EventType.KEY_DOWN, onKeyboardSelect);
1412
+ if (customEventProps) {
1413
+ customEventProps.forEach((customEventProp) => {
1414
+ switch (customEventProp.eventType) {
1415
+ // Handle Pointer Events
1416
+ case Events.EventType.POINTER_DOWN:
1417
+ case Events.EventType.POINTER_UP:
1418
+ case Events.EventType.POINTER_MOVE:
1419
+ case Events.EventType.POINTER_WHEEL:
1420
+ case Events.EventType.TOUCH_END:
1421
+ case Events.EventType.TOUCH_MOVE:
1422
+ case Events.EventType.TOUCH_START:
1423
+ eventManager.on(
1424
+ customEventProp.eventType,
1425
+ customEventProp.callback
1426
+ );
1427
+ break;
1428
+ // Handle Keyboard Events
1429
+ case Events.EventType.KEY_DOWN:
1430
+ case Events.EventType.KEY_UP:
1431
+ eventManager.on(
1432
+ customEventProp.eventType,
1433
+ customEventProp.callback
1434
+ );
1435
+ break;
1436
+ // Handle Pinch Events
1437
+ case Events.EventType.PINCH:
1438
+ case Events.EventType.PINCH_END:
1439
+ case Events.EventType.PINCH_START:
1440
+ eventManager.on(
1441
+ customEventProp.eventType,
1442
+ customEventProp.callback
1443
+ );
1444
+ break;
1445
+ // Handle Selection Events
1446
+ case Events.EventType.OBJECT_SELECTED:
1447
+ case Events.EventType.OBJECT_DESELECTED:
1448
+ eventManager.on(
1449
+ customEventProp.eventType,
1450
+ customEventProp.callback
1451
+ );
1452
+ break;
1453
+ // Handle Events
1454
+ default:
1455
+ eventManager.on(
1456
+ customEventProp.eventType,
1457
+ customEventProp.callback
1458
+ );
1459
+ break;
1460
+ }
1388
1461
  });
1389
- const eventManager = scene.current?.eventManager;
1390
- eventManager.on(Events.EventType.OBJECT_SELECTED, onSelectMotif);
1391
- eventManager.on(Events.EventType.OBJECT_DESELECTED, onDeselectMotif);
1392
- eventManager.on(Events.EventType.POINTER_MOVE, onMouseMove);
1393
- eventManager.on(Events.EventType.POINTER_WHEEL, onMouseScroll);
1394
- eventManager.on(Events.EventType.POINTER_DOWN, onMouseDown);
1395
- eventManager.on(Events.EventType.POINTER_UP, onMouseUp);
1396
- eventManager.on(Events.EventType.KEY_DOWN, onKeyboardRotate);
1397
- eventManager.on(Events.EventType.KEY_DOWN, onKeyboardTranslate);
1398
- eventManager.on(Events.EventType.KEY_DOWN, onKeyboardSelect);
1399
- if (customEventProps) {
1400
- customEventProps.forEach((customEventProp) => {
1401
- switch (customEventProp.eventType) {
1402
- // Handle Pointer Events
1403
- case Events.EventType.POINTER_DOWN:
1404
- case Events.EventType.POINTER_UP:
1405
- case Events.EventType.POINTER_MOVE:
1406
- case Events.EventType.POINTER_WHEEL:
1407
- case Events.EventType.TOUCH_END:
1408
- case Events.EventType.TOUCH_MOVE:
1409
- case Events.EventType.TOUCH_START:
1410
- eventManager.on(
1411
- customEventProp.eventType,
1412
- customEventProp.callback
1413
- );
1414
- break;
1415
- // Handle Keyboard Events
1416
- case Events.EventType.KEY_DOWN:
1417
- case Events.EventType.KEY_UP:
1418
- eventManager.on(
1419
- customEventProp.eventType,
1420
- customEventProp.callback
1421
- );
1422
- break;
1423
- // Handle Pinch Events
1424
- case Events.EventType.PINCH:
1425
- case Events.EventType.PINCH_END:
1426
- case Events.EventType.PINCH_START:
1427
- eventManager.on(
1428
- customEventProp.eventType,
1429
- customEventProp.callback
1430
- );
1431
- break;
1432
- // Handle Selection Events
1433
- case Events.EventType.OBJECT_SELECTED:
1434
- case Events.EventType.OBJECT_DESELECTED:
1435
- eventManager.on(
1436
- customEventProp.eventType,
1437
- customEventProp.callback
1438
- );
1439
- break;
1440
- // Handle Events
1441
- default:
1442
- eventManager.on(
1443
- customEventProp.eventType,
1444
- customEventProp.callback
1445
- );
1446
- break;
1447
- }
1448
- });
1449
- }
1450
1462
  }
1463
+ scene.current.start();
1464
+ }
1465
+ }, []);
1466
+ useEffect(() => {
1467
+ if (!scene.current || !canvasRef.current) return;
1468
+ motifs.current.length = 0;
1469
+ motifProps.forEach((motifProp) => {
1470
+ motifs.current.push(motifProp.motif);
1471
+ });
1472
+ if (motifs.current.length > 0) {
1473
+ if (showRMSD) setKabschRMSD(calculateAllKabschRMSD(motifs.current));
1474
+ scene.current.removeAll();
1475
+ const positions = calculatePositions(motifs.current.length);
1476
+ motifs.current.forEach((motifMesh, index) => {
1477
+ scene.current?.add(motifMesh);
1478
+ if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
1479
+ motifMesh.setPosition(positions[index].x, positions[index].y, positions[index].z);
1480
+ if (motifProps[index].rotation) motifMesh.setQuaternion(motifProps[index].rotation);
1481
+ let scale = canvasRef.current.width / 250;
1482
+ if (motifProps[index].scale) scale = motifProps[index].scale;
1483
+ motifMesh.setScale(scale);
1484
+ if (motifProps[index].locked) lockedMotifIdState.current.add(motifMesh.uuid);
1485
+ });
1451
1486
  }
1452
- scene.current?.start();
1453
1487
  }, [rendererWidth, rendererHeight, rendererSizeIsWindow, motifProps]);
1454
1488
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("canvas", { ref: canvasRef }));
1455
1489
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@judah-silva/rnacanvas",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "A 3D Canvas for displaying and interacting with custom RNA models. Powered by BabylonJS.",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
Binary file