Skip to content

Commit 08c4161

Browse files
committed
update: add connection lines deletion
1 parent 8ed318b commit 08c4161

File tree

7 files changed

+169
-25
lines changed

7 files changed

+169
-25
lines changed

src/mixins/lines/LinesManager.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as THREE from "three";
22

3-
import { ATOM_CONNECTION_LINE_NAME } from "../../enums";
3+
import { ATOM_CONNECTION_LINE_NAME, COLORS } from "../../enums";
44
import settings from "../../settings";
55
import { BaseTHREEGroupManager } from "../base";
66
import { calculateMidpoint, getAtomWorldPosition } from "../threeJsUtils";
@@ -40,6 +40,41 @@ export class LinesManager extends BaseTHREEGroupManager {
4040
});
4141
}
4242

43+
/**
44+
* Creates an angle line connecting three atoms
45+
*/
46+
createAngleBetweenAtoms(
47+
firstAtom: THREE.Object3D,
48+
middleAtom: THREE.Object3D,
49+
lastAtom: THREE.Object3D,
50+
): THREE.Line {
51+
// Create a line geometry with three points
52+
const firstPoint = getAtomWorldPosition(firstAtom);
53+
const middlePoint = getAtomWorldPosition(middleAtom);
54+
const lastPoint = getAtomWorldPosition(lastAtom);
55+
56+
const geometry = new THREE.BufferGeometry().setFromPoints([
57+
firstPoint,
58+
middlePoint,
59+
lastPoint,
60+
]);
61+
62+
const material = new THREE.LineBasicMaterial({ color: settings.colors.amber });
63+
const line = new THREE.Line(geometry, material);
64+
line.name = ATOM_CONNECTION_LINE_NAME;
65+
66+
line.userData.atomicIndices = [
67+
firstAtom.userData.atomicIndex,
68+
middleAtom.userData.atomicIndex,
69+
lastAtom.userData.atomicIndex,
70+
];
71+
72+
line.userData.isAngleLine = true;
73+
74+
this.THREEGroup.add(line);
75+
return line;
76+
}
77+
4378
/**
4479
* Gets the center position of a line
4580
*/
@@ -68,4 +103,41 @@ export class LinesManager extends BaseTHREEGroupManager {
68103
(child) => child.type === "Line" && child.name === ATOM_CONNECTION_LINE_NAME,
69104
) as THREE.Line[];
70105
}
106+
107+
setLineAsHovered(line: THREE.Line): void {
108+
if (!line.userData.selected) {
109+
(line.material as THREE.LineBasicMaterial).color.set(COLORS.GREEN);
110+
}
111+
line.userData.hovered = true;
112+
}
113+
114+
unsetLineAsHovered(line: THREE.Line): void {
115+
if (!line.userData.selected) {
116+
(line.material as THREE.LineBasicMaterial).color.set(settings.colors.amber);
117+
}
118+
line.userData.hovered = false;
119+
}
120+
121+
setLineAsSelected(line: THREE.Line): void {
122+
this.deselectAllLines();
123+
124+
line.userData.selected = true;
125+
(line.material as THREE.LineBasicMaterial).color.set(COLORS.GREEN);
126+
}
127+
128+
unsetLineAsSelected(line: THREE.Line): void {
129+
line.userData.selected = false;
130+
(line.material as THREE.LineBasicMaterial).color.set(
131+
line.userData.hovered ? COLORS.GREEN : settings.colors.amber,
132+
);
133+
}
134+
135+
deselectAllLines(): void {
136+
this.getLines().forEach((line) => {
137+
line.userData.selected = false;
138+
(line.material as THREE.LineBasicMaterial).color.set(
139+
line.userData.hovered ? COLORS.GREEN : settings.colors.amber,
140+
);
141+
});
142+
}
71143
}

