Skip to content

Commit a16509d

Browse files
phtaylormauroporras
authored andcommitted
fix: AxialRotationHandle now correctly handles rotating objects (#187)
* fix: AxialRotationHandle now correctly handles rotating objects around arbitrary axes, when not connected to the selection manager. - simplified the Handles overall.
1 parent d099341 commit a16509d

14 files changed

+131
-194
lines changed

src/Handles/ArcSlider.ts

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@ import {
1010
Circle,
1111
Sphere,
1212
Registry,
13-
Parameter,
1413
ZeaPointerEvent,
1514
ZeaMouseEvent,
1615
ZeaTouchEvent,
17-
XRControllerEvent,
1816
} from '@zeainc/zea-engine'
19-
import { BaseAxialRotationHandle } from './BaseAxialRotationHandle'
17+
import { Handle } from './Handle'
2018
import ParameterValueChange from '../UndoRedo/Changes/ParameterValueChange'
2119
import './Shaders/HandleShader'
2220
import UndoRedoManager from '../UndoRedo/UndoRedoManager'
21+
import { Change } from '../UndoRedo/Change'
2322

2423
/**
2524
* Class representing a slider scene widget with an arc shape. There are two parts in this widget, the slider and the handle.<br>
@@ -35,17 +34,22 @@ import UndoRedoManager from '../UndoRedo/UndoRedoManager'
3534
* * **dragStart:** Triggered when the pointer is down.
3635
* * **dragEnd:** Triggered when the pointer is released.
3736
*
38-
* @extends BaseAxialRotationHandle
37+
* @extends Handle
3938
*/
40-
class ArcSlider extends BaseAxialRotationHandle {
39+
class ArcSlider extends Handle {
40+
param: XfoParameter | NumberParameter
4141
arcRadiusParam: NumberParameter
4242
arcAngleParam: NumberParameter
4343
handleRadiusParam: NumberParameter
44-
handleMat: Material
45-
handle: GeomItem
46-
arc: GeomItem
47-
handleXfo = new Xfo()
48-
handleGeomOffsetXfo = new Xfo()
44+
range: Array<number>
45+
private handleMat: Material
46+
private handle: GeomItem
47+
private arc: GeomItem
48+
private baseXfo: Xfo
49+
private handleXfo = new Xfo()
50+
private vec0: Vec3
51+
private change: Change
52+
private handleGeomOffsetXfo = new Xfo()
4953

5054
/**
5155
* Creates an instance of ArcSlider.
@@ -81,27 +85,27 @@ class ArcSlider extends BaseAxialRotationHandle {
8185
this.handle.geomOffsetXfoParam.value = this.handleGeomOffsetXfo
8286

8387
// this.barRadiusParam.on('valueChanged', () => {
84-
// arcGeom.radiusParam.value = this.barRadiusParam.getValue();
88+
// arcGeom.radiusParam.value = this.barRadiusParam.value;
8589
// });
8690

8791
this.range = [0, arcAngle]
8892
this.arcAngleParam.on('valueChanged', () => {
89-
const arcAngle = this.arcAngleParam.getValue()
93+
const arcAngle = this.arcAngleParam.value
9094
arcGeom.angleParam.value = arcAngle
9195
this.range = [0, arcAngle]
9296
})
9397
this.arcRadiusParam.on('valueChanged', () => {
94-
const arcRadius = this.arcRadiusParam.getValue()
98+
const arcRadius = this.arcRadiusParam.value
9599
arcGeom.radiusParam.value = arcRadius
96100
this.handleGeomOffsetXfo.tr.x = arcRadius
97101
this.handle.geomOffsetXfoParam.value = this.handleGeomOffsetXfo
98102
})
99103
this.handleRadiusParam.on('valueChanged', () => {
100-
handleGeom.radiusParam.value = this.handleRadiusParam.getValue()
104+
handleGeom.radiusParam.value = this.handleRadiusParam.value
101105
})
102106

103107
this.colorParam.on('valueChanged', () => {
104-
this.handleMat.getParameter('BaseColor').value = this.colorParam.getValue()
108+
this.handleMat.getParameter('BaseColor').value = this.colorParam.value
105109
})
106110

107111
this.addChild(this.handle)
@@ -149,34 +153,21 @@ class ArcSlider extends BaseAxialRotationHandle {
149153
*/
150154
highlight(): void {
151155
super.highlight()
152-
this.handleMat.getParameter('BaseColor').value = this.highlightColorParam.getValue()
156+
this.handleMat.getParameter('BaseColor').value = this.highlightColorParam.value
153157
}
154158

155159
/**
156160
* Removes the shining shader from the handle.
157161
*/
158162
unhighlight(): void {
159163
super.unhighlight()
160-
this.handleMat.getParameter('BaseColor').value = this.colorParam.getValue()
164+
this.handleMat.getParameter('BaseColor').value = this.colorParam.value
161165
}
162166

163-
// /**
164-
// * The setTargetParam method.
165-
// * @param param - The param param.
166-
// */
167-
// setTargetParam(param) {
168-
// this.param = param;
169-
// const __updateSlider = () => {
170-
// this.__updateSlider(param.getValue());
171-
// };
172-
// __updateSlider();
173-
// param.on('valueChanged', __updateSlider);
174-
// }
175-
176167
/**
177168
* Sets global xfo target parameter
178169
*
179-
* @param param - The param param.
170+
* @param param - The parameter that will be modified during manipulation
180171
* @param track - The track param.
181172
*/
182173
setTargetParam(param: XfoParameter | NumberParameter, track = true): void {
@@ -190,7 +181,7 @@ class ArcSlider extends BaseAxialRotationHandle {
190181
param.on('valueChanged', __updateGizmo)
191182
} else if (this.param instanceof NumberParameter) {
192183
const __updateGizmo = () => {
193-
this.handleXfo.ori.setFromAxisAndAngle(new Vec3(0, 0, 1), <number>param.getValue())
184+
this.handleXfo.ori.setFromAxisAndAngle(new Vec3(0, 0, 1), <number>param.value)
194185
this.handle.globalXfoParam.value = this.handleXfo
195186
}
196187
__updateGizmo()
@@ -205,7 +196,7 @@ class ArcSlider extends BaseAxialRotationHandle {
205196
// const range =
206197
// this.param && this.param.getRange() ? this.param.getRange() : [0, 1];
207198
// const v = Math.remap(value, range[0], range[1], 0, 1);
208-
// const length = this.arcAngleParam.getValue();
199+
// const length = this.arcAngleParam.value;
209200
// this.handleXfo.ori.setFromAxisAndAngle(this.axis, ) = v * length;
210201
// this.handle.localXfoParam.value = this.handleXfo;
211202
// }
@@ -230,10 +221,10 @@ class ArcSlider extends BaseAxialRotationHandle {
230221
onDragStart(event: ZeaPointerEvent): void {
231222
this.baseXfo = this.globalXfoParam.value.clone()
232223
this.baseXfo.sc.set(1, 1, 1)
233-
// this.offsetXfo = this.baseXfo.inverse().multiply(this.param.getValue());
224+
// this.offsetXfo = this.baseXfo.inverse().multiply(this.param.value);
234225

235226
this.vec0 = this.globalXfoParam.value.ori.getXaxis()
236-
// this.grabCircleRadius = this.arcRadiusParam.getValue();
227+
// this.grabCircleRadius = this.arcRadiusParam.value;
237228
this.vec0.normalizeInPlace()
238229

239230
this.change = new ParameterValueChange(this.param)

src/Handles/AxialRotationHandle.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ import './Shaders/HandleShader'
2020
*/
2121
class AxialRotationHandle extends BaseAxialRotationHandle {
2222
radiusParam: NumberParameter
23-
handleMat: Material
24-
handleXfo: Xfo
23+
private handleMat: Material
2524
handle: GeomItem
2625
/**
2726
* Create an axial rotation scene widget.
@@ -46,7 +45,6 @@ class AxialRotationHandle extends BaseAxialRotationHandle {
4645
// const handleGeom = new Cylinder(radius, thickness * 2, 64, 2, false);
4746
const handleGeom = new Torus(thickness, radius, 64, Math.PI * 0.5)
4847
this.handle = new GeomItem('handle', handleGeom, this.handleMat)
49-
this.handleXfo = new Xfo()
5048

5149
this.radiusParam.on('valueChanged', () => {
5250
radius = this.radiusParam.getValue()

src/Handles/BaseAxialRotationHandle.ts

Lines changed: 24 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {
22
MathFunctions,
3-
Parameter,
43
Vec3,
54
Xfo,
65
XfoParameter,
@@ -14,22 +13,22 @@ import ParameterValueChange from '../UndoRedo/Changes/ParameterValueChange'
1413
import UndoRedoManager from '../UndoRedo/UndoRedoManager'
1514
import SelectionXfoChange from '../UndoRedo/Changes/SelectionXfoChange'
1615
import SelectionGroup from '../SelectionGroup'
17-
import { Change } from '..'
16+
import { Change } from '../UndoRedo/Change'
1817

1918
/**
2019
* Class representing an axial rotation scene widget.
2120
*
2221
* @extends Handle
2322
*/
2423
class BaseAxialRotationHandle extends Handle {
25-
param: NumberParameter | XfoParameter
26-
baseXfo: Xfo
27-
grabCircleRadius: number
28-
vec0: Vec3
29-
change: Change
30-
range: Array<number>
31-
24+
param: XfoParameter
25+
private baseXfo: Xfo
26+
private handleXfo: Xfo
27+
private handleToTargetXfo: Xfo
28+
private vec0: Vec3
29+
private change: Change
3230
selectionGroup: SelectionGroup
31+
3332
/**
3433
* Create an axial rotation scene widget.
3534
*
@@ -49,28 +48,21 @@ class BaseAxialRotationHandle extends Handle {
4948
}
5049

5150
/**
52-
* Sets global xfo target parameter
51+
* Sets the target parameter for this manipulator.
52+
* This parameter will be modified by interactions on the manipulator.
5353
*
54-
* @param param - The param param.
55-
* @param track - The track param.
54+
* @param param - The parameter that will be modified during manipulation
5655
*/
57-
setTargetParam(param: XfoParameter, track = true): void {
56+
setTargetParam(param: XfoParameter): void {
5857
this.param = param
59-
if (track) {
60-
const __updateGizmo = () => {
61-
this.globalXfoParam.value = param.getValue()
62-
}
63-
__updateGizmo()
64-
param.on('valueChanged', __updateGizmo)
65-
}
6658
}
6759

6860
/**
6961
* Returns target's global xfo parameter.
7062
*
71-
* @return {Parameter} - returns handle's target global Xfo.
63+
* @return {Parameter} - returns parameter
7264
*/
73-
getTargetParam(): NumberParameter | XfoParameter {
65+
getTargetParam(): XfoParameter {
7466
return this.param ? this.param : this.globalXfoParam
7567
}
7668

@@ -83,7 +75,6 @@ class BaseAxialRotationHandle extends Handle {
8375
this.baseXfo = this.globalXfoParam.value.clone()
8476

8577
this.vec0 = this.grabPos.subtract(this.baseXfo.tr)
86-
this.grabCircleRadius = this.vec0.length()
8778
this.vec0.normalizeInPlace()
8879

8980
// this.offsetXfo = this.localXfoParam.value.inverse()
@@ -92,7 +83,9 @@ class BaseAxialRotationHandle extends Handle {
9283
this.change = new SelectionXfoChange(Array.from(items), this.baseXfo)
9384
UndoRedoManager.getInstance().addChange(this.change)
9485
} else {
95-
const param = this.getTargetParam()
86+
const invBaseXfo = this.baseXfo.inverse()
87+
const param = this.getTargetParam() as XfoParameter
88+
this.handleToTargetXfo = invBaseXfo.multiply(param.value)
9689
this.change = new ParameterValueChange(param)
9790
UndoRedoManager.getInstance().addChange(this.change)
9891
}
@@ -105,29 +98,13 @@ class BaseAxialRotationHandle extends Handle {
10598
*/
10699
onDrag(event: ZeaPointerEvent): void {
107100
const vec1 = this.holdPos.subtract(this.baseXfo.tr)
108-
// const dragCircleRadius = vec1.length()
109101
vec1.normalizeInPlace()
110-
111-
// modulate the angle by the radius the mouse moves
112-
// away from the center of the handle.
113-
// This makes it possible to rotate the object more than
114-
// 180 degrees in a single movement.
115-
// Note: this modulator made rotations quite unpredictable
116-
// especially when the angle between the ray and the plane is acute.
117-
// disabling for now.
118-
const modulator = 1.0 //dragCircleRadius / this.grabCircleRadius
119-
let angle = this.vec0.angleTo(vec1) * modulator
102+
let angle = this.vec0.angleTo(vec1)
120103
if (this.vec0.cross(vec1).dot(this.baseXfo.ori.getZaxis()) < 0) angle = -angle
121104

122-
if (this.range) {
123-
angle = MathFunctions.clamp(angle, this.range[0], this.range[1])
124-
}
125-
126105
if ((event instanceof ZeaMouseEvent || event instanceof ZeaTouchEvent) && event.shiftKey) {
127-
// modulat the angle to X degree increments.
128-
const degree: number = 22.5
129-
const rad: number = degree * (Math.PI / 180)
130-
const increment = rad //Math.degToRad(22.5)
106+
// modulate the angle to X degree increments.
107+
const increment = MathFunctions.degToRad(22.5)
131108
angle = Math.floor(angle / increment) * increment
132109
}
133110

@@ -138,8 +115,10 @@ class BaseAxialRotationHandle extends Handle {
138115
const selectionXfoChange = <SelectionXfoChange>this.change
139116
selectionXfoChange.setDeltaXfo(deltaXfo)
140117
} else {
141-
const value = this.baseXfo.clone()
142-
value.ori = deltaXfo.ori.multiply(value.ori)
118+
// Add the values in global space.
119+
const newBase = this.baseXfo.clone()
120+
newBase.ori = deltaXfo.ori.multiply(newBase.ori)
121+
const value = newBase.multiply(this.handleToTargetXfo)
143122

144123
this.change.update({
145124
value,

src/Handles/Handle.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,12 @@ class Handle extends TreeItem {
282282
console.warn('@Handle#onDragEnd - Implement me!', event)
283283
}
284284

285-
// TODO:(check) added this method since we check for type Handle and call this method in XfoHandle.ts
286-
setTargetParam(param: Parameter<unknown>, track: boolean): void {
285+
/**
286+
* Sets the target parameter for manipulation
287+
*
288+
* @param param - The parameter that will be modified during manipulation
289+
*/
290+
setTargetParam(param: Parameter<unknown>): void {
287291
console.warn('setTargetParam not implemented')
288292
}
289293
}

0 commit comments

Comments
 (0)