Adjust translate gizmo rendering
This commit is contained in:
parent
56295c8c9d
commit
2359533df5
1 changed files with 54 additions and 36 deletions
|
|
@ -33,7 +33,7 @@ const HIGHLIGHT_COLORS = {
|
||||||
|
|
||||||
export function createTranslateGizmo(options: TranslateGizmoOptions = {}): TranslateGizmo {
|
export function createTranslateGizmo(options: TranslateGizmoOptions = {}): TranslateGizmo {
|
||||||
const {
|
const {
|
||||||
arrowLength = 1.0,
|
arrowLength = 1.0, // Extension past bounding box
|
||||||
arrowHeadLength = 0.2,
|
arrowHeadLength = 0.2,
|
||||||
arrowHeadRadius = 0.08,
|
arrowHeadRadius = 0.08,
|
||||||
shaftRadius = 0.025,
|
shaftRadius = 0.025,
|
||||||
|
|
@ -50,17 +50,23 @@ export function createTranslateGizmo(options: TranslateGizmoOptions = {}): Trans
|
||||||
// Store arrow references for positioning
|
// Store arrow references for positioning
|
||||||
const arrows: Record<string, THREE.Group> = {}
|
const arrows: Record<string, THREE.Group> = {}
|
||||||
|
|
||||||
// Store world-space offsets from center to each arrow origin
|
// Store shaft and cone meshes for dynamic length adjustment
|
||||||
|
const shafts: Record<string, THREE.Mesh> = {}
|
||||||
|
const cones: Record<string, THREE.Mesh> = {}
|
||||||
|
|
||||||
|
// Store world-space half-sizes from center to each bounding box face
|
||||||
type ArrowKey = 'x_pos' | 'x_neg' | 'y_pos' | 'y_neg' | 'z_pos' | 'z_neg'
|
type ArrowKey = 'x_pos' | 'x_neg' | 'y_pos' | 'y_neg' | 'z_pos' | 'z_neg'
|
||||||
const arrowOffsets: Record<ArrowKey, THREE.Vector3> = {
|
const boundsHalfSizes: Record<ArrowKey, number> = {
|
||||||
x_pos: new THREE.Vector3(),
|
x_pos: 0,
|
||||||
x_neg: new THREE.Vector3(),
|
x_neg: 0,
|
||||||
y_pos: new THREE.Vector3(),
|
y_pos: 0,
|
||||||
y_neg: new THREE.Vector3(),
|
y_neg: 0,
|
||||||
z_pos: new THREE.Vector3(),
|
z_pos: 0,
|
||||||
z_neg: new THREE.Vector3(),
|
z_neg: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentScale = 1
|
||||||
|
|
||||||
function createArrow(axis: 'x' | 'y' | 'z', negative: boolean): THREE.Group {
|
function createArrow(axis: 'x' | 'y' | 'z', negative: boolean): THREE.Group {
|
||||||
const arrowGroup = new THREE.Group()
|
const arrowGroup = new THREE.Group()
|
||||||
const suffix = negative ? '_neg' : '_pos'
|
const suffix = negative ? '_neg' : '_pos'
|
||||||
|
|
@ -69,22 +75,21 @@ export function createTranslateGizmo(options: TranslateGizmoOptions = {}): Trans
|
||||||
|
|
||||||
const color = AXIS_COLORS[axis]
|
const color = AXIS_COLORS[axis]
|
||||||
|
|
||||||
// Create shaft (cylinder)
|
// Create shaft with unit height - will be scaled dynamically
|
||||||
const shaftLength = arrowLength - arrowHeadLength
|
const shaftGeometry = new THREE.CylinderGeometry(shaftRadius, shaftRadius, 1, 8)
|
||||||
const shaftGeometry = new THREE.CylinderGeometry(shaftRadius, shaftRadius, shaftLength, 8)
|
|
||||||
const shaftMaterial = new THREE.MeshBasicMaterial({ color })
|
const shaftMaterial = new THREE.MeshBasicMaterial({ color })
|
||||||
materials[`${axis}${suffix}_shaft`] = shaftMaterial
|
materials[`${axis}${suffix}_shaft`] = shaftMaterial
|
||||||
const shaft = new THREE.Mesh(shaftGeometry, shaftMaterial)
|
const shaft = new THREE.Mesh(shaftGeometry, shaftMaterial)
|
||||||
shaft.userData = { axis, isGizmoHandle: true, negative }
|
shaft.userData = { axis, isGizmoHandle: true, negative }
|
||||||
shaft.position.y = shaftLength / 2
|
shafts[`${axis}${suffix}`] = shaft
|
||||||
|
|
||||||
// Create cone (arrow head)
|
// Create cone (arrow head) - positioned dynamically
|
||||||
const coneGeometry = new THREE.ConeGeometry(arrowHeadRadius, arrowHeadLength, 16)
|
const coneGeometry = new THREE.ConeGeometry(arrowHeadRadius, arrowHeadLength, 16)
|
||||||
const coneMaterial = new THREE.MeshBasicMaterial({ color })
|
const coneMaterial = new THREE.MeshBasicMaterial({ color })
|
||||||
materials[`${axis}${suffix}_cone`] = coneMaterial
|
materials[`${axis}${suffix}_cone`] = coneMaterial
|
||||||
const cone = new THREE.Mesh(coneGeometry, coneMaterial)
|
const cone = new THREE.Mesh(coneGeometry, coneMaterial)
|
||||||
cone.userData = { axis, isGizmoHandle: true, negative }
|
cone.userData = { axis, isGizmoHandle: true, negative }
|
||||||
cone.position.y = shaftLength + arrowHeadLength / 2
|
cones[`${axis}${suffix}`] = cone
|
||||||
|
|
||||||
arrowGroup.add(shaft)
|
arrowGroup.add(shaft)
|
||||||
arrowGroup.add(cone)
|
arrowGroup.add(cone)
|
||||||
|
|
@ -111,8 +116,6 @@ export function createTranslateGizmo(options: TranslateGizmoOptions = {}): Trans
|
||||||
group.add(createArrow('z', false))
|
group.add(createArrow('z', false))
|
||||||
group.add(createArrow('z', true))
|
group.add(createArrow('z', true))
|
||||||
|
|
||||||
let currentScale = 1
|
|
||||||
|
|
||||||
function show(): void {
|
function show(): void {
|
||||||
group.visible = true
|
group.visible = true
|
||||||
}
|
}
|
||||||
|
|
@ -124,28 +127,43 @@ export function createTranslateGizmo(options: TranslateGizmoOptions = {}): Trans
|
||||||
function setPositionAndBounds(center: THREE.Vector3, box: THREE.Box3): void {
|
function setPositionAndBounds(center: THREE.Vector3, box: THREE.Box3): void {
|
||||||
group.position.copy(center)
|
group.position.copy(center)
|
||||||
|
|
||||||
// Calculate world-space offsets from center to each bounding box face
|
// Store world-space distances from center to each bounding box face
|
||||||
arrowOffsets.x_pos.set(box.max.x - center.x, 0, 0)
|
boundsHalfSizes.x_pos = box.max.x - center.x
|
||||||
arrowOffsets.x_neg.set(box.min.x - center.x, 0, 0)
|
boundsHalfSizes.x_neg = center.x - box.min.x
|
||||||
arrowOffsets.y_pos.set(0, box.max.y - center.y, 0)
|
boundsHalfSizes.y_pos = box.max.y - center.y
|
||||||
arrowOffsets.y_neg.set(0, box.min.y - center.y, 0)
|
boundsHalfSizes.y_neg = center.y - box.min.y
|
||||||
arrowOffsets.z_pos.set(0, 0, box.max.z - center.z)
|
boundsHalfSizes.z_pos = box.max.z - center.z
|
||||||
arrowOffsets.z_neg.set(0, 0, box.min.z - center.z)
|
boundsHalfSizes.z_neg = center.z - box.min.z
|
||||||
|
|
||||||
// Apply offsets to arrow positions (compensating for current scale)
|
// Update arrow lengths based on bounds
|
||||||
updateArrowPositions()
|
updateArrowLengths()
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateArrowPositions(): void {
|
function updateArrowLengths(): void {
|
||||||
// Position arrows so they appear at bounding box faces in world space
|
// Arrows originate from center and extend past bounding box by arrowLength
|
||||||
// Since group.scale affects child positions, we divide by scale to compensate
|
// Total length = boundsHalfSize + arrowLength (in world space)
|
||||||
|
// In local space, divide by currentScale
|
||||||
const keys: ArrowKey[] = ['x_pos', 'x_neg', 'y_pos', 'y_neg', 'z_pos', 'z_neg']
|
const keys: ArrowKey[] = ['x_pos', 'x_neg', 'y_pos', 'y_neg', 'z_pos', 'z_neg']
|
||||||
|
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const arrow = arrows[key]
|
const shaft = shafts[key]
|
||||||
const offset = arrowOffsets[key]
|
const cone = cones[key]
|
||||||
if (arrow) {
|
if (!shaft || !cone) continue
|
||||||
arrow.position.copy(offset).divideScalar(currentScale)
|
|
||||||
}
|
// Total world-space length from center to arrow tip
|
||||||
|
const worldLength = boundsHalfSizes[key] + arrowLength
|
||||||
|
// Convert to local space (compensating for group scale)
|
||||||
|
const localLength = worldLength / currentScale
|
||||||
|
|
||||||
|
// Shaft length = total length - cone head length
|
||||||
|
const shaftLength = localLength - arrowHeadLength
|
||||||
|
|
||||||
|
// Update shaft: scale Y to desired length, position at midpoint
|
||||||
|
shaft.scale.y = Math.max(0.01, shaftLength)
|
||||||
|
shaft.position.y = shaftLength / 2
|
||||||
|
|
||||||
|
// Position cone at end of shaft
|
||||||
|
cone.position.y = shaftLength + arrowHeadLength / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,8 +216,8 @@ export function createTranslateGizmo(options: TranslateGizmoOptions = {}): Trans
|
||||||
currentScale = scale
|
currentScale = scale
|
||||||
group.scale.setScalar(scale)
|
group.scale.setScalar(scale)
|
||||||
|
|
||||||
// Update arrow positions to compensate for new scale
|
// Update arrow lengths to compensate for new scale
|
||||||
updateArrowPositions()
|
updateArrowLengths()
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispose(): void {
|
function dispose(): void {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue