Skip to content

Commit b118c5f

Browse files
authored
Improve ui of "adjust last operation" of translate (#101)
Implemented dynamic transform UI for translate constraints, so that the UI reflects axis + distance.
1 parent a90ca00 commit b118c5f

5 files changed

Lines changed: 139 additions & 2 deletions

File tree

src/controls/TransformControls.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,21 @@ export class TransformControls {
111111
const mode = this.mode;
112112
if (this.mode === "translate") {
113113
const translateVector = this.getTranslateVector(this.eventHandler.currentMousePosition, this.initialMousePosition);
114-
const translateOperation = new TranslateOperation({ weas: this.weas, vector: translateVector });
114+
const constraintType = this.translateConstraintType;
115+
let axis = null;
116+
let planeNormal = null;
117+
if (constraintType === "axis") {
118+
axis = this.translateAxis.clone();
119+
} else if (constraintType === "plane" || constraintType === "normal") {
120+
planeNormal = this.translatePlaneNormal.clone();
121+
}
122+
const translateOperation = new TranslateOperation({
123+
weas: this.weas,
124+
vector: translateVector,
125+
constraintType,
126+
axis,
127+
planeNormal,
128+
});
115129
this.weas.ops.execute(translateOperation, false);
116130
} else if (this.mode === "rotate") {
117131
const rotationAngle = this.getRotationAngle(this.eventHandler.currentMousePosition, this.initialMousePosition);

src/operation/transform.js

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class TranslateOperation extends BaseOperation {
1313
},
1414
};
1515

16-
constructor({ weas, vector = new THREE.Vector3() }) {
16+
constructor({ weas, vector = new THREE.Vector3(), constraintType = null, axis = null, planeNormal = null }) {
1717
super(weas);
1818
// currentFrame
1919
this.currentFrame = weas.avr.currentFrame;
@@ -25,6 +25,26 @@ class TranslateOperation extends BaseOperation {
2525
vector = new THREE.Vector3(vector[0], vector[1], vector[2]);
2626
}
2727
this.vector = vector.clone();
28+
this.constraintType = constraintType || null; // null | "axis" | "plane" | "normal"
29+
this.axis = axis ? axis.clone() : new THREE.Vector3();
30+
this.normal = new THREE.Vector3();
31+
this.planeNormal = new THREE.Vector3();
32+
this.planeU = new THREE.Vector3();
33+
this.planeV = new THREE.Vector3();
34+
this.distance = 0;
35+
this.uDistance = 0;
36+
this.vDistance = 0;
37+
if (this.constraintType === "axis" && this.axis.lengthSq() === 0) {
38+
this.axis.set(1, 0, 0);
39+
}
40+
if (this.constraintType === "normal" && planeNormal) {
41+
this.normal.copy(planeNormal);
42+
}
43+
if (this.constraintType === "plane" && planeNormal) {
44+
this.planeNormal.copy(planeNormal);
45+
}
46+
this.refreshConstraintStateFromVector();
47+
this.uiFields = this.buildUISchema();
2848
}
2949

3050
execute() {
@@ -50,6 +70,109 @@ class TranslateOperation extends BaseOperation {
5070
this.undo();
5171
});
5272
}
73+
74+
buildUISchema() {
75+
if (this.constraintType === "axis") {
76+
return {
77+
title: "Translate (Axis)",
78+
fields: {
79+
distance: { type: "number", min: -10, max: 10, step: 0.1 },
80+
axisX: { type: "number", min: -1, max: 1, step: 0.01, path: "axis.x" },
81+
axisY: { type: "number", min: -1, max: 1, step: 0.01, path: "axis.y" },
82+
axisZ: { type: "number", min: -1, max: 1, step: 0.01, path: "axis.z" },
83+
},
84+
};
85+
}
86+
if (this.constraintType === "normal") {
87+
return {
88+
title: "Translate (Normal)",
89+
fields: {
90+
distance: { type: "number", min: -10, max: 10, step: 0.1 },
91+
normalX: { type: "number", min: -1, max: 1, step: 0.01, path: "normal.x" },
92+
normalY: { type: "number", min: -1, max: 1, step: 0.01, path: "normal.y" },
93+
normalZ: { type: "number", min: -1, max: 1, step: 0.01, path: "normal.z" },
94+
},
95+
};
96+
}
97+
if (this.constraintType === "plane") {
98+
return {
99+
title: "Translate (Plane)",
100+
fields: {
101+
u: { type: "number", min: -10, max: 10, step: 0.1, path: "uDistance" },
102+
v: { type: "number", min: -10, max: 10, step: 0.1, path: "vDistance" },
103+
normalX: { type: "number", min: -1, max: 1, step: 0.01, path: "planeNormal.x" },
104+
normalY: { type: "number", min: -1, max: 1, step: 0.01, path: "planeNormal.y" },
105+
normalZ: { type: "number", min: -1, max: 1, step: 0.01, path: "planeNormal.z" },
106+
},
107+
};
108+
}
109+
return this.constructor.ui || null;
110+
}
111+
112+
applyParams(params) {
113+
super.applyParams(params);
114+
this.refreshVectorFromConstraint();
115+
}
116+
117+
refreshConstraintStateFromVector() {
118+
if (this.constraintType === "axis") {
119+
this.normalizeAxis(this.axis);
120+
this.distance = this.vector.dot(this.axis);
121+
return;
122+
}
123+
if (this.constraintType === "normal") {
124+
this.normalizeAxis(this.normal, new THREE.Vector3(0, 0, 1));
125+
this.distance = this.vector.dot(this.normal);
126+
return;
127+
}
128+
if (this.constraintType === "plane") {
129+
this.ensurePlaneBasis();
130+
this.uDistance = this.vector.dot(this.planeU);
131+
this.vDistance = this.vector.dot(this.planeV);
132+
return;
133+
}
134+
}
135+
136+
refreshVectorFromConstraint() {
137+
if (this.constraintType === "axis") {
138+
this.normalizeAxis(this.axis);
139+
this.vector.copy(this.axis).multiplyScalar(this.distance || 0);
140+
return;
141+
}
142+
if (this.constraintType === "normal") {
143+
this.normalizeAxis(this.normal, new THREE.Vector3(0, 0, 1));
144+
this.vector.copy(this.normal).multiplyScalar(this.distance || 0);
145+
return;
146+
}
147+
if (this.constraintType === "plane") {
148+
this.ensurePlaneBasis();
149+
this.vector
150+
.copy(this.planeU)
151+
.multiplyScalar(this.uDistance || 0)
152+
.add(this.planeV.clone().multiplyScalar(this.vDistance || 0));
153+
}
154+
}
155+
156+
normalizeAxis(axis, fallback = new THREE.Vector3(1, 0, 0)) {
157+
if (!axis || axis.lengthSq() === 0) {
158+
axis.copy(fallback);
159+
}
160+
axis.normalize();
161+
}
162+
163+
ensurePlaneBasis() {
164+
this.normalizeAxis(this.planeNormal, new THREE.Vector3(0, 0, 1));
165+
const n = this.planeNormal;
166+
const ref = Math.abs(n.x) < 0.9 ? new THREE.Vector3(1, 0, 0) : new THREE.Vector3(0, 1, 0);
167+
const u = new THREE.Vector3().crossVectors(n, ref);
168+
if (u.lengthSq() === 0) {
169+
u.crossVectors(n, new THREE.Vector3(0, 0, 1));
170+
}
171+
u.normalize();
172+
const v = new THREE.Vector3().crossVectors(n, u).normalize();
173+
this.planeU.copy(u);
174+
this.planeV.copy(v);
175+
}
53176
}
54177

55178
class RotateOperation extends BaseOperation {
3.1 KB
Loading
3.16 KB
Loading
414 Bytes
Loading

0 commit comments

Comments
 (0)