src/mixins/listeners/mixins.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const RaycasterMixinWithListeners = <T extends Constructor>(superclass: T
3333

3434
pointer: THREE.Vector2 = new THREE.Vector2();
3535

36-
intersectedAtom: THREE.Object3D | null = null;
36+
intersectedObject: THREE.Object3D | null = null;
3737

3838
initRaycaster() {
3939
this.raycaster = new THREE.Raycaster();

src/mixins/measurements/all.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,18 @@ export const AllMeasurementsMixin = (superclass: any) =>
137137
resetAllMeasurements() {
138138
this.measurementManagers.forEach((manager) => manager.resetMeasurements());
139139
}
140+
141+
142+
deleteConnection() {
143+
const activeManager = this.getActiveMeasurementManager();
144+
if (activeManager && activeManager.currentSelectedLine) {
145+
activeManager.deleteSelectedLine();
146+
this.render();
147+
148+
// Update state if needed
149+
if (activeManager.updateState) {
150+
activeManager.updateState(activeManager.getSettings());
151+
}
152+
}
153+
}
140154
};

src/mixins/measurements/angle.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import * as THREE from "three";
33
import { MEASUREMENT_MODES_ENUM } from "../../enums";
44
import { AngleLabelsManager } from "../labels/angle";
55
import { LabelsManagerConstructor } from "../labels/base";
6-
import { LinesManager } from "../lines/LinesManager";
76
import { calculateAngleBetweenAtoms, calculateAngleLabelPosition } from "../threeJsUtils";
87
import { BaseMeasurementManager } from "./base";
98

@@ -12,8 +11,6 @@ export class AnglesMeasurementManager extends BaseMeasurementManager<AngleLabels
1211

1312
override LabelsManagerCls: LabelsManagerConstructor<AngleLabelsManager> = AngleLabelsManager;
1413

15-
linesManager: LinesManager;
16-
1714
constructor(
1815
waveStructureGroup: THREE.Group,
1916
waveCamera: THREE.Camera,
@@ -23,7 +20,6 @@ export class AnglesMeasurementManager extends BaseMeasurementManager<AngleLabels
2320
const groupName = MEASUREMENT_MODES_ENUM.ANGLE;
2421
super(waveStructureGroup, waveCamera, wave, groupName, updateState);
2522
this.labelsManager = this.getLabelsManagerInstance();
26-
this.linesManager = new LinesManager(waveStructureGroup, waveCamera, wave, groupName);
2723
}
2824

2925
// @ts-ignore
@@ -76,9 +72,12 @@ export class AnglesMeasurementManager extends BaseMeasurementManager<AngleLabels
7672

7773
triplets.forEach((triplet) => {
7874
if (triplet.length === 3) {
79-
const line1 = this.linesManager.createLineBetweenAtoms(triplet[0], triplet[1]);
80-
const line2 = this.linesManager.createLineBetweenAtoms(triplet[1], triplet[2]);
81-
lines.push(line1, line2);
75+
const angleLine = this.linesManager.createAngleBetweenAtoms(
76+
triplet[0],
77+
triplet[1],
78+
triplet[2],
79+
);
80+
lines.push(angleLine);
8281
}
8382
});
8483

src/mixins/measurements/base.ts

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import * as THREE from "three";
33
import { MEASUREMENT_MODES_ENUM } from "../../enums";
44
import { BaseTHREEGroupManager } from "../base";
55
import { BaseLabelsManager, LabelsManagerConstructor } from "../labels/base";
6+
import { LinesManager } from "../lines/LinesManager";
67
import { RaycasterMixinWithListeners } from "../listeners/mixins";
78
import {
89
getObjectCoordinateAsArray,
910
highlightAtom,
1011
isIntersectionObjectAnAtom,
12+
isObjectAnAtom,
1113
setAtomAsHovered,
1214
setColorForAtom,
1315
unsetAtomAsHovered,
@@ -32,8 +34,12 @@ export class BaseMeasurementManager<T extends BaseLabelsManager> extends BaseMan
3234

3335
labelsManager: any;
3436

37+
linesManager: LinesManager;
38+
3539
updateState: any;
3640

41+
currentSelectedLine: THREE.Line | null = null;
42+
3743
constructor(
3844
waveStructureGroup: THREE.Group,
3945
waveCamera: THREE.Camera,
@@ -44,8 +50,9 @@ export class BaseMeasurementManager<T extends BaseLabelsManager> extends BaseMan
4450
super(waveStructureGroup, waveCamera, wave, groupName + "-measurement-group");
4551
this.initRaycaster();
4652
this.selectedAtoms = [];
47-
this.intersectedAtom = null;
53+
this.intersectedObject = null;
4854
this.canvas = wave.renderer.domElement;
55+
this.linesManager = new LinesManager(waveStructureGroup, waveCamera, wave, groupName);
4956
this.updateState = updateState;
5057
}
5158

@@ -93,21 +100,24 @@ export class BaseMeasurementManager<T extends BaseLabelsManager> extends BaseMan
93100
);
94101
}
95102

