rotation in RotatedDOMRect
This commit is contained in:
parent
9b22f838af
commit
14d94f83e7
|
|
@ -38,12 +38,13 @@ export class DistanceField extends HTMLElement {
|
||||||
this.initWebGL();
|
this.initWebGL();
|
||||||
this.initShaders();
|
this.initShaders();
|
||||||
this.initPingPongTextures();
|
this.initPingPongTextures();
|
||||||
this.initSeedPointRendering();
|
this.populateSeedPoints();
|
||||||
|
|
||||||
window.addEventListener('resize', this.handleResize);
|
window.addEventListener('resize', this.handleResize);
|
||||||
this.shapes.forEach((geometry) => {
|
this.shapes.forEach((geometry) => {
|
||||||
geometry.addEventListener('move', this.handleGeometryUpdate);
|
geometry.addEventListener('move', this.handleGeometryUpdate);
|
||||||
geometry.addEventListener('resize', this.handleGeometryUpdate);
|
geometry.addEventListener('resize', this.handleGeometryUpdate);
|
||||||
|
geometry.addEventListener('rotate', this.handleGeometryUpdate);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,6 +53,7 @@ export class DistanceField extends HTMLElement {
|
||||||
this.shapes.forEach((geometry) => {
|
this.shapes.forEach((geometry) => {
|
||||||
geometry.removeEventListener('move', this.handleGeometryUpdate);
|
geometry.removeEventListener('move', this.handleGeometryUpdate);
|
||||||
geometry.removeEventListener('resize', this.handleGeometryUpdate);
|
geometry.removeEventListener('resize', this.handleGeometryUpdate);
|
||||||
|
geometry.removeEventListener('rotate', this.handleGeometryUpdate);
|
||||||
});
|
});
|
||||||
this.cleanupWebGLResources();
|
this.cleanupWebGLResources();
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +73,8 @@ export class DistanceField extends HTMLElement {
|
||||||
* Handles updates to geometry elements by re-initializing seed points and rerunning the JFA.
|
* Handles updates to geometry elements by re-initializing seed points and rerunning the JFA.
|
||||||
*/
|
*/
|
||||||
private handleGeometryUpdate = () => {
|
private handleGeometryUpdate = () => {
|
||||||
this.initSeedPointRendering();
|
console.log('handleGeometryUpdate');
|
||||||
|
this.populateSeedPoints();
|
||||||
this.runJumpFloodingAlgorithm();
|
this.runJumpFloodingAlgorithm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -140,7 +143,7 @@ export class DistanceField extends HTMLElement {
|
||||||
* Initializes rendering of seed points (shapes) into a texture.
|
* Initializes rendering of seed points (shapes) into a texture.
|
||||||
* Seed points are the starting locations for distance calculations.
|
* Seed points are the starting locations for distance calculations.
|
||||||
*/
|
*/
|
||||||
private initSeedPointRendering() {
|
private populateSeedPoints() {
|
||||||
const gl = this.glContext;
|
const gl = this.glContext;
|
||||||
const positions: number[] = [];
|
const positions: number[] = [];
|
||||||
|
|
||||||
|
|
@ -150,12 +153,39 @@ export class DistanceField extends HTMLElement {
|
||||||
// Collect positions and assign unique IDs to all shapes
|
// Collect positions and assign unique IDs to all shapes
|
||||||
this.shapes.forEach((geometry, index) => {
|
this.shapes.forEach((geometry, index) => {
|
||||||
const rect = geometry.getClientRect();
|
const rect = geometry.getClientRect();
|
||||||
|
const rotation = (geometry.rotation * Math.PI) / 180; // Convert to radians
|
||||||
|
|
||||||
// Convert DOM coordinates to Normalized Device Coordinates (NDC)
|
// Calculate the center of the rectangle
|
||||||
const x1 = (rect.left / windowWidth) * 2 - 1;
|
const centerX = (rect.left + rect.right) / 2;
|
||||||
const y1 = -((rect.top / windowHeight) * 2 - 1);
|
const centerY = (rect.top + rect.bottom) / 2;
|
||||||
const x2 = (rect.right / windowWidth) * 2 - 1;
|
|
||||||
const y2 = -((rect.bottom / windowHeight) * 2 - 1);
|
// Function to rotate a point around the center
|
||||||
|
const rotatePoint = (x: number, y: number) => {
|
||||||
|
const dx = x - centerX;
|
||||||
|
const dy = y - centerY;
|
||||||
|
const cos = Math.cos(rotation);
|
||||||
|
const sin = Math.sin(rotation);
|
||||||
|
return {
|
||||||
|
x: centerX + dx * cos - dy * sin,
|
||||||
|
y: centerY + dx * sin + dy * cos,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rotate each corner of the rectangle
|
||||||
|
const topLeft = rotatePoint(rect.left, rect.top);
|
||||||
|
const topRight = rotatePoint(rect.right, rect.top);
|
||||||
|
const bottomLeft = rotatePoint(rect.left, rect.bottom);
|
||||||
|
const bottomRight = rotatePoint(rect.right, rect.bottom);
|
||||||
|
|
||||||
|
// Convert rotated coordinates to NDC
|
||||||
|
const x1 = (topLeft.x / windowWidth) * 2 - 1;
|
||||||
|
const y1 = -((topLeft.y / windowHeight) * 2 - 1);
|
||||||
|
const x2 = (topRight.x / windowWidth) * 2 - 1;
|
||||||
|
const y2 = -((topRight.y / windowHeight) * 2 - 1);
|
||||||
|
const x3 = (bottomLeft.x / windowWidth) * 2 - 1;
|
||||||
|
const y3 = -((bottomLeft.y / windowHeight) * 2 - 1);
|
||||||
|
const x4 = (bottomRight.x / windowWidth) * 2 - 1;
|
||||||
|
const y4 = -((bottomRight.y / windowHeight) * 2 - 1);
|
||||||
|
|
||||||
const shapeID = index + 1; // Avoid zero to prevent hash function issues
|
const shapeID = index + 1; // Avoid zero to prevent hash function issues
|
||||||
|
|
||||||
|
|
@ -165,20 +195,20 @@ export class DistanceField extends HTMLElement {
|
||||||
y1,
|
y1,
|
||||||
shapeID,
|
shapeID,
|
||||||
x2,
|
x2,
|
||||||
y1,
|
|
||||||
shapeID,
|
|
||||||
x1,
|
|
||||||
y2,
|
y2,
|
||||||
shapeID,
|
shapeID,
|
||||||
|
x3,
|
||||||
|
y3,
|
||||||
|
shapeID,
|
||||||
|
|
||||||
x1,
|
x3,
|
||||||
y2,
|
y3,
|
||||||
shapeID,
|
|
||||||
x2,
|
|
||||||
y1,
|
|
||||||
shapeID,
|
shapeID,
|
||||||
x2,
|
x2,
|
||||||
y2,
|
y2,
|
||||||
|
shapeID,
|
||||||
|
x4,
|
||||||
|
y4,
|
||||||
shapeID
|
shapeID
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -376,7 +406,7 @@ export class DistanceField extends HTMLElement {
|
||||||
this.initPingPongTextures();
|
this.initPingPongTextures();
|
||||||
|
|
||||||
// Re-initialize seed point rendering to update positions
|
// Re-initialize seed point rendering to update positions
|
||||||
this.initSeedPointRendering();
|
this.populateSeedPoints();
|
||||||
|
|
||||||
// Rerun the Jump Flooding Algorithm with the new sizes
|
// Rerun the Jump Flooding Algorithm with the new sizes
|
||||||
this.runJumpFloodingAlgorithm();
|
this.runJumpFloodingAlgorithm();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ const resizeObserver = new ResizeObserverManager();
|
||||||
|
|
||||||
export type Shape = 'rectangle' | 'circle' | 'triangle';
|
export type Shape = 'rectangle' | 'circle' | 'triangle';
|
||||||
|
|
||||||
|
type RotatedDOMRect = DOMRect & { rotation: number };
|
||||||
|
|
||||||
export type MoveEventDetail = { movementX: number; movementY: number };
|
export type MoveEventDetail = { movementX: number; movementY: number };
|
||||||
|
|
||||||
export class MoveEvent extends CustomEvent<MoveEventDetail> {
|
export class MoveEvent extends CustomEvent<MoveEventDetail> {
|
||||||
|
|
@ -239,15 +241,18 @@ export class FolkShape extends HTMLElement {
|
||||||
|
|
||||||
#initialRotation = 0;
|
#initialRotation = 0;
|
||||||
#startAngle = 0;
|
#startAngle = 0;
|
||||||
#previousRotate = 0;
|
#previousRotation = 0;
|
||||||
#rotate = Number(this.getAttribute('rotate')) || 0;
|
|
||||||
get rotate(): number {
|
// TODO: consider using radians instead of degrees
|
||||||
return this.#rotate;
|
#rotation = Number(this.getAttribute('rotate')) || 0;
|
||||||
|
|
||||||
|
get rotation(): number {
|
||||||
|
return this.#rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
set rotate(rotate: number) {
|
set rotation(rotation: number) {
|
||||||
this.#previousRotate = this.#rotate;
|
this.#previousRotation = this.#rotation;
|
||||||
this.#rotate = rotate;
|
this.#rotation = rotation;
|
||||||
this.#requestUpdate('rotate');
|
this.#requestUpdate('rotate');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,8 +284,8 @@ export class FolkShape extends HTMLElement {
|
||||||
this.#update(new Set(['type', 'x', 'y', 'height', 'width', 'rotate']));
|
this.#update(new Set(['type', 'x', 'y', 'height', 'width', 'rotate']));
|
||||||
}
|
}
|
||||||
|
|
||||||
getClientRect(): DOMRect {
|
getClientRect(): RotatedDOMRect {
|
||||||
const { x, y, width, height } = this;
|
const { x, y, width, height, rotation } = this;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x,
|
x,
|
||||||
|
|
@ -291,6 +296,7 @@ export class FolkShape extends HTMLElement {
|
||||||
top: y,
|
top: y,
|
||||||
right: x + width,
|
right: x + width,
|
||||||
bottom: y + height,
|
bottom: y + height,
|
||||||
|
rotation,
|
||||||
toJSON: undefined as any,
|
toJSON: undefined as any,
|
||||||
};
|
};
|
||||||
// return DOMRectReadOnly.fromRect({ x: this.x, y: this.y, width: this.width, height: this.height });
|
// return DOMRectReadOnly.fromRect({ x: this.x, y: this.y, width: this.width, height: this.height });
|
||||||
|
|
@ -320,7 +326,7 @@ export class FolkShape extends HTMLElement {
|
||||||
// Might be an argument for making elements dumber (i.e. not have them manage their own state) and do this from the outside.
|
// Might be an argument for making elements dumber (i.e. not have them manage their own state) and do this from the outside.
|
||||||
// But we also want to preserve the self-sufficient nature of elements' behaviour...
|
// But we also want to preserve the self-sufficient nature of elements' behaviour...
|
||||||
// Maybe some kind of shared utility, used by both the element and the outside environment?
|
// Maybe some kind of shared utility, used by both the element and the outside environment?
|
||||||
this.#initialRotation = this.#rotate;
|
this.#initialRotation = this.#rotation;
|
||||||
const centerX = this.#x + this.width / 2;
|
const centerX = this.#x + this.width / 2;
|
||||||
const centerY = this.#y + this.height / 2;
|
const centerY = this.#y + this.height / 2;
|
||||||
this.#startAngle = Math.atan2(event.clientY - centerY, event.clientX - centerX);
|
this.#startAngle = Math.atan2(event.clientY - centerY, event.clientX - centerX);
|
||||||
|
|
@ -383,7 +389,7 @@ export class FolkShape extends HTMLElement {
|
||||||
const currentAngle = Math.atan2(event.clientY - centerY, event.clientX - centerX);
|
const currentAngle = Math.atan2(event.clientY - centerY, event.clientX - centerX);
|
||||||
|
|
||||||
const deltaAngle = currentAngle - this.#startAngle;
|
const deltaAngle = currentAngle - this.#startAngle;
|
||||||
this.rotate = this.#initialRotation + (deltaAngle * 180) / Math.PI;
|
this.rotation = this.#initialRotation + (deltaAngle * 180) / Math.PI;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -473,14 +479,14 @@ export class FolkShape extends HTMLElement {
|
||||||
|
|
||||||
if (updatedProperties.has('rotate')) {
|
if (updatedProperties.has('rotate')) {
|
||||||
// Although the change in resize isn't useful inside this component, the outside world might find it helpful to calculate acceleration and other physics
|
// Although the change in resize isn't useful inside this component, the outside world might find it helpful to calculate acceleration and other physics
|
||||||
const notCancelled = this.dispatchEvent(new RotateEvent({ rotate: this.#rotate - this.#previousRotate }));
|
const notCancelled = this.dispatchEvent(new RotateEvent({ rotate: this.#rotation - this.#previousRotation }));
|
||||||
|
|
||||||
if (notCancelled) {
|
if (notCancelled) {
|
||||||
if (updatedProperties.has('rotate')) {
|
if (updatedProperties.has('rotate')) {
|
||||||
this.style.rotate = `${this.#rotate}deg`;
|
this.style.rotate = `${this.#rotation}deg`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.#rotate = this.#previousRotate;
|
this.#rotation = this.#previousRotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue