feat: Implement keyboard navigation for desktop layout items with occlusion and Z-order awareness.
This commit is contained in:
@@ -143,11 +143,82 @@ export class LayoutCard implements LayoutCardState {
|
||||
];
|
||||
|
||||
return corners.map(p => ({
|
||||
x: (p.x * cos - p.y * sin) + this.x,
|
||||
y: (p.x * sin + p.y * cos) + this.y
|
||||
x: (p.x * cos - p.y * sin) + this._centerX,
|
||||
y: (p.x * sin + p.y * cos) + this._centerY
|
||||
}));
|
||||
}
|
||||
|
||||
containsPoint(x: number, y: number): boolean {
|
||||
// Translate point to local space relative to center
|
||||
const dx = x - this._centerX;
|
||||
const dy = y - this._centerY;
|
||||
|
||||
// Rotate point by -rotation to align with AABB
|
||||
const rad = (-this.rotation * Math.PI) / 180;
|
||||
const cos = Math.cos(rad);
|
||||
const sin = Math.sin(rad);
|
||||
|
||||
const localX = dx * cos - dy * sin;
|
||||
const localY = dx * sin + dy * cos;
|
||||
|
||||
const hw = this.width / 2;
|
||||
const hh = this.height / 2;
|
||||
|
||||
return localX >= -hw && localX <= hw && localY >= -hh && localY <= hh;
|
||||
}
|
||||
|
||||
getVisibleFraction(): number {
|
||||
const samplesX = 4;
|
||||
const samplesY = 4;
|
||||
const totalSamples = samplesX * samplesY;
|
||||
let visibleSamples = 0;
|
||||
|
||||
// Get potential occluders (higher Z-index)
|
||||
const occluders = Array.from(this.store.items.values()).filter(other =>
|
||||
other.id !== this.id && other.z > this.z
|
||||
);
|
||||
|
||||
if (occluders.length === 0) return 1.0;
|
||||
|
||||
const rad = (this.rotation * Math.PI) / 180;
|
||||
const cos = Math.cos(rad);
|
||||
const sin = Math.sin(rad);
|
||||
const hw = this.width / 2;
|
||||
const hh = this.height / 2;
|
||||
|
||||
// Sample points across the card surface
|
||||
for (let i = 0; i < samplesX; i++) {
|
||||
for (let j = 0; j < samplesY; j++) {
|
||||
// Normalized coordinates [-1, 1]
|
||||
const nx = (i / (samplesX - 1)) * 2 - 1;
|
||||
const ny = (j / (samplesY - 1)) * 2 - 1;
|
||||
|
||||
// Local coordinates
|
||||
const lx = nx * hw * 0.9; // 0.9 to avoid edge cases
|
||||
const ly = ny * hh * 0.9;
|
||||
|
||||
// World coordinates
|
||||
const wx = (lx * cos - ly * sin) + this._centerX;
|
||||
const wy = (lx * sin + ly * cos) + this._centerY;
|
||||
|
||||
// Check occlusion
|
||||
let isOccluded = false;
|
||||
for (const occluder of occluders) {
|
||||
if (occluder.containsPoint(wx, wy)) {
|
||||
isOccluded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isOccluded) {
|
||||
visibleSamples++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return visibleSamples / totalSamples;
|
||||
}
|
||||
|
||||
private getAxes(): { x: number; y: number }[] {
|
||||
const rad = (this.rotation * Math.PI) / 180;
|
||||
const cos = Math.cos(rad);
|
||||
|
||||
Reference in New Issue
Block a user