96-
setIntersectedAtom(intersectItem: THREE.Object3D | null) {
97-
if (this.intersectedAtom !== intersectItem) {
98-
this.intersectedAtom = intersectItem;
103+
setIntersectedAtom(intersectItem: THREE.Object3D) {
104+
if (this.intersectedObject !== intersectItem && isObjectAnAtom(intersectItem)) {
105+
this.intersectedObject = intersectItem;
99106
}
100107
}
101108

102109
isIntersectedAtomSelected() {
103-
if (!this.intersectedAtom) return false;
110+
if (!this.intersectedObject) return false;
104111
return this.selectedAtoms.some(
105-
(atom) => atom.userData.atomicIndex === this.intersectedAtom?.userData.atomicIndex,
112+
(atom) => atom.userData.atomicIndex === this.intersectedObject?.userData.atomicIndex,
106113
);
107114
}
108115

109116
getIntersections() {
110-
return this.raycaster.intersectObjects([...this.wave.getAtomGroups()], true);
117+
return this.raycaster.intersectObjects(
118+
[...this.wave.getAtomGroups(), ...this.linesManager.getLines()],
119+
true,
120+
);
111121
}
112122

113123
toggleAtomSelection(atomObject: THREE.Object3D) {
@@ -118,6 +128,17 @@ export class BaseMeasurementManager<T extends BaseLabelsManager> extends BaseMan
118128
}
119129
}
120130

131+
toggleLineSelection(line: THREE.Line) {
132+
console.log("toggleLineSelection", line);
133+
console.log("selected line", this.currentSelectedLine);
134+
135+
if (line.userData.selected) {
136+
this.handleLineDeselection(line);
137+
} else {
138+
this.handleLineSelection(line);
139+
}
140+
}
141+
121142
refillSelectedAtoms(): void {
122143
const validAtoms: THREE.Object3D[] = [];
123144

@@ -144,12 +165,17 @@ export class BaseMeasurementManager<T extends BaseLabelsManager> extends BaseMan
144165
if (!this.isActive) return;
145166
this.checkMouseCoordinates(event, this.waveCamera);
146167
const intersects = this.getIntersections();
168+
console.log("INTERSECTS", intersects);
147169

148170
intersects.forEach((object: THREE.Intersection<THREE.Object3D<THREE.Object3DEventMap>>) => {
149171
if (isIntersectionObjectAnAtom(object)) {
150172
const atom = object.object;
151173
this.toggleAtomSelection(atom);
152174
}
175+
if (object.object.type === "Line") {
176+
console.log("LINE", object.object);
177+
this.toggleLineSelection(object.object);
178+
}
153179
});
154180
this.copyValuesToClipboard();
155181
}
@@ -159,17 +185,17 @@ export class BaseMeasurementManager<T extends BaseLabelsManager> extends BaseMan
159185
this.checkMouseCoordinates(event, this.waveCamera);
160186
const intersects = this.getIntersections();
161187

162-
intersects.forEach((object: THREE.Intersection<THREE.Object3D<THREE.Object3DEventMap>>) => {
188+
intersects.forEach((object: THREE.Intersection<THREE.Object3D | THREE.Line>) => {
163189
if (isIntersectionObjectAnAtom(object)) {
164190
this.setIntersectedAtom(object.object);
165191
setAtomAsHovered(object.object);
166192
}
167193
});
168194

169-
if (!intersects.length && this.intersectedAtom) {
195+
if (!intersects.length && this.intersectedObject) {
170196
const isSelected = this.isIntersectedAtomSelected();
171-
if (this.intersectedAtom && !isSelected) {
172-
unsetAtomAsHovered(this.intersectedAtom);
197+
if (this.intersectedObject && !isSelected && isObjectAnAtom(this.intersectedObject)) {
198+
unsetAtomAsHovered(this.intersectedObject);
173199
}
174200
this.setIntersectedAtom(null);
175201
}
@@ -243,4 +269,38 @@ export class BaseMeasurementManager<T extends BaseLabelsManager> extends BaseMan
243269
this.THREEGroup.clear();
244270
this.labelsManager.THREEGroup.clear();
245271
}
272+
273+
handleLineSelection(line: THREE.Line): void {
274+
this.linesManager.setLineAsSelected(line);
275+
this.currentSelectedLine = line;
276+
}
277+
278+
handleLineDeselection(line: THREE.Line): void {
279+
this.linesManager.unsetLineAsSelected(line);
280+
this.currentSelectedLine = null;
281+
}
282+
283+
removeAtomsFromSelectionByIndices(atomicIndices: number[]): void {
284+
this.selectedAtoms = this.selectedAtoms.filter(
285+
(atom) => !atomicIndices.includes(atom.userData.atomicIndex),
286+
);
287+
288+
atomicIndices.forEach((index) => {
289+
const atom = this.getAtomObjectByAtomicIndex(index);
290+
if (atom) {
291+
atom.userData.selected = false;
292+
setColorForAtom(atom);
293+
}
294+
});
295+
}
296+
297+
deleteSelectedLine(): void {
298+
if (this.currentSelectedLine) {
299+
const atomicIndices = this.currentSelectedLine.userData.atomicIndices || [];
300+
this.linesManager.removeLine(this.currentSelectedLine);
301+
this.removeAtomsFromSelectionByIndices(atomicIndices);
302+
this.currentSelectedLine = null;
303+
this.createMeasurements();
304+
}
305+
}
246306
}

src/mixins/measurements/distance.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ export class DistancesMeasurementManager extends BaseMeasurementManager<Distance
1313
override LabelsManagerCls: LabelsManagerConstructor<DistanceLabelsManager> =
1414
DistanceLabelsManager;
1515

16-
currentSelectedLine: THREE.Line | null = null;
17-
18-
linesManager: LinesManager;
19-
2016
constructor(
2117
waveStructureGroup: THREE.Group,
2218
waveCamera: THREE.Camera,
@@ -32,7 +28,6 @@ export class DistancesMeasurementManager extends BaseMeasurementManager<Distance
3228
groupName,
3329
);
3430
this.currentSelectedLine = null;
35-
this.linesManager = new LinesManager(waveStructureGroup, waveCamera, wave, groupName);
3631
}
3732

3833
// @ts-ignore

src/mixins/threeJsUtils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,7 @@ export function unsetAtomAsHovered(atom: THREE.Object3D): void {
164164
atomObject.userData.hovered = false;
165165
setColorForAtom(atom);
166166
}
167+
168+
export function isObjectAnAtom(object: THREE.Object3D): object is AtomObject {
169+
return object instanceof THREE.Mesh;
170+
}

0 commit comments

Comments
 (0)