Skip to content

Commit

Permalink
Implemented shape rotation using bounding box corners. Created method…
Browse files Browse the repository at this point in the history
… on shape class for retrieving corners in world space. Rotation now smoothly follows the mouse as it orbits the shape. Behold the wonders of trigonometry...
  • Loading branch information
ZainGS committed Aug 29, 2024
1 parent 5799bc5 commit 42bdddf
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 51 deletions.
4 changes: 2 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ async function webGPURendering() {
diamond.y = 0;

const redDiamond = shapeFactory.createDiamond(0,0,
1,
1,
.5,
.5,
froggyGreen,
{ r: 0, g: 0, b: 0, a: 1 },
2
Expand Down
71 changes: 45 additions & 26 deletions src/renderer/webgpu-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SceneGraph } from "../scene-graph/scene-graph";
import { WebGPURenderStrategy } from "./render-strategies/webgpu-render-strategy";
import { Node } from "../scene-graph/node";
import { InteractionService } from '../services/interaction-service';
import { mat4, vec3 } from "gl-matrix";
import { mat4, vec3, vec4 } from "gl-matrix";
import { Shape } from "../scene-graph/shapes/shape";

// src/renderer/webgpu-renderer.ts
Expand Down Expand Up @@ -99,43 +99,62 @@ export class WebGPURenderer {
}

private isMouseNearCorner(mouseX: number, mouseY: number, shape: Shape): boolean {
const corners = shape.getWorldSpaceCorners();

// Convert the mouse point to NDC space
const ndcX = (mouseX / this.canvas.width) * 2 - 1;
const ndcY = (mouseY / this.canvas.height) * -2 + 1;

// Transform mouse coordinates from canvas space to world space
const [transformedX, transformedY] = this.transformMouseCoordinates(mouseX, mouseY);
var boundingBox: { x: number; y: number; width: number; height: number } = shape.boundingBox;
// Convert the NDC mouse point to world space using the inverse of the world matrix
const mousePoint = vec4.fromValues(ndcX, ndcY, 0, 1);

// Invert the world matrix to go from screen space back to world space
const inverseWorldMatrix = mat4.create();
mat4.invert(inverseWorldMatrix, this.interactionService.getWorldMatrix());
vec4.transformMat4(mousePoint, mousePoint, inverseWorldMatrix);

// Invert the local matrix to go from world space to the shape's local space
const inverseLocalMatrix = mat4.create();
const success = mat4.invert(inverseLocalMatrix, shape.localMatrix);
if (!success) {
// console.error("Matrix inversion failed");
return false;
}

const point = vec3.fromValues(transformedX, transformedY, 0);
vec3.transformMat4(point, point, inverseLocalMatrix);
mat4.invert(inverseLocalMatrix, shape.localMatrix);
vec4.transformMat4(mousePoint, mousePoint, inverseLocalMatrix);

const corners = [
{ x: boundingBox.x, y: boundingBox.y }, // Bottom-left
{ x: boundingBox.x + boundingBox.width, y: boundingBox.y }, // Bottom-right
{ x: boundingBox.x, y: boundingBox.y + boundingBox.height }, // Top-left
{ x: boundingBox.x + boundingBox.width, y: boundingBox.y + boundingBox.height }, // Bottom-right
];

const threshold = .12; // Adjust this value as needed
const threshold = 0.035; // Adjust the threshold based on your needs

return corners.some(corner => {
const distance = Math.sqrt(Math.pow(point[0] - corner.x, 2) + Math.pow(point[1] - corner.y, 2));
const distance = Math.sqrt(
Math.pow(mousePoint[0] - corner[0], 2) +
Math.pow(mousePoint[1] - corner[1], 2)
);
return distance <= threshold;
});
}

private calculateMouseAngle(mouseX: number, mouseY: number, shape: Shape): number {
if (shape) {

// Convert the mouse coordinates from screen space to NDC space
let ndcX = (mouseX / this.canvas.width) * 2 - 1;
let ndcY = (mouseY / this.canvas.height) * -2 + 1;

// Create a vec4 for the mouse point in NDC space
const mousePoint = vec4.fromValues(ndcX, ndcY, 0, 1);

// Invert the world matrix to transform the mouse point to world space
const inverseWorldMatrix = mat4.create();
mat4.invert(inverseWorldMatrix, this.interactionService.getWorldMatrix());
vec4.transformMat4(mousePoint, mousePoint, inverseWorldMatrix);

// The shape's center should be transformed similarly if needed,
// but in this case, we assume it's in local space(?), so we directly use it.
const centerX = shape.x;
const centerY = shape.y;

// Calculate the angle using atan2 directly, without inverting the Y-axis
return Math.atan2(mouseY - centerY, mouseX - centerX);
// For some reason 0.05 aligns the rotation with the mouse. It's a mystery...
var sensitivity = 0.05;
// Calculate the angle using atan2, ensuring both points are in the same space
const angle = sensitivity * Math.atan2(mousePoint[1] - centerY, mousePoint[0] - centerX);

return angle;
}
return 0;
}
Expand Down Expand Up @@ -268,8 +287,8 @@ export class WebGPURenderer {

// Get current mouse position in screen space
const rect = this.canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;

// Convert mouse position to model space
const [modelX, modelY] = this.transformMouseCoordinates(x, y);
Expand Down Expand Up @@ -861,7 +880,7 @@ export class WebGPURenderer {
const fragmentShaderCode = `
@fragment
fn main_fragment() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 1.0, 1.0, 1.0); // Blue color
return vec4<f32>(0.5, 0.1, 1.0, 1.0); // Blue color
}
`;

Expand Down
4 changes: 1 addition & 3 deletions src/scene-graph/shapes/circle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { mat4, vec3, vec4 } from 'gl-matrix';

export class Circle extends Shape {

private _interactionService: InteractionService;

constructor(renderStrategy: RenderStrategy,
x: number,
y:number,
Expand All @@ -18,7 +16,7 @@ export class Circle extends Shape {
strokeWidth: number = 1,
interactionService: InteractionService) {

super(renderStrategy, fillColor, strokeColor, strokeWidth);
super(renderStrategy, fillColor, strokeColor, strokeWidth, interactionService);
this._interactionService = interactionService;
this.width = radius;
this.height = radius;
Expand Down
4 changes: 1 addition & 3 deletions src/scene-graph/shapes/diamond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { mat4, vec3, vec4 } from 'gl-matrix';

export class Diamond extends Shape {

private _interactionService: InteractionService;

constructor(renderStrategy: RenderStrategy,
x: number,
y: number,
Expand All @@ -19,7 +17,7 @@ export class Diamond extends Shape {
strokeWidth: number = 1,
interactionService: InteractionService) {

super(renderStrategy, fillColor, strokeColor, strokeWidth);
super(renderStrategy, fillColor, strokeColor, strokeWidth, interactionService);
this._interactionService = interactionService;
this.width = width;
this.height = height;
Expand Down
4 changes: 1 addition & 3 deletions src/scene-graph/shapes/inverted-triangle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { Shape } from './shape';

export class InvertedTriangle extends Shape {

private _interactionService: InteractionService;

constructor(renderStrategy: RenderStrategy,
x: number,
y: number,
Expand All @@ -19,7 +17,7 @@ export class InvertedTriangle extends Shape {
strokeWidth: number = 1,
interactionService: InteractionService) {

super(renderStrategy, fillColor, strokeColor, strokeWidth);
super(renderStrategy, fillColor, strokeColor, strokeWidth, interactionService);
this.width = width;
this.height = height;
this._interactionService = interactionService;
Expand Down
6 changes: 2 additions & 4 deletions src/scene-graph/shapes/polygon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { Shape } from './shape';

export class Polygon extends Shape {
private _points: { x: number; y: number }[];
private _interactionService: InteractionService;


constructor(
renderStrategy: RenderStrategy,
points: { x: number; y: number }[],
Expand All @@ -18,9 +17,8 @@ export class Polygon extends Shape {
strokeWidth: number = 1,
interactionService: InteractionService
) {
super(renderStrategy, fillColor, strokeColor, strokeWidth);
super(renderStrategy, fillColor, strokeColor, strokeWidth, interactionService);
this._points = points;
this._interactionService = interactionService;
this.calculateBoundingBox(); // Calculate initial bounding box
}

Expand Down
5 changes: 1 addition & 4 deletions src/scene-graph/shapes/rectangle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { Shape } from './shape';

export class Rectangle extends Shape {

private _interactionService: InteractionService;

constructor(renderStrategy: RenderStrategy,
x: number,
y: number,
Expand All @@ -20,8 +18,7 @@ export class Rectangle extends Shape {
strokeColor: RGBA = {r:0,g:0,b:0,a:1},
strokeWidth: number = 1,
interactionService: InteractionService) {
super(renderStrategy, fillColor, strokeColor, strokeWidth);
this._interactionService = interactionService;
super(renderStrategy, fillColor, strokeColor, strokeWidth, interactionService);
this.width = width;
this.height = height;
this.x = x;
Expand Down
20 changes: 17 additions & 3 deletions src/scene-graph/shapes/shape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { mat4, vec4 } from 'gl-matrix';
import { RenderStrategy } from '../../renderer/render-strategies/render-strategy';
import { RGBA } from '../../types/rgba';
import { Node } from '../node';
import { InteractionService } from '../../services/interaction-service';

export abstract class Shape extends Node {

Expand All @@ -13,7 +14,7 @@ export abstract class Shape extends Node {
protected _strokeWidth: number;
protected _boundingBox: { x: number; y: number; width: number; height: number };
protected _previousBoundingBox: { x: number; y: number; width: number; height: number };

protected _interactionService: InteractionService;
protected _isSelected: boolean = false;

get width() {
Expand Down Expand Up @@ -56,8 +57,10 @@ export abstract class Shape extends Node {
constructor(renderStrategy: RenderStrategy,
fillColor: RGBA = {r: 0, g: 0, b: 0, a: 0},
strokeColor: RGBA = {r: 0, g: 0, b: 0, a: 0},
strokeWidth: number = 1) {
strokeWidth: number = 1,
interactionService: InteractionService) {
super(renderStrategy);
this._interactionService = interactionService;
this._fillColor = fillColor;
this._strokeColor = strokeColor;
this._strokeWidth = strokeWidth;
Expand Down Expand Up @@ -191,5 +194,16 @@ export abstract class Shape extends Node {
public resetDirtyFlag() {
this._isDirty = false;
}


public getWorldSpaceCorners(): [vec4, vec4, vec4, vec4] {
// Define the four corners of the bounding box rectangle in local space
const corners: vec4[] = [
vec4.fromValues(-this.width / 2, -this.height / 2, 0, 1), // Bottom-left
vec4.fromValues(this.width / 2, -this.height / 2, 0, 1), // Bottom-right
vec4.fromValues(this.width / 2, this.height / 2, 0, 1), // Top-right
vec4.fromValues(-this.width / 2, this.height / 2, 0, 1), // Top-left
];

return corners as [vec4, vec4, vec4, vec4];
}
}
4 changes: 1 addition & 3 deletions src/scene-graph/shapes/triangle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { Shape } from './shape';

export class Triangle extends Shape {

private _interactionService: InteractionService;

constructor(renderStrategy: RenderStrategy,
x: number,
y: number,
Expand All @@ -19,7 +17,7 @@ export class Triangle extends Shape {
strokeWidth: number = 1,
interactionService: InteractionService) {

super(renderStrategy, fillColor, strokeColor, strokeWidth);
super(renderStrategy, fillColor, strokeColor, strokeWidth, interactionService);
this.width = width;
this.height = height;
this.x = x;
Expand Down

0 comments on commit 42bdddf

Please sign in to comment.