@judah-silva/rnacanvas 0.0.4 → 0.0.6
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 +0 -5
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +47 -43
- package/dist/index.mjs +45 -41
- package/package.json +6 -2
- package/judah-silva-rnacanvas-0.0.3.tgz +0 -0
package/README.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -112,6 +112,7 @@ declare class Motif extends Group<Residue> {
|
|
|
112
112
|
rotateByQuaternion(quat: Quat): void;
|
|
113
113
|
setQuaternion(quat: Quat): void;
|
|
114
114
|
multiplyScalar(scalar: number): void;
|
|
115
|
+
setScale(scale: number): void;
|
|
115
116
|
get uuid(): string;
|
|
116
117
|
get quat(): Quat;
|
|
117
118
|
get scale(): number;
|
|
@@ -334,7 +335,7 @@ declare class RenderScene {
|
|
|
334
335
|
* @author Judah Silva <jusilva@csumb.edu>
|
|
335
336
|
*/
|
|
336
337
|
|
|
337
|
-
declare function
|
|
338
|
+
declare function parseAtomCoords(meshObject: MotifMesh): Promise<Vec3[]>;
|
|
338
339
|
|
|
339
340
|
/**
|
|
340
341
|
* Copyright (c) 2025 RNA3DS Lab CSUMB.
|
|
@@ -342,6 +343,7 @@ declare function updateAllMotifs(motifMeshArray: Motif[]): Promise<void>;
|
|
|
342
343
|
* @author Judah Silva
|
|
343
344
|
*/
|
|
344
345
|
|
|
346
|
+
type MotifMesh = Record<string, any>;
|
|
345
347
|
/**
|
|
346
348
|
* ________________________________________________________________________________________________
|
|
347
349
|
*/
|
|
@@ -356,7 +358,7 @@ declare function updateAllMotifs(motifMeshArray: Motif[]): Promise<void>;
|
|
|
356
358
|
* @returns {Promise<Motif>}
|
|
357
359
|
* @async
|
|
358
360
|
*/
|
|
359
|
-
declare function getMotif(
|
|
361
|
+
declare function getMotif(motifName: string, motifMesh: MotifMesh, motifColorHex?: string): Promise<Motif>;
|
|
360
362
|
|
|
361
363
|
/**
|
|
362
364
|
* Copyright (c) 2025 RNA3DS Lab CSUMB.
|
|
@@ -521,4 +523,4 @@ declare function calculateRMSDSlide(coordinates1: Vec3[], coordinates2: Vec3[]):
|
|
|
521
523
|
|
|
522
524
|
declare function rotateAllPoints(atomCoords: Vec3[], quat: Quat): Vec3[];
|
|
523
525
|
|
|
524
|
-
export { Canvas, CanvasAttributeTypes, CanvasDataManager, type CanvasProps, type CustomEventProps, EventManager, Events, Group, Matrix4, MeshObject, Motif, type MotifProps, Quat, RenderScene, Residue, type ScoreInfo, Vec3, calculateAllKabschRMSD, calculateRMSD, calculateRMSDSlide, getMotif, getPoints, getRMSD, kabschSlidingWindow,
|
|
526
|
+
export { Canvas, CanvasAttributeTypes, CanvasDataManager, type CanvasProps, type CustomEventProps, EventManager, Events, Group, Matrix4, MeshObject, Motif, type MotifMesh, type MotifProps, Quat, RenderScene, Residue, type ScoreInfo, Vec3, calculateAllKabschRMSD, calculateRMSD, calculateRMSDSlide, getMotif, getPoints, getRMSD, kabschSlidingWindow, parseAtomCoords, rotateAllPoints };
|
package/dist/index.d.ts
CHANGED
|
@@ -112,6 +112,7 @@ declare class Motif extends Group<Residue> {
|
|
|
112
112
|
rotateByQuaternion(quat: Quat): void;
|
|
113
113
|
setQuaternion(quat: Quat): void;
|
|
114
114
|
multiplyScalar(scalar: number): void;
|
|
115
|
+
setScale(scale: number): void;
|
|
115
116
|
get uuid(): string;
|
|
116
117
|
get quat(): Quat;
|
|
117
118
|
get scale(): number;
|
|
@@ -334,7 +335,7 @@ declare class RenderScene {
|
|
|
334
335
|
* @author Judah Silva <jusilva@csumb.edu>
|
|
335
336
|
*/
|
|
336
337
|
|
|
337
|
-
declare function
|
|
338
|
+
declare function parseAtomCoords(meshObject: MotifMesh): Promise<Vec3[]>;
|
|
338
339
|
|
|
339
340
|
/**
|
|
340
341
|
* Copyright (c) 2025 RNA3DS Lab CSUMB.
|
|
@@ -342,6 +343,7 @@ declare function updateAllMotifs(motifMeshArray: Motif[]): Promise<void>;
|
|
|
342
343
|
* @author Judah Silva
|
|
343
344
|
*/
|
|
344
345
|
|
|
346
|
+
type MotifMesh = Record<string, any>;
|
|
345
347
|
/**
|
|
346
348
|
* ________________________________________________________________________________________________
|
|
347
349
|
*/
|
|
@@ -356,7 +358,7 @@ declare function updateAllMotifs(motifMeshArray: Motif[]): Promise<void>;
|
|
|
356
358
|
* @returns {Promise<Motif>}
|
|
357
359
|
* @async
|
|
358
360
|
*/
|
|
359
|
-
declare function getMotif(
|
|
361
|
+
declare function getMotif(motifName: string, motifMesh: MotifMesh, motifColorHex?: string): Promise<Motif>;
|
|
360
362
|
|
|
361
363
|
/**
|
|
362
364
|
* Copyright (c) 2025 RNA3DS Lab CSUMB.
|
|
@@ -521,4 +523,4 @@ declare function calculateRMSDSlide(coordinates1: Vec3[], coordinates2: Vec3[]):
|
|
|
521
523
|
|
|
522
524
|
declare function rotateAllPoints(atomCoords: Vec3[], quat: Quat): Vec3[];
|
|
523
525
|
|
|
524
|
-
export { Canvas, CanvasAttributeTypes, CanvasDataManager, type CanvasProps, type CustomEventProps, EventManager, Events, Group, Matrix4, MeshObject, Motif, type MotifProps, Quat, RenderScene, Residue, type ScoreInfo, Vec3, calculateAllKabschRMSD, calculateRMSD, calculateRMSDSlide, getMotif, getPoints, getRMSD, kabschSlidingWindow,
|
|
526
|
+
export { Canvas, CanvasAttributeTypes, CanvasDataManager, type CanvasProps, type CustomEventProps, EventManager, Events, Group, Matrix4, MeshObject, Motif, type MotifMesh, type MotifProps, Quat, RenderScene, Residue, type ScoreInfo, Vec3, calculateAllKabschRMSD, calculateRMSD, calculateRMSDSlide, getMotif, getPoints, getRMSD, kabschSlidingWindow, parseAtomCoords, rotateAllPoints };
|
package/dist/index.js
CHANGED
|
@@ -50,8 +50,8 @@ __export(index_exports, {
|
|
|
50
50
|
getPoints: () => getPoints,
|
|
51
51
|
getRMSD: () => getRMSD,
|
|
52
52
|
kabschSlidingWindow: () => kabschSlidingWindow,
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
parseAtomCoords: () => parseAtomCoords,
|
|
54
|
+
rotateAllPoints: () => rotateAllPoints
|
|
55
55
|
});
|
|
56
56
|
module.exports = __toCommonJS(index_exports);
|
|
57
57
|
|
|
@@ -269,6 +269,13 @@ var Motif = class extends Group {
|
|
|
269
269
|
this._node.scaling.z * scalar
|
|
270
270
|
);
|
|
271
271
|
}
|
|
272
|
+
setScale(scale) {
|
|
273
|
+
this._node.scaling = new import_core5.Vector3(
|
|
274
|
+
scale,
|
|
275
|
+
scale,
|
|
276
|
+
scale
|
|
277
|
+
);
|
|
278
|
+
}
|
|
272
279
|
get uuid() {
|
|
273
280
|
return this._node.uniqueId.toString();
|
|
274
281
|
}
|
|
@@ -813,12 +820,10 @@ var RenderScene = class {
|
|
|
813
820
|
};
|
|
814
821
|
|
|
815
822
|
// src/3D/utils/GetAtomInfo.ts
|
|
816
|
-
async function parseAtomCoords(
|
|
817
|
-
const response = await fetch(`/${fileName}`);
|
|
818
|
-
const jsonData = await response.json();
|
|
823
|
+
async function parseAtomCoords(meshObject) {
|
|
819
824
|
const coordinates = [];
|
|
820
|
-
for (const [key] of Object.entries(
|
|
821
|
-
const atomMap =
|
|
825
|
+
for (const [key] of Object.entries(meshObject)) {
|
|
826
|
+
const atomMap = meshObject[key][0];
|
|
822
827
|
if (atomMap[`"C1'"`]) {
|
|
823
828
|
coordinates.push(
|
|
824
829
|
new Vec3(
|
|
@@ -831,20 +836,6 @@ async function parseAtomCoords(fileName) {
|
|
|
831
836
|
}
|
|
832
837
|
return coordinates;
|
|
833
838
|
}
|
|
834
|
-
async function getAtomCoords(fileName) {
|
|
835
|
-
const coordinates = await parseAtomCoords(fileName);
|
|
836
|
-
return coordinates;
|
|
837
|
-
}
|
|
838
|
-
async function updateAllMotifs(motifMeshArray) {
|
|
839
|
-
const fileNames = [];
|
|
840
|
-
for (const motif of motifMeshArray) {
|
|
841
|
-
fileNames.push(motif.userData.fileName);
|
|
842
|
-
}
|
|
843
|
-
const atomInfoLists = await Promise.all(fileNames.map((o) => getAtomCoords(o)));
|
|
844
|
-
for (let i = 0; i < motifMeshArray.length; i += 1) {
|
|
845
|
-
motifMeshArray[i].userData.atomInfo = atomInfoLists[i];
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
839
|
|
|
849
840
|
// src/3D/utils/GetPoints.ts
|
|
850
841
|
function getPoints(nucleotideData) {
|
|
@@ -858,12 +849,10 @@ function getPoints(nucleotideData) {
|
|
|
858
849
|
}
|
|
859
850
|
|
|
860
851
|
// src/3D/utils/GetMotif.ts
|
|
861
|
-
async function getMotif(
|
|
862
|
-
const motif = new Motif(`${
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
for (const [key] of Object.entries(jsonObject)) {
|
|
866
|
-
const { vertices, indices } = getPoints(jsonObject[key]);
|
|
852
|
+
async function getMotif(motifName, motifMesh, motifColorHex = "0xcc2900") {
|
|
853
|
+
const motif = new Motif(`${motifName}_motif`);
|
|
854
|
+
for (const [key] of Object.entries(motifMesh)) {
|
|
855
|
+
const { vertices, indices } = getPoints(motifMesh[key]);
|
|
867
856
|
const residue = new Residue("residue");
|
|
868
857
|
const backboneMesh = new MeshObject(`backbone_${key}`);
|
|
869
858
|
backboneMesh.applyVertexData(vertices[0], indices[0]);
|
|
@@ -875,7 +864,8 @@ async function getMotif(motifJSONFileName, motifColorHex = "0xcc2900") {
|
|
|
875
864
|
residue.addChild(ringMesh);
|
|
876
865
|
motif.addChild(residue);
|
|
877
866
|
}
|
|
878
|
-
motif.userData.fileName =
|
|
867
|
+
motif.userData.fileName = motifName;
|
|
868
|
+
motif.userData.atomInfo = await parseAtomCoords(motifMesh);
|
|
879
869
|
return motif;
|
|
880
870
|
}
|
|
881
871
|
|
|
@@ -970,6 +960,7 @@ function getRMSD(a, b) {
|
|
|
970
960
|
if (a instanceof Motif && b instanceof Motif) {
|
|
971
961
|
newCoords1 = rotateAllPoints(a.userData.atomInfo, a.quat);
|
|
972
962
|
newCoords2 = rotateAllPoints(b.userData.atomInfo, b.quat);
|
|
963
|
+
return calculateRMSDSlide(newCoords1, newCoords2);
|
|
973
964
|
} else {
|
|
974
965
|
return -1;
|
|
975
966
|
}
|
|
@@ -1163,7 +1154,7 @@ function Canvas({
|
|
|
1163
1154
|
const [kabschRMSD, setKabschRMSD] = (0, import_react.useState)([]);
|
|
1164
1155
|
const [lockedMotifIds, setLockedMotifIds] = (0, import_react.useState)([]);
|
|
1165
1156
|
const addMotif = (motif) => {
|
|
1166
|
-
if (
|
|
1157
|
+
if (selectedMotifMeshState.current.has(motif)) {
|
|
1167
1158
|
return;
|
|
1168
1159
|
}
|
|
1169
1160
|
const newSet = /* @__PURE__ */ new Set();
|
|
@@ -1176,12 +1167,12 @@ function Canvas({
|
|
|
1176
1167
|
selectedMotifMeshState.current.add(motif);
|
|
1177
1168
|
};
|
|
1178
1169
|
const removeMotif = (motif) => {
|
|
1179
|
-
if (!
|
|
1170
|
+
if (!selectedMotifMeshState.current.has(motif)) {
|
|
1180
1171
|
return;
|
|
1181
1172
|
}
|
|
1182
1173
|
selectedMotifMeshState.current.delete(motif);
|
|
1183
1174
|
setSelectedmotifIds((prevState) => {
|
|
1184
|
-
const newState = prevState;
|
|
1175
|
+
const newState = new Set(prevState);
|
|
1185
1176
|
newState.delete(motif.uuid);
|
|
1186
1177
|
return newState;
|
|
1187
1178
|
});
|
|
@@ -1266,10 +1257,12 @@ function Canvas({
|
|
|
1266
1257
|
const zoomSpeed = 0.1;
|
|
1267
1258
|
const zoomDirection = event.originalEvent.deltaY > 0 ? -1 : 1;
|
|
1268
1259
|
selectedMotifMeshState.current.forEach((element) => {
|
|
1269
|
-
if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
|
|
1260
|
+
if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid) && !(element.scale <= 1 && zoomDirection === -1) && !(element.scale >= 30 && zoomDirection === 1)) {
|
|
1270
1261
|
const scaleFactor = 1 + zoomDirection * zoomSpeed;
|
|
1271
1262
|
element.multiplyScalar(scaleFactor);
|
|
1272
1263
|
}
|
|
1264
|
+
if (element.scale < 1) element.setScale(1);
|
|
1265
|
+
if (element.scale > 30) element.setScale(30);
|
|
1273
1266
|
});
|
|
1274
1267
|
}
|
|
1275
1268
|
}
|
|
@@ -1317,7 +1310,7 @@ function Canvas({
|
|
|
1317
1310
|
if (!event.rotationAxis.equals(Vec3.Zero) || !event.translationDirection.equals(Vec3.Zero)) {
|
|
1318
1311
|
return;
|
|
1319
1312
|
}
|
|
1320
|
-
if (!/^[
|
|
1313
|
+
if (!/^[1-9]$/.test(event.key) || Number(event.key) > motifs.length) {
|
|
1321
1314
|
return;
|
|
1322
1315
|
}
|
|
1323
1316
|
const motif = motifs[Number(event.key) - 1];
|
|
@@ -1342,6 +1335,21 @@ function Canvas({
|
|
|
1342
1335
|
}
|
|
1343
1336
|
return positions;
|
|
1344
1337
|
};
|
|
1338
|
+
const updateMotifs = () => {
|
|
1339
|
+
const positions = calculatePositions(motifs.length);
|
|
1340
|
+
motifs.forEach((motifMesh, index) => {
|
|
1341
|
+
if (!scene.current?.children.has(motifMesh.uuid)) return;
|
|
1342
|
+
if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
|
|
1343
|
+
motifMesh.setPosition(positions[index].x, positions[index].y, positions[index].z);
|
|
1344
|
+
if (motifProps[index].rotation) motifMesh.setQuaternion(motifProps[index].rotation);
|
|
1345
|
+
let scale = canvasRef.current.width / 250;
|
|
1346
|
+
if (motifProps[index].scale) scale = motifProps[index].scale;
|
|
1347
|
+
motifMesh.setScale(scale);
|
|
1348
|
+
});
|
|
1349
|
+
};
|
|
1350
|
+
(0, import_react.useEffect)(() => {
|
|
1351
|
+
updateMotifs();
|
|
1352
|
+
}, [motifProps]);
|
|
1345
1353
|
(0, import_react.useEffect)(() => {
|
|
1346
1354
|
const unsubscribe = CanvasDataManager.subscribe("selectedMotifs" /* SELECTED_MOTIFS */, () => {
|
|
1347
1355
|
setSelectedmotifIds(CanvasDataManager.selectedMotifIds);
|
|
@@ -1396,6 +1404,8 @@ function Canvas({
|
|
|
1396
1404
|
(0, import_react.useEffect)(() => {
|
|
1397
1405
|
if (CanvasDataManager.kabschRMSD !== kabschRMSD) {
|
|
1398
1406
|
CanvasDataManager.setKabschRMSD(kabschRMSD);
|
|
1407
|
+
} else if (CanvasDataManager.kabschRMSD.length !== motifs.length) {
|
|
1408
|
+
CanvasDataManager.setKabschRMSD(calculateAllKabschRMSD(motifs));
|
|
1399
1409
|
}
|
|
1400
1410
|
}, [kabschRMSD]);
|
|
1401
1411
|
(0, import_react.useEffect)(() => {
|
|
@@ -1415,12 +1425,10 @@ function Canvas({
|
|
|
1415
1425
|
rendererSizeIsWindow ? window.innerHeight : rendererHeight
|
|
1416
1426
|
);
|
|
1417
1427
|
}
|
|
1418
|
-
const positions = calculatePositions(motifs.length);
|
|
1419
1428
|
if (motifs.length > 0) {
|
|
1420
|
-
|
|
1421
|
-
setKabschRMSD(calculateAllKabschRMSD(motifs));
|
|
1422
|
-
});
|
|
1429
|
+
setKabschRMSD(calculateAllKabschRMSD(motifs));
|
|
1423
1430
|
if (scene.current.children.size !== motifs.length) {
|
|
1431
|
+
const positions = calculatePositions(motifs.length);
|
|
1424
1432
|
motifs.forEach((motifMesh, index) => {
|
|
1425
1433
|
scene.current?.add(motifMesh);
|
|
1426
1434
|
if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
|
|
@@ -1491,10 +1499,6 @@ function Canvas({
|
|
|
1491
1499
|
}
|
|
1492
1500
|
});
|
|
1493
1501
|
}
|
|
1494
|
-
} else {
|
|
1495
|
-
motifs.forEach((motifMesh) => {
|
|
1496
|
-
scene.current?.add(motifMesh);
|
|
1497
|
-
});
|
|
1498
1502
|
}
|
|
1499
1503
|
}
|
|
1500
1504
|
scene.current?.start();
|
|
@@ -1523,6 +1527,6 @@ function Canvas({
|
|
|
1523
1527
|
getPoints,
|
|
1524
1528
|
getRMSD,
|
|
1525
1529
|
kabschSlidingWindow,
|
|
1526
|
-
|
|
1527
|
-
|
|
1530
|
+
parseAtomCoords,
|
|
1531
|
+
rotateAllPoints
|
|
1528
1532
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -212,6 +212,13 @@ var Motif = class extends Group {
|
|
|
212
212
|
this._node.scaling.z * scalar
|
|
213
213
|
);
|
|
214
214
|
}
|
|
215
|
+
setScale(scale) {
|
|
216
|
+
this._node.scaling = new Vector32(
|
|
217
|
+
scale,
|
|
218
|
+
scale,
|
|
219
|
+
scale
|
|
220
|
+
);
|
|
221
|
+
}
|
|
215
222
|
get uuid() {
|
|
216
223
|
return this._node.uniqueId.toString();
|
|
217
224
|
}
|
|
@@ -756,12 +763,10 @@ var RenderScene = class {
|
|
|
756
763
|
};
|
|
757
764
|
|
|
758
765
|
// src/3D/utils/GetAtomInfo.ts
|
|
759
|
-
async function parseAtomCoords(
|
|
760
|
-
const response = await fetch(`/${fileName}`);
|
|
761
|
-
const jsonData = await response.json();
|
|
766
|
+
async function parseAtomCoords(meshObject) {
|
|
762
767
|
const coordinates = [];
|
|
763
|
-
for (const [key] of Object.entries(
|
|
764
|
-
const atomMap =
|
|
768
|
+
for (const [key] of Object.entries(meshObject)) {
|
|
769
|
+
const atomMap = meshObject[key][0];
|
|
765
770
|
if (atomMap[`"C1'"`]) {
|
|
766
771
|
coordinates.push(
|
|
767
772
|
new Vec3(
|
|
@@ -774,20 +779,6 @@ async function parseAtomCoords(fileName) {
|
|
|
774
779
|
}
|
|
775
780
|
return coordinates;
|
|
776
781
|
}
|
|
777
|
-
async function getAtomCoords(fileName) {
|
|
778
|
-
const coordinates = await parseAtomCoords(fileName);
|
|
779
|
-
return coordinates;
|
|
780
|
-
}
|
|
781
|
-
async function updateAllMotifs(motifMeshArray) {
|
|
782
|
-
const fileNames = [];
|
|
783
|
-
for (const motif of motifMeshArray) {
|
|
784
|
-
fileNames.push(motif.userData.fileName);
|
|
785
|
-
}
|
|
786
|
-
const atomInfoLists = await Promise.all(fileNames.map((o) => getAtomCoords(o)));
|
|
787
|
-
for (let i = 0; i < motifMeshArray.length; i += 1) {
|
|
788
|
-
motifMeshArray[i].userData.atomInfo = atomInfoLists[i];
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
782
|
|
|
792
783
|
// src/3D/utils/GetPoints.ts
|
|
793
784
|
function getPoints(nucleotideData) {
|
|
@@ -801,12 +792,10 @@ function getPoints(nucleotideData) {
|
|
|
801
792
|
}
|
|
802
793
|
|
|
803
794
|
// src/3D/utils/GetMotif.ts
|
|
804
|
-
async function getMotif(
|
|
805
|
-
const motif = new Motif(`${
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
for (const [key] of Object.entries(jsonObject)) {
|
|
809
|
-
const { vertices, indices } = getPoints(jsonObject[key]);
|
|
795
|
+
async function getMotif(motifName, motifMesh, motifColorHex = "0xcc2900") {
|
|
796
|
+
const motif = new Motif(`${motifName}_motif`);
|
|
797
|
+
for (const [key] of Object.entries(motifMesh)) {
|
|
798
|
+
const { vertices, indices } = getPoints(motifMesh[key]);
|
|
810
799
|
const residue = new Residue("residue");
|
|
811
800
|
const backboneMesh = new MeshObject(`backbone_${key}`);
|
|
812
801
|
backboneMesh.applyVertexData(vertices[0], indices[0]);
|
|
@@ -818,7 +807,8 @@ async function getMotif(motifJSONFileName, motifColorHex = "0xcc2900") {
|
|
|
818
807
|
residue.addChild(ringMesh);
|
|
819
808
|
motif.addChild(residue);
|
|
820
809
|
}
|
|
821
|
-
motif.userData.fileName =
|
|
810
|
+
motif.userData.fileName = motifName;
|
|
811
|
+
motif.userData.atomInfo = await parseAtomCoords(motifMesh);
|
|
822
812
|
return motif;
|
|
823
813
|
}
|
|
824
814
|
|
|
@@ -913,6 +903,7 @@ function getRMSD(a, b) {
|
|
|
913
903
|
if (a instanceof Motif && b instanceof Motif) {
|
|
914
904
|
newCoords1 = rotateAllPoints(a.userData.atomInfo, a.quat);
|
|
915
905
|
newCoords2 = rotateAllPoints(b.userData.atomInfo, b.quat);
|
|
906
|
+
return calculateRMSDSlide(newCoords1, newCoords2);
|
|
916
907
|
} else {
|
|
917
908
|
return -1;
|
|
918
909
|
}
|
|
@@ -1106,7 +1097,7 @@ function Canvas({
|
|
|
1106
1097
|
const [kabschRMSD, setKabschRMSD] = useState([]);
|
|
1107
1098
|
const [lockedMotifIds, setLockedMotifIds] = useState([]);
|
|
1108
1099
|
const addMotif = (motif) => {
|
|
1109
|
-
if (
|
|
1100
|
+
if (selectedMotifMeshState.current.has(motif)) {
|
|
1110
1101
|
return;
|
|
1111
1102
|
}
|
|
1112
1103
|
const newSet = /* @__PURE__ */ new Set();
|
|
@@ -1119,12 +1110,12 @@ function Canvas({
|
|
|
1119
1110
|
selectedMotifMeshState.current.add(motif);
|
|
1120
1111
|
};
|
|
1121
1112
|
const removeMotif = (motif) => {
|
|
1122
|
-
if (!
|
|
1113
|
+
if (!selectedMotifMeshState.current.has(motif)) {
|
|
1123
1114
|
return;
|
|
1124
1115
|
}
|
|
1125
1116
|
selectedMotifMeshState.current.delete(motif);
|
|
1126
1117
|
setSelectedmotifIds((prevState) => {
|
|
1127
|
-
const newState = prevState;
|
|
1118
|
+
const newState = new Set(prevState);
|
|
1128
1119
|
newState.delete(motif.uuid);
|
|
1129
1120
|
return newState;
|
|
1130
1121
|
});
|
|
@@ -1209,10 +1200,12 @@ function Canvas({
|
|
|
1209
1200
|
const zoomSpeed = 0.1;
|
|
1210
1201
|
const zoomDirection = event.originalEvent.deltaY > 0 ? -1 : 1;
|
|
1211
1202
|
selectedMotifMeshState.current.forEach((element) => {
|
|
1212
|
-
if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid)) {
|
|
1203
|
+
if (!lockedMotifIdState.current.includes(element.uuid) && !hardLockedMotifIds.includes(element.uuid) && !(element.scale <= 1 && zoomDirection === -1) && !(element.scale >= 30 && zoomDirection === 1)) {
|
|
1213
1204
|
const scaleFactor = 1 + zoomDirection * zoomSpeed;
|
|
1214
1205
|
element.multiplyScalar(scaleFactor);
|
|
1215
1206
|
}
|
|
1207
|
+
if (element.scale < 1) element.setScale(1);
|
|
1208
|
+
if (element.scale > 30) element.setScale(30);
|
|
1216
1209
|
});
|
|
1217
1210
|
}
|
|
1218
1211
|
}
|
|
@@ -1260,7 +1253,7 @@ function Canvas({
|
|
|
1260
1253
|
if (!event.rotationAxis.equals(Vec3.Zero) || !event.translationDirection.equals(Vec3.Zero)) {
|
|
1261
1254
|
return;
|
|
1262
1255
|
}
|
|
1263
|
-
if (!/^[
|
|
1256
|
+
if (!/^[1-9]$/.test(event.key) || Number(event.key) > motifs.length) {
|
|
1264
1257
|
return;
|
|
1265
1258
|
}
|
|
1266
1259
|
const motif = motifs[Number(event.key) - 1];
|
|
@@ -1285,6 +1278,21 @@ function Canvas({
|
|
|
1285
1278
|
}
|
|
1286
1279
|
return positions;
|
|
1287
1280
|
};
|
|
1281
|
+
const updateMotifs = () => {
|
|
1282
|
+
const positions = calculatePositions(motifs.length);
|
|
1283
|
+
motifs.forEach((motifMesh, index) => {
|
|
1284
|
+
if (!scene.current?.children.has(motifMesh.uuid)) return;
|
|
1285
|
+
if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
|
|
1286
|
+
motifMesh.setPosition(positions[index].x, positions[index].y, positions[index].z);
|
|
1287
|
+
if (motifProps[index].rotation) motifMesh.setQuaternion(motifProps[index].rotation);
|
|
1288
|
+
let scale = canvasRef.current.width / 250;
|
|
1289
|
+
if (motifProps[index].scale) scale = motifProps[index].scale;
|
|
1290
|
+
motifMesh.setScale(scale);
|
|
1291
|
+
});
|
|
1292
|
+
};
|
|
1293
|
+
useEffect(() => {
|
|
1294
|
+
updateMotifs();
|
|
1295
|
+
}, [motifProps]);
|
|
1288
1296
|
useEffect(() => {
|
|
1289
1297
|
const unsubscribe = CanvasDataManager.subscribe("selectedMotifs" /* SELECTED_MOTIFS */, () => {
|
|
1290
1298
|
setSelectedmotifIds(CanvasDataManager.selectedMotifIds);
|
|
@@ -1339,6 +1347,8 @@ function Canvas({
|
|
|
1339
1347
|
useEffect(() => {
|
|
1340
1348
|
if (CanvasDataManager.kabschRMSD !== kabschRMSD) {
|
|
1341
1349
|
CanvasDataManager.setKabschRMSD(kabschRMSD);
|
|
1350
|
+
} else if (CanvasDataManager.kabschRMSD.length !== motifs.length) {
|
|
1351
|
+
CanvasDataManager.setKabschRMSD(calculateAllKabschRMSD(motifs));
|
|
1342
1352
|
}
|
|
1343
1353
|
}, [kabschRMSD]);
|
|
1344
1354
|
useEffect(() => {
|
|
@@ -1358,12 +1368,10 @@ function Canvas({
|
|
|
1358
1368
|
rendererSizeIsWindow ? window.innerHeight : rendererHeight
|
|
1359
1369
|
);
|
|
1360
1370
|
}
|
|
1361
|
-
const positions = calculatePositions(motifs.length);
|
|
1362
1371
|
if (motifs.length > 0) {
|
|
1363
|
-
|
|
1364
|
-
setKabschRMSD(calculateAllKabschRMSD(motifs));
|
|
1365
|
-
});
|
|
1372
|
+
setKabschRMSD(calculateAllKabschRMSD(motifs));
|
|
1366
1373
|
if (scene.current.children.size !== motifs.length) {
|
|
1374
|
+
const positions = calculatePositions(motifs.length);
|
|
1367
1375
|
motifs.forEach((motifMesh, index) => {
|
|
1368
1376
|
scene.current?.add(motifMesh);
|
|
1369
1377
|
if (motifProps[index].position) positions[index] = motifProps[index].position.clone();
|
|
@@ -1434,10 +1442,6 @@ function Canvas({
|
|
|
1434
1442
|
}
|
|
1435
1443
|
});
|
|
1436
1444
|
}
|
|
1437
|
-
} else {
|
|
1438
|
-
motifs.forEach((motifMesh) => {
|
|
1439
|
-
scene.current?.add(motifMesh);
|
|
1440
|
-
});
|
|
1441
1445
|
}
|
|
1442
1446
|
}
|
|
1443
1447
|
scene.current?.start();
|
|
@@ -1465,6 +1469,6 @@ export {
|
|
|
1465
1469
|
getPoints,
|
|
1466
1470
|
getRMSD,
|
|
1467
1471
|
kabschSlidingWindow,
|
|
1468
|
-
|
|
1469
|
-
|
|
1472
|
+
parseAtomCoords,
|
|
1473
|
+
rotateAllPoints
|
|
1470
1474
|
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@judah-silva/rnacanvas",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "A 3D Canvas for displaying and interacting with custom RNA models. Powered by BabylonJS.",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"main": "./dist/index.js",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/RNAStructureAnalysisLab/RNACanvas"
|
|
10
|
+
},
|
|
6
11
|
"module": "./dist/index.mjs",
|
|
7
12
|
"scripts": {
|
|
8
13
|
"build": "tsup"
|
|
@@ -14,7 +19,6 @@
|
|
|
14
19
|
"test"
|
|
15
20
|
],
|
|
16
21
|
"author": "Judah Silva",
|
|
17
|
-
"license": "MIT",
|
|
18
22
|
"dependencies": {
|
|
19
23
|
"@babylonjs/core": "^7.50.0",
|
|
20
24
|
"numeric": "^1.2.6",
|
|
Binary file
|