From df319db8833c7b8e34138ecd8f7314b1d8940cf1 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Fri, 20 Dec 2024 21:12:35 +0530 Subject: [PATCH 01/44] First draft for ee workflow --- .eslintrc.json | 3 +- ee-workflow/index.html | 13 ++ ee-workflow/index.js | 385 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 ee-workflow/index.html create mode 100644 ee-workflow/index.js diff --git a/.eslintrc.json b/.eslintrc.json index d2e5d1e4d..aa3ff2e12 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -44,7 +44,8 @@ "out/**", "data/**", "assets/**/*.js", - "coverage/**" + "coverage/**", + "ee-workflow/**" ], "rules": { "array-callback-return": "error", diff --git a/ee-workflow/index.html b/ee-workflow/index.html new file mode 100644 index 000000000..6fb3b0014 --- /dev/null +++ b/ee-workflow/index.html @@ -0,0 +1,13 @@ + + + + + + + + +
+
+ + + diff --git a/ee-workflow/index.js b/ee-workflow/index.js new file mode 100644 index 000000000..3082f0ccf --- /dev/null +++ b/ee-workflow/index.js @@ -0,0 +1,385 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const main = { + stepsQueue: [], + groupStepsQueue: [[]], + instantQueue: [], + reDrawAll: () => { + if (main.pause) { + return; + } + + clear(); + background(220); + main.stepsQueue = []; + main.groupStepsQueue = [[]]; + main.instantQueue = []; + main.storage.forEach((object) => { + if (object instanceof Box) { + object.color = map( + noise(object.x * 0.01, object.y * 0.01, frameCount * 0.01), + 0, + 1, + 0, + 255 + ); + } + + main.instantQueue.push(object); + }); + }, + delay: 50, + pause: false, + storage: [], + groupsStorage: [], +}; + +/** + * + */ +function setup() { + createCanvas(1600, 1600); + background(220); +} + +/** + * + */ +function draw() { + if (main.pause) { + return; + } + + if (frameCount % main.delay === 0) { + const firstObject = main.stepsQueue.shift(); + if (firstObject === undefined) { + return; + } + + console.log(firstObject); + + firstObject.draw(); + if (!firstObject.throw) { + main.storage.push(firstObject); + } + + const group = main.groupStepsQueue.shift(); + if (group !== undefined) { + if (group instanceof Group) { + group.draw(); + main.groupsStorage.push(group); + } else { + group.forEach((object) => { + object.draw(); + main.storage.push(object); + }); + } + } + } + + // can be replaced with requestAnimationFrame, just update the redrawAll function to pass callback which renderes UI, and also create instantRender function which will render the instantQueue when called. + if (main.instantQueue.length > 0) { + main.instantQueue.forEach((object) => { + object.draw(); + }); + main.instantQueue = []; + } +} + +/** + * + */ +function mouseClicked() { + // navigate through the timeline and change color of clicked circle object to black + // and then push to instantQueue + + const clickedObject = main.storage.find((object) => { + if (object instanceof Circle) { + return ( + mouseX > object.x - object.radius && + mouseX < object.x + object.radius && + mouseY > object.y - object.radius && + mouseY < object.y + object.radius + ); + } else if (object instanceof Box) { + return ( + mouseX > object.x && + mouseX < object.x + object.width && + mouseY > object.y && + mouseY < object.y + object.height + ); + } + }); + + if (clickedObject instanceof Circle) { + console.log(clickedObject); + if (clickedObject.gid) { + const group = main.groupsStorage.find( + (group) => group.groupId === clickedObject.gid + ); + console.log(group); + group.onMouseClick(); + return; + } + + clickedObject.color = 'black'; + main.instantQueue.push(clickedObject); + } else if (clickedObject instanceof Box) { + clickedObject.sideEffectCallback?.(); + } +} + +/** + * + */ +function mouseMoved() { + // highlight the object + main.storage.forEach((object) => { + if (object instanceof Circle) { + if ( + mouseX > object.x - object.radius && + mouseX < object.x + object.radius && + mouseY > object.y - object.radius && + mouseY < object.y + object.radius + ) { + object.color = 'gray'; + main.instantQueue.push(object); + } else if (object.color === 'gray') { + object.color = 'black'; + main.instantQueue.push(object); + } + } + }); +} + +class Box { + constructor( + x = 0, + y = 0, + width = 50, + height = 50, + color = 'black', + sideEffectCallback = null + ) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.color = color; + this.throw = false; + this.sideEffectCallback = sideEffectCallback; + } + + draw() { + fill(this.color); + rect(this.x, this.y, this.width, this.height); + } +} + +class Circle { + constructor(x = 0, y = 0, radius = 50, color = 'black') { + this.x = x; + this.y = y; + this.radius = radius; + this.color = color; + } + + draw() { + fill(this.color); + circle(this.x, this.y, this.radius); + } +} + +class Line { + constructor(x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 'black') { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + this.color = color; + } + + draw() { + stroke(this.color); + line(this.x1, this.y1, this.x2, this.y2); + } +} + +class RippleEffect { + constructor(x = 0, y = 0) { + this.x = x; + this.y = y; + this.throw = false; + this.times = 0; + this.ripples = []; + + for (let i = 0; i < 10; i++) { + this.ripples.push({ + radius: 0, + baseSpeed: 1 + i * 0.5, + }); + } + } + + draw() { + this.create(this.x, this.y); + this.throw = true; + this.times++; + + if (this.times <= 10) { + console.log('times', this.times); + main.stepsQueue.push(this); + } else { + main.reDrawAll(); + } + } + + create(rippleX, rippleY) { + // Calculate the area to clear + const { ripples, numRipples, speed, maxRadius } = { + ripples: this.ripples, + numRipples: 3, + speed: 1, + maxRadius: 200, + }; + const clearWidth = maxRadius * 2 + (numRipples - 1) * 40; + const clearHeight = maxRadius * 1.5; + + push(); + // Clear only the area used by the ripples + fill(220); + noStroke(); + rect( + rippleX - 1, + rippleY - clearHeight / 2 - 200, + clearWidth, + clearHeight + 400 + ); + let allComplete = true; + translate(rippleX, rippleY); + + for (let i = 0; i < ripples.length; i++) { + const ripple = ripples[i]; + if (ripple.radius < maxRadius) { + ripple.radius += ripple.baseSpeed * speed; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + allComplete = false; + } else { + ripple.radius = maxRadius; // Ensure the radius doesn't exceed maxRadius + } + + // Black color with fading opacity + const opacity = map(ripple.radius, 0, maxRadius, 255, 0); + stroke(0, opacity); + noFill(); + + // Increased spacing between ripples + const spacing = 40; + arc( + 0, + 0, + (ripple.radius + i * spacing) * 2, + (ripple.radius + i * spacing) * 2, + -HALF_PI, + HALF_PI + ); + } + + pop(); // Restore the original transformation state + } +} + +class Group { + constructor(objects, id) { + this.objects = objects; + this.groupId = id; + } + + draw() { + this.objects.forEach((object) => { + object.gid = this.groupId; + object.draw(); + // main.storage.push(object); + }); + } + + onMouseClick() { + this.objects.forEach((object) => { + object.color = 'black'; + main.instantQueue.push(object); + }); + } +} + +// We will create a timeline, where circle signify the circular nodes, connected with lines. +// the timeline should be horizontal and line should not overlap with the circle. + +const timeline = [ + new Circle(100, 100, 50, 'red'), + new Line(125, 100, 175, 100, 'black'), + new Circle(200, 100, 50, 'blue'), + new Line(225, 100, 275, 100, 'black'), + new Circle(300, 100, 50, 'green'), + new Line(325, 100, 375, 100, 'black'), + new Circle(400, 100, 50, 'yellow'), + new Line(425, 100, 475, 100, 'black'), + new Circle(500, 100, 50, 'purple'), +]; + +timeline.forEach((object, index) => { + main.stepsQueue.push(object); +}); + +const button = new Box(100, 200, 100, 50, 'black'); +button.sideEffectCallback = () => { + main.reDrawAll(); +}; +main.stepsQueue.push(button); + +const pauseButton = new Box(300, 200, 100, 50, 'red'); +pauseButton.sideEffectCallback = () => { + main.pause = !main.pause; + // remove fourth element from the timeline and redraw + // main.storage.splice(3, 1); + // main.reDrawAll(); +}; +main.stepsQueue.push(pauseButton); + +main.stepsQueue.push(new RippleEffect(600, 600)); + +const group1 = [ + new Circle(100, 300, 50, 'red'), + new Line(125, 300, 175, 300, 'black'), + new Circle(200, 300, 50, 'blue'), + new Line(225, 300, 275, 300, 'black'), + new Circle(300, 300, 50, 'green'), + new Line(325, 300, 375, 300, 'black'), + new Circle(400, 300, 50, 'yellow'), +]; + +const group2 = [ + new Circle(100, 400, 50, 'red'), + new Line(125, 400, 175, 400, 'black'), + new Circle(200, 400, 50, 'blue'), + new Line(225, 400, 275, 400, 'black'), + new Circle(300, 400, 50, 'green'), + new Line(325, 400, 375, 400, 'black'), + new Circle(400, 400, 50, 'yellow'), +]; + +main.groupStepsQueue.push(group1); +main.groupStepsQueue.push(new Group(group2, 2)); From ccf92d876859334086f3b245eb6890ebdd023f67 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 23 Dec 2024 12:42:49 +0530 Subject: [PATCH 02/44] Setup vite, break code into classes, main, figure, and create basic runner --- .eslintrc.json | 3 +- ee-workflow/.gitignore | 24 + ee-workflow/index.html | 6 +- ee-workflow/index.js | 385 --------- ee-workflow/package-lock.json | 903 ++++++++++++++++++++ ee-workflow/package.json | 19 + ee-workflow/public/vite.svg | 1 + ee-workflow/src/components/figure/box.ts | 56 ++ ee-workflow/src/components/figure/circle.ts | 53 ++ ee-workflow/src/components/figure/index.ts | 31 + ee-workflow/src/components/figure/line.ts | 56 ++ ee-workflow/src/main.ts | 94 ++ ee-workflow/src/typescript.svg | 1 + ee-workflow/src/vite-env.d.ts | 15 + ee-workflow/tsconfig.json | 23 + 15 files changed, 1279 insertions(+), 391 deletions(-) create mode 100644 ee-workflow/.gitignore delete mode 100644 ee-workflow/index.js create mode 100644 ee-workflow/package-lock.json create mode 100644 ee-workflow/package.json create mode 100644 ee-workflow/public/vite.svg create mode 100644 ee-workflow/src/components/figure/box.ts create mode 100644 ee-workflow/src/components/figure/circle.ts create mode 100644 ee-workflow/src/components/figure/index.ts create mode 100644 ee-workflow/src/components/figure/line.ts create mode 100644 ee-workflow/src/main.ts create mode 100644 ee-workflow/src/typescript.svg create mode 100644 ee-workflow/src/vite-env.d.ts create mode 100644 ee-workflow/tsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json index aa3ff2e12..d2e5d1e4d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -44,8 +44,7 @@ "out/**", "data/**", "assets/**/*.js", - "coverage/**", - "ee-workflow/**" + "coverage/**" ], "rules": { "array-callback-return": "error", diff --git a/ee-workflow/.gitignore b/ee-workflow/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/ee-workflow/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/ee-workflow/index.html b/ee-workflow/index.html index 6fb3b0014..fb5aad747 100644 --- a/ee-workflow/index.html +++ b/ee-workflow/index.html @@ -2,12 +2,10 @@ - -
-
- +
+ diff --git a/ee-workflow/index.js b/ee-workflow/index.js deleted file mode 100644 index 3082f0ccf..000000000 --- a/ee-workflow/index.js +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const main = { - stepsQueue: [], - groupStepsQueue: [[]], - instantQueue: [], - reDrawAll: () => { - if (main.pause) { - return; - } - - clear(); - background(220); - main.stepsQueue = []; - main.groupStepsQueue = [[]]; - main.instantQueue = []; - main.storage.forEach((object) => { - if (object instanceof Box) { - object.color = map( - noise(object.x * 0.01, object.y * 0.01, frameCount * 0.01), - 0, - 1, - 0, - 255 - ); - } - - main.instantQueue.push(object); - }); - }, - delay: 50, - pause: false, - storage: [], - groupsStorage: [], -}; - -/** - * - */ -function setup() { - createCanvas(1600, 1600); - background(220); -} - -/** - * - */ -function draw() { - if (main.pause) { - return; - } - - if (frameCount % main.delay === 0) { - const firstObject = main.stepsQueue.shift(); - if (firstObject === undefined) { - return; - } - - console.log(firstObject); - - firstObject.draw(); - if (!firstObject.throw) { - main.storage.push(firstObject); - } - - const group = main.groupStepsQueue.shift(); - if (group !== undefined) { - if (group instanceof Group) { - group.draw(); - main.groupsStorage.push(group); - } else { - group.forEach((object) => { - object.draw(); - main.storage.push(object); - }); - } - } - } - - // can be replaced with requestAnimationFrame, just update the redrawAll function to pass callback which renderes UI, and also create instantRender function which will render the instantQueue when called. - if (main.instantQueue.length > 0) { - main.instantQueue.forEach((object) => { - object.draw(); - }); - main.instantQueue = []; - } -} - -/** - * - */ -function mouseClicked() { - // navigate through the timeline and change color of clicked circle object to black - // and then push to instantQueue - - const clickedObject = main.storage.find((object) => { - if (object instanceof Circle) { - return ( - mouseX > object.x - object.radius && - mouseX < object.x + object.radius && - mouseY > object.y - object.radius && - mouseY < object.y + object.radius - ); - } else if (object instanceof Box) { - return ( - mouseX > object.x && - mouseX < object.x + object.width && - mouseY > object.y && - mouseY < object.y + object.height - ); - } - }); - - if (clickedObject instanceof Circle) { - console.log(clickedObject); - if (clickedObject.gid) { - const group = main.groupsStorage.find( - (group) => group.groupId === clickedObject.gid - ); - console.log(group); - group.onMouseClick(); - return; - } - - clickedObject.color = 'black'; - main.instantQueue.push(clickedObject); - } else if (clickedObject instanceof Box) { - clickedObject.sideEffectCallback?.(); - } -} - -/** - * - */ -function mouseMoved() { - // highlight the object - main.storage.forEach((object) => { - if (object instanceof Circle) { - if ( - mouseX > object.x - object.radius && - mouseX < object.x + object.radius && - mouseY > object.y - object.radius && - mouseY < object.y + object.radius - ) { - object.color = 'gray'; - main.instantQueue.push(object); - } else if (object.color === 'gray') { - object.color = 'black'; - main.instantQueue.push(object); - } - } - }); -} - -class Box { - constructor( - x = 0, - y = 0, - width = 50, - height = 50, - color = 'black', - sideEffectCallback = null - ) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - this.color = color; - this.throw = false; - this.sideEffectCallback = sideEffectCallback; - } - - draw() { - fill(this.color); - rect(this.x, this.y, this.width, this.height); - } -} - -class Circle { - constructor(x = 0, y = 0, radius = 50, color = 'black') { - this.x = x; - this.y = y; - this.radius = radius; - this.color = color; - } - - draw() { - fill(this.color); - circle(this.x, this.y, this.radius); - } -} - -class Line { - constructor(x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 'black') { - this.x1 = x1; - this.y1 = y1; - this.x2 = x2; - this.y2 = y2; - this.color = color; - } - - draw() { - stroke(this.color); - line(this.x1, this.y1, this.x2, this.y2); - } -} - -class RippleEffect { - constructor(x = 0, y = 0) { - this.x = x; - this.y = y; - this.throw = false; - this.times = 0; - this.ripples = []; - - for (let i = 0; i < 10; i++) { - this.ripples.push({ - radius: 0, - baseSpeed: 1 + i * 0.5, - }); - } - } - - draw() { - this.create(this.x, this.y); - this.throw = true; - this.times++; - - if (this.times <= 10) { - console.log('times', this.times); - main.stepsQueue.push(this); - } else { - main.reDrawAll(); - } - } - - create(rippleX, rippleY) { - // Calculate the area to clear - const { ripples, numRipples, speed, maxRadius } = { - ripples: this.ripples, - numRipples: 3, - speed: 1, - maxRadius: 200, - }; - const clearWidth = maxRadius * 2 + (numRipples - 1) * 40; - const clearHeight = maxRadius * 1.5; - - push(); - // Clear only the area used by the ripples - fill(220); - noStroke(); - rect( - rippleX - 1, - rippleY - clearHeight / 2 - 200, - clearWidth, - clearHeight + 400 - ); - let allComplete = true; - translate(rippleX, rippleY); - - for (let i = 0; i < ripples.length; i++) { - const ripple = ripples[i]; - if (ripple.radius < maxRadius) { - ripple.radius += ripple.baseSpeed * speed; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - allComplete = false; - } else { - ripple.radius = maxRadius; // Ensure the radius doesn't exceed maxRadius - } - - // Black color with fading opacity - const opacity = map(ripple.radius, 0, maxRadius, 255, 0); - stroke(0, opacity); - noFill(); - - // Increased spacing between ripples - const spacing = 40; - arc( - 0, - 0, - (ripple.radius + i * spacing) * 2, - (ripple.radius + i * spacing) * 2, - -HALF_PI, - HALF_PI - ); - } - - pop(); // Restore the original transformation state - } -} - -class Group { - constructor(objects, id) { - this.objects = objects; - this.groupId = id; - } - - draw() { - this.objects.forEach((object) => { - object.gid = this.groupId; - object.draw(); - // main.storage.push(object); - }); - } - - onMouseClick() { - this.objects.forEach((object) => { - object.color = 'black'; - main.instantQueue.push(object); - }); - } -} - -// We will create a timeline, where circle signify the circular nodes, connected with lines. -// the timeline should be horizontal and line should not overlap with the circle. - -const timeline = [ - new Circle(100, 100, 50, 'red'), - new Line(125, 100, 175, 100, 'black'), - new Circle(200, 100, 50, 'blue'), - new Line(225, 100, 275, 100, 'black'), - new Circle(300, 100, 50, 'green'), - new Line(325, 100, 375, 100, 'black'), - new Circle(400, 100, 50, 'yellow'), - new Line(425, 100, 475, 100, 'black'), - new Circle(500, 100, 50, 'purple'), -]; - -timeline.forEach((object, index) => { - main.stepsQueue.push(object); -}); - -const button = new Box(100, 200, 100, 50, 'black'); -button.sideEffectCallback = () => { - main.reDrawAll(); -}; -main.stepsQueue.push(button); - -const pauseButton = new Box(300, 200, 100, 50, 'red'); -pauseButton.sideEffectCallback = () => { - main.pause = !main.pause; - // remove fourth element from the timeline and redraw - // main.storage.splice(3, 1); - // main.reDrawAll(); -}; -main.stepsQueue.push(pauseButton); - -main.stepsQueue.push(new RippleEffect(600, 600)); - -const group1 = [ - new Circle(100, 300, 50, 'red'), - new Line(125, 300, 175, 300, 'black'), - new Circle(200, 300, 50, 'blue'), - new Line(225, 300, 275, 300, 'black'), - new Circle(300, 300, 50, 'green'), - new Line(325, 300, 375, 300, 'black'), - new Circle(400, 300, 50, 'yellow'), -]; - -const group2 = [ - new Circle(100, 400, 50, 'red'), - new Line(125, 400, 175, 400, 'black'), - new Circle(200, 400, 50, 'blue'), - new Line(225, 400, 275, 400, 'black'), - new Circle(300, 400, 50, 'green'), - new Line(325, 400, 375, 400, 'black'), - new Circle(400, 400, 50, 'yellow'), -]; - -main.groupStepsQueue.push(group1); -main.groupStepsQueue.push(new Group(group2, 2)); diff --git a/ee-workflow/package-lock.json b/ee-workflow/package-lock.json new file mode 100644 index 000000000..6ee79ddce --- /dev/null +++ b/ee-workflow/package-lock.json @@ -0,0 +1,903 @@ +{ + "name": "ee-workflow", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ee-workflow", + "version": "0.0.0", + "dependencies": { + "@types/p5": "^1.7.6", + "p5": "^1.11.2" + }, + "devDependencies": { + "typescript": "~5.6.2", + "vite": "^6.0.3" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz", + "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz", + "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz", + "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz", + "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz", + "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz", + "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz", + "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz", + "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz", + "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz", + "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz", + "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz", + "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz", + "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz", + "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz", + "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz", + "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz", + "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz", + "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz", + "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/p5": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.7.6.tgz", + "integrity": "sha512-6pLTOo0V3N5jZb5nTwjiv3lPHLK3Z/TjbhQUj8CTWXocUk1Z/f6OHTp3Pcwi1BhWnf5gqKUcyEb1gP0KIJuQgw==" + }, + "node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/p5": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/p5/-/p5-1.11.2.tgz", + "integrity": "sha512-kvtMTmxJexkbfuaVThg/1JE6iQyS8tTAqGE1UGbfApKUIzDc5hHM23hc08FV3jG0jlNS2En9kqZDb4FrJ8x+kg==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz", + "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.29.1", + "@rollup/rollup-android-arm64": "4.29.1", + "@rollup/rollup-darwin-arm64": "4.29.1", + "@rollup/rollup-darwin-x64": "4.29.1", + "@rollup/rollup-freebsd-arm64": "4.29.1", + "@rollup/rollup-freebsd-x64": "4.29.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.29.1", + "@rollup/rollup-linux-arm-musleabihf": "4.29.1", + "@rollup/rollup-linux-arm64-gnu": "4.29.1", + "@rollup/rollup-linux-arm64-musl": "4.29.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.29.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1", + "@rollup/rollup-linux-riscv64-gnu": "4.29.1", + "@rollup/rollup-linux-s390x-gnu": "4.29.1", + "@rollup/rollup-linux-x64-gnu": "4.29.1", + "@rollup/rollup-linux-x64-musl": "4.29.1", + "@rollup/rollup-win32-arm64-msvc": "4.29.1", + "@rollup/rollup-win32-ia32-msvc": "4.29.1", + "@rollup/rollup-win32-x64-msvc": "4.29.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.5.tgz", + "integrity": "sha512-akD5IAH/ID5imgue2DYhzsEwCi0/4VKY31uhMLEYJwPP4TiUp8pL5PIK+Wo7H8qT8JY9i+pVfPydcFPYD1EL7g==", + "dev": true, + "dependencies": { + "esbuild": "0.24.0", + "postcss": "^8.4.49", + "rollup": "^4.23.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/ee-workflow/package.json b/ee-workflow/package.json new file mode 100644 index 000000000..8878ab0df --- /dev/null +++ b/ee-workflow/package.json @@ -0,0 +1,19 @@ +{ + "name": "ee-workflow", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "typescript": "~5.6.2", + "vite": "^6.0.3" + }, + "dependencies": { + "@types/p5": "^1.7.6", + "p5": "^1.11.2" + } +} diff --git a/ee-workflow/public/vite.svg b/ee-workflow/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/ee-workflow/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts new file mode 100644 index 000000000..91762a092 --- /dev/null +++ b/ee-workflow/src/components/figure/box.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import p5 from 'p5'; +import Figure from '.'; +import main from '../../main'; + +export default class Box extends Figure { + width: number; + height: number; + + constructor( + p5Instance: p5, + x: number, + y: number, + color: string, + width: number, + height: number + ) { + super(); + this.p5 = p5Instance; + this.x = x; + this.y = y; + this.color = color; + this.width = width; + this.height = height; + } + + draw() { + this.p5?.fill(this.color); + this.p5?.rect(this.x, this.y, this.width, this.height); + } + + onHover() { + this.color = 'red'; // TODO: Discuss the function + main.addFigure(this, true); + } + + onClick() { + this.color = 'blue'; // TODO: Discuss the function + main.addFigure(this, true); + } +} diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts new file mode 100644 index 000000000..428a5a429 --- /dev/null +++ b/ee-workflow/src/components/figure/circle.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import p5 from 'p5'; +import Figure from '.'; +import main from '../../main'; + +export default class Circle extends Figure { + diameter: number; + + constructor( + p5Instance: p5, + x: number, + y: number, + color: string, + diameter: number + ) { + super(); + this.p5 = p5Instance; + this.x = x; + this.y = y; + this.color = color; + this.diameter = diameter; + } + + draw() { + this.p5?.fill(this.color); + this.p5?.circle(this.x, this.y, this.diameter); + } + + onHover() { + this.color = 'red'; // TODO: Discuss the function + main.addFigure(this, true); + } + + onClick() { + this.color = 'blue'; // TODO: Discuss the function + main.addFigure(this, true); + } +} diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts new file mode 100644 index 000000000..dae02bf9c --- /dev/null +++ b/ee-workflow/src/components/figure/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import p5 from 'p5'; + +export default abstract class Figure { + p5: p5 | null = null; + x = 0; + y = 0; + color = 'black'; + throw?: boolean; // Property to determine if the object should be added to the snapshot + + abstract draw(): void; + + abstract onHover(): void; + + abstract onClick(): void; +} diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts new file mode 100644 index 000000000..2d28e54aa --- /dev/null +++ b/ee-workflow/src/components/figure/line.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import p5 from 'p5'; +import Figure from '.'; +import main from '../../main'; + +export default class Line extends Figure { + endX: number; + endY: number; + + constructor( + p5Instance: p5, + x: number, + y: number, + color: string, + endX: number, + endY: number + ) { + super(); + this.p5 = p5Instance; + this.x = x; + this.y = y; + this.color = color; + this.endX = endX; + this.endY = endY; + } + + draw() { + this.p5?.fill(this.color); + this.p5?.line(this.x, this.y, this.endX, this.endY); + } + + onHover() { + this.color = 'red'; // TODO: Discuss the function + main.addFigure(this, true); + } + + onClick() { + this.color = 'blue'; // TODO: Discuss the function + main.addFigure(this, true); + } +} diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts new file mode 100644 index 000000000..b8f414572 --- /dev/null +++ b/ee-workflow/src/main.ts @@ -0,0 +1,94 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import p5 from 'p5'; +import Figure from './components/figure'; + +class Main { + p5: p5; + stepsQueue: Figure[] = []; + instantQueue: Figure[] = []; + delay = 50; + pause = false; + snapshot: Figure[] = []; + + constructor() { + this.p5 = new p5(this.init); + } + + private init = (p: p5) => { + p.setup = this.setUp; + p.draw = this.draw; + }; + + private setUp = () => { + this.p5.createCanvas(1600, 1600); + this.p5.background(245); + }; + + private draw = () => { + if (this.pause) { + return; + } + + if (this.p5.frameCount % this.delay === 0) { + const firstObject = this.stepsQueue.shift(); + if (firstObject === undefined) { + return; + } + + firstObject.draw(); + if (!firstObject.throw) { + this.snapshot.push(firstObject); + } + } + + if (this.instantQueue.length > 0) { + this.instantQueue.forEach((object) => { + object.draw(); + }); + this.instantQueue = []; + } + }; + + reDrawAll = () => { + if (this.pause) { + return; + } + + this.p5.clear(); + this.p5.background(245); + this.stepsQueue = []; + this.instantQueue = []; + this.snapshot.forEach((figure) => { + this.instantQueue.push(figure); + }); + }; + + addFigure = (figure: Figure, instant = false) => { + if (instant) { + this.instantQueue.push(figure); + } else { + this.stepsQueue.push(figure); + } + }; + + togglePause = () => { + this.pause = !this.pause; + }; +} + +export default new Main(); diff --git a/ee-workflow/src/typescript.svg b/ee-workflow/src/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/ee-workflow/src/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ee-workflow/src/vite-env.d.ts b/ee-workflow/src/vite-env.d.ts new file mode 100644 index 000000000..5acb16ccd --- /dev/null +++ b/ee-workflow/src/vite-env.d.ts @@ -0,0 +1,15 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/ee-workflow/tsconfig.json b/ee-workflow/tsconfig.json new file mode 100644 index 000000000..0511b9f0e --- /dev/null +++ b/ee-workflow/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} From a976145499b94a1e201e5998ed1c10dde0edb82b Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 23 Dec 2024 13:00:02 +0530 Subject: [PATCH 03/44] Add events and refactoring --- ee-workflow/src/components/figure/box.ts | 17 ++++++-- ee-workflow/src/components/figure/circle.ts | 22 ++++++---- ee-workflow/src/components/figure/index.ts | 2 + ee-workflow/src/components/figure/line.ts | 16 +++---- ee-workflow/src/main.ts | 46 +++++++++++++++------ 5 files changed, 69 insertions(+), 34 deletions(-) diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 91762a092..eb579191b 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import p5 from 'p5'; import Figure from '.'; import main from '../../main'; @@ -23,7 +22,6 @@ export default class Box extends Figure { height: number; constructor( - p5Instance: p5, x: number, y: number, color: string, @@ -31,7 +29,7 @@ export default class Box extends Figure { height: number ) { super(); - this.p5 = p5Instance; + this.p5 = main.getP5Instance(); this.x = x; this.y = y; this.color = color; @@ -53,4 +51,17 @@ export default class Box extends Figure { this.color = 'blue'; // TODO: Discuss the function main.addFigure(this, true); } + + isHovering(): boolean { + if (this.p5?.mouseX === undefined || this.p5?.mouseY === undefined) { + return false; + } + + return ( + this.p5?.mouseX > this.x && + this.p5?.mouseX < this.x + this.width && + this.p5?.mouseY > this.y && + this.p5?.mouseY < this.y + this.height + ); + } } diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index 428a5a429..f9e9393de 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -14,22 +14,15 @@ * limitations under the License. */ -import p5 from 'p5'; import Figure from '.'; import main from '../../main'; export default class Circle extends Figure { diameter: number; - constructor( - p5Instance: p5, - x: number, - y: number, - color: string, - diameter: number - ) { + constructor(x: number, y: number, color: string, diameter: number) { super(); - this.p5 = p5Instance; + this.p5 = main.getP5Instance(); this.x = x; this.y = y; this.color = color; @@ -50,4 +43,15 @@ export default class Circle extends Figure { this.color = 'blue'; // TODO: Discuss the function main.addFigure(this, true); } + + isHovering(): boolean { + if (this.p5?.mouseX === undefined || this.p5?.mouseY === undefined) { + return false; + } + + return ( + this.p5?.dist(this.x, this.y, this.p5.mouseX, this.p5.mouseY) < + this.diameter / 2 + ); + } } diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index dae02bf9c..bc030eca0 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -28,4 +28,6 @@ export default abstract class Figure { abstract onHover(): void; abstract onClick(): void; + + abstract isHovering(): boolean; } diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 2d28e54aa..3001e60b3 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import p5 from 'p5'; import Figure from '.'; import main from '../../main'; @@ -22,16 +21,9 @@ export default class Line extends Figure { endX: number; endY: number; - constructor( - p5Instance: p5, - x: number, - y: number, - color: string, - endX: number, - endY: number - ) { + constructor(x: number, y: number, color: string, endX: number, endY: number) { super(); - this.p5 = p5Instance; + this.p5 = main.getP5Instance(); this.x = x; this.y = y; this.color = color; @@ -53,4 +45,8 @@ export default class Line extends Figure { this.color = 'blue'; // TODO: Discuss the function main.addFigure(this, true); } + + isHovering(): boolean { + return false; // TODO: Implement if line hover is needed + } } diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index b8f414572..77605a7b9 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -29,17 +29,19 @@ class Main { this.p5 = new p5(this.init); } - private init = (p: p5) => { + private init(p: p5) { p.setup = this.setUp; p.draw = this.draw; - }; + p.mouseMoved = this.onHover; + p.mouseClicked = this.onClick; + } - private setUp = () => { + private setUp() { this.p5.createCanvas(1600, 1600); this.p5.background(245); - }; + } - private draw = () => { + private draw() { if (this.pause) { return; } @@ -62,9 +64,29 @@ class Main { }); this.instantQueue = []; } - }; + } + + private onHover() { + this.snapshot.forEach((object) => { + if (object.isHovering()) { + object.onHover(); + } + }); + } + + private onClick() { + this.snapshot.forEach((object) => { + if (object.isHovering()) { + object.onClick(); + } + }); + } + + getP5Instance() { + return this.p5; + } - reDrawAll = () => { + reDrawAll() { if (this.pause) { return; } @@ -76,19 +98,19 @@ class Main { this.snapshot.forEach((figure) => { this.instantQueue.push(figure); }); - }; + } - addFigure = (figure: Figure, instant = false) => { + addFigure(figure: Figure, instant = false) { if (instant) { this.instantQueue.push(figure); } else { this.stepsQueue.push(figure); } - }; + } - togglePause = () => { + togglePause() { this.pause = !this.pause; - }; + } } export default new Main(); From e7e1307f897555f384b152551aa615480bba5697 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 23 Dec 2024 14:45:25 +0530 Subject: [PATCH 04/44] Update events, update vars, callbacks logic --- ee-workflow/src/components/figure/box.ts | 33 +++++++++++++++---- ee-workflow/src/components/figure/circle.ts | 36 +++++++++++++++++---- ee-workflow/src/components/figure/index.ts | 24 +++++++++++++- ee-workflow/src/components/figure/line.ts | 35 ++++++++++++++++---- 4 files changed, 108 insertions(+), 20 deletions(-) diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index eb579191b..5ba83e3a7 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -24,34 +24,53 @@ export default class Box extends Figure { constructor( x: number, y: number, - color: string, width: number, - height: number + height: number, + fill?: string, + stroke?: string ) { super(); this.p5 = main.getP5Instance(); this.x = x; this.y = y; - this.color = color; this.width = width; this.height = height; + this.fill = fill || 'black'; + this.stroke = stroke || 'black'; + this.previousFill = this.fill; + this.previousStroke = this.stroke; } draw() { - this.p5?.fill(this.color); + this.p5?.push(); + this.p5?.fill(this.fill); + this.p5?.stroke(this.stroke); this.p5?.rect(this.x, this.y, this.width, this.height); + this.p5?.pop(); } onHover() { - this.color = 'red'; // TODO: Discuss the function + this.savePreviousColors(); + this.fill = 'red'; // TODO: Discuss the function main.addFigure(this, true); } - onClick() { - this.color = 'blue'; // TODO: Discuss the function + onLeave() { + if ( + this.fill === this.previousFill && + this.stroke === this.previousStroke + ) { + return; + } + + this.reApplyPreviousColors(); main.addFigure(this, true); } + onClick() { + // TODO: Discuss the function + } + isHovering(): boolean { if (this.p5?.mouseX === undefined || this.p5?.mouseY === undefined) { return false; diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index f9e9393de..ca24e0e26 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -20,30 +20,54 @@ import main from '../../main'; export default class Circle extends Figure { diameter: number; - constructor(x: number, y: number, color: string, diameter: number) { + constructor( + x: number, + y: number, + diameter: number, + fill?: string, + stroke?: string + ) { super(); this.p5 = main.getP5Instance(); this.x = x; this.y = y; - this.color = color; this.diameter = diameter; + this.fill = fill || 'black'; + this.stroke = stroke || 'black'; + this.previousFill = this.fill; + this.previousStroke = this.stroke; } draw() { - this.p5?.fill(this.color); + this.p5?.push(); + this.p5?.fill(this.fill); + this.p5?.stroke(this.stroke); this.p5?.circle(this.x, this.y, this.diameter); + this.p5?.pop(); } onHover() { - this.color = 'red'; // TODO: Discuss the function + this.savePreviousColors(); + this.fill = 'red'; // TODO: Discuss the function main.addFigure(this, true); } - onClick() { - this.color = 'blue'; // TODO: Discuss the function + onLeave() { + if ( + this.fill === this.previousFill && + this.stroke === this.previousStroke + ) { + return; + } + + this.reApplyPreviousColors(); main.addFigure(this, true); } + onClick() { + // TODO: Discuss the function + } + isHovering(): boolean { if (this.p5?.mouseX === undefined || this.p5?.mouseY === undefined) { return false; diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index bc030eca0..7dcfea6c3 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -20,7 +20,10 @@ export default abstract class Figure { p5: p5 | null = null; x = 0; y = 0; - color = 'black'; + fill = 'black'; + stroke = 'black'; + previousFill = ''; + previousStroke = ''; throw?: boolean; // Property to determine if the object should be added to the snapshot abstract draw(): void; @@ -30,4 +33,23 @@ export default abstract class Figure { abstract onClick(): void; abstract isHovering(): boolean; + + abstract onLeave(): void; + + savePreviousColors() { + if (this.previousFill === this.fill) { + this.previousFill = this.fill; + } + + if (this.previousStroke === this.stroke) { + this.previousStroke = this.stroke; + } + } + + reApplyPreviousColors() { + this.fill = this.previousFill; + this.previousFill = this.fill; + this.stroke = this.previousStroke; + this.previousStroke = this.stroke; + } } diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 3001e60b3..b0f354bc4 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -21,31 +21,54 @@ export default class Line extends Figure { endX: number; endY: number; - constructor(x: number, y: number, color: string, endX: number, endY: number) { + constructor( + x: number, + y: number, + endX: number, + endY: number, + stroke?: string + ) { super(); this.p5 = main.getP5Instance(); this.x = x; this.y = y; - this.color = color; this.endX = endX; this.endY = endY; + this.stroke = stroke || 'black'; + this.fill = 'black'; // Line doesn't have fill + this.previousFill = this.fill; + this.previousStroke = this.stroke; } draw() { - this.p5?.fill(this.color); + this.p5?.push(); + this.p5?.stroke(this.stroke); this.p5?.line(this.x, this.y, this.endX, this.endY); + this.p5?.pop(); } onHover() { - this.color = 'red'; // TODO: Discuss the function + this.savePreviousColors(); + this.stroke = 'red'; // TODO: Discuss the function main.addFigure(this, true); } - onClick() { - this.color = 'blue'; // TODO: Discuss the function + onLeave() { + if ( + this.fill === this.previousFill && + this.stroke === this.previousStroke + ) { + return; + } + + this.reApplyPreviousColors(); main.addFigure(this, true); } + onClick() { + // TODO: Discuss the function + } + isHovering(): boolean { return false; // TODO: Implement if line hover is needed } From a92bd70567b9c177135372ccf08015cc011ccd97 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 23 Dec 2024 14:47:27 +0530 Subject: [PATCH 05/44] Bind this to init functions, update throw value of object once added to snapshot, and handle onleave --- ee-workflow/src/main.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 77605a7b9..a68eca0f7 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -26,14 +26,14 @@ class Main { snapshot: Figure[] = []; constructor() { - this.p5 = new p5(this.init); + this.p5 = new p5(this.init.bind(this)); } private init(p: p5) { - p.setup = this.setUp; - p.draw = this.draw; - p.mouseMoved = this.onHover; - p.mouseClicked = this.onClick; + p.setup = this.setUp.bind(this); + p.draw = this.draw.bind(this); + p.mouseMoved = this.onHover.bind(this); + p.mouseClicked = this.onClick.bind(this); } private setUp() { @@ -55,12 +55,18 @@ class Main { firstObject.draw(); if (!firstObject.throw) { this.snapshot.push(firstObject); + firstObject.throw = true; } } if (this.instantQueue.length > 0) { this.instantQueue.forEach((object) => { object.draw(); + + if (!object.throw) { + this.snapshot.push(object); + object.throw = true; + } }); this.instantQueue = []; } @@ -70,6 +76,8 @@ class Main { this.snapshot.forEach((object) => { if (object.isHovering()) { object.onHover(); + } else { + object.onLeave(); } }); } From 408552563c13e28b29b6be4f34243fa34746f1f9 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 23 Dec 2024 15:54:43 +0530 Subject: [PATCH 06/44] Handle removal logic --- ee-workflow/src/components/figure/box.ts | 8 ++++++++ ee-workflow/src/components/figure/circle.ts | 8 ++++++++ ee-workflow/src/components/figure/index.ts | 13 ++++++++++++- ee-workflow/src/components/figure/line.ts | 7 +++++++ ee-workflow/src/main.ts | 10 ++++++++-- 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 5ba83e3a7..681aacd03 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -83,4 +83,12 @@ export default class Box extends Figure { this.p5?.mouseY < this.y + this.height ); } + + remove() { + this.p5?.push(); + this.p5?.fill(main.backgroundColor); + this.p5?.stroke(main.backgroundColor); + this.p5?.rect(this.x, this.y, this.width, this.height); + this.p5?.pop(); + } } diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index ca24e0e26..f1105d34e 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -78,4 +78,12 @@ export default class Circle extends Figure { this.diameter / 2 ); } + + remove() { + this.p5?.push(); + this.p5?.fill(main.backgroundColor); + this.p5?.stroke(main.backgroundColor); + this.p5?.circle(this.x, this.y, this.diameter + 1); + this.p5?.pop(); + } } diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 7dcfea6c3..9ed082b84 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -18,6 +18,7 @@ import p5 from 'p5'; export default abstract class Figure { p5: p5 | null = null; + uid = ''; x = 0; y = 0; fill = 'black'; @@ -26,15 +27,25 @@ export default abstract class Figure { previousStroke = ''; throw?: boolean; // Property to determine if the object should be added to the snapshot + static objectCount = 0; + + constructor() { + Figure.objectCount++; + this.uid = + `object-${Figure.objectCount}` + Math.random().toString(36).slice(2, 9); + } + abstract draw(): void; abstract onHover(): void; + abstract onLeave(): void; + abstract onClick(): void; abstract isHovering(): boolean; - abstract onLeave(): void; + abstract remove(): void; savePreviousColors() { if (this.previousFill === this.fill) { diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index b0f354bc4..1ccb223b4 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -72,4 +72,11 @@ export default class Line extends Figure { isHovering(): boolean { return false; // TODO: Implement if line hover is needed } + + remove() { + this.p5?.push(); + this.p5?.stroke(main.backgroundColor); + this.p5?.line(this.x, this.y, this.endX, this.endY); + this.p5?.pop(); + } } diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index a68eca0f7..5c59acd03 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -24,6 +24,7 @@ class Main { delay = 50; pause = false; snapshot: Figure[] = []; + backgroundColor = 245; constructor() { this.p5 = new p5(this.init.bind(this)); @@ -38,7 +39,7 @@ class Main { private setUp() { this.p5.createCanvas(1600, 1600); - this.p5.background(245); + this.p5.background(this.backgroundColor); } private draw() { @@ -100,7 +101,7 @@ class Main { } this.p5.clear(); - this.p5.background(245); + this.p5.background(this.backgroundColor); this.stepsQueue = []; this.instantQueue = []; this.snapshot.forEach((figure) => { @@ -119,6 +120,11 @@ class Main { togglePause() { this.pause = !this.pause; } + + removeFigure(figure: Figure) { + this.snapshot = this.snapshot.filter((f) => f.uid !== figure.uid); + this.reDrawAll(); + } } export default new Main(); From 6e0db1020adc59eaa24297361d2388b2ebdd7f0b Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 23 Dec 2024 17:54:03 +0530 Subject: [PATCH 07/44] Move some props to parent, and add text, image classes --- ee-workflow/src/components/figure/box.ts | 9 +-- ee-workflow/src/components/figure/circle.ts | 9 +-- ee-workflow/src/components/figure/image.ts | 72 +++++++++++++++++++++ ee-workflow/src/components/figure/index.ts | 10 ++- ee-workflow/src/components/figure/line.ts | 9 +-- ee-workflow/src/components/figure/text.ts | 70 ++++++++++++++++++++ 6 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 ee-workflow/src/components/figure/image.ts create mode 100644 ee-workflow/src/components/figure/text.ts diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 681aacd03..0c126583e 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -29,16 +29,9 @@ export default class Box extends Figure { fill?: string, stroke?: string ) { - super(); - this.p5 = main.getP5Instance(); - this.x = x; - this.y = y; + super(x, y, fill, stroke); this.width = width; this.height = height; - this.fill = fill || 'black'; - this.stroke = stroke || 'black'; - this.previousFill = this.fill; - this.previousStroke = this.stroke; } draw() { diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index f1105d34e..ee56a2929 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -27,15 +27,8 @@ export default class Circle extends Figure { fill?: string, stroke?: string ) { - super(); - this.p5 = main.getP5Instance(); - this.x = x; - this.y = y; + super(x, y, fill, stroke); this.diameter = diameter; - this.fill = fill || 'black'; - this.stroke = stroke || 'black'; - this.previousFill = this.fill; - this.previousStroke = this.stroke; } draw() { diff --git a/ee-workflow/src/components/figure/image.ts b/ee-workflow/src/components/figure/image.ts new file mode 100644 index 000000000..bf5674bbe --- /dev/null +++ b/ee-workflow/src/components/figure/image.ts @@ -0,0 +1,72 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import p5 from 'p5'; +import Figure from '.'; + +export default class Image extends Figure { + image: p5.Image; + width: number; + height: number; + + constructor( + x: number, + y: number, + image: p5.Image, + width: number, + height: number + ) { + super(x, y); + this.image = image; + this.width = width; + this.height = height; + } + + draw() { + this.p5?.push(); + this.p5?.image(this.image, this.x, this.y, this.width, this.height); + this.p5?.pop(); + } + + onHover() { + return; + } + + onLeave() { + return; + } + + onClick() { + // TODO: Discuss the function + } + + isHovering(): boolean { + return false; + } + + remove() { + const whiteImage = this.p5?.createImage( + this.width, + this.height + ) as p5.Image; + + whiteImage.set(this.x, this.y, this.p5?.color(255) as p5.Color); + + this.p5?.push(); + this.p5?.image(whiteImage, this.x, this.y, this.width, this.height); + this.p5?.pop(); + } +} diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 9ed082b84..49cbb2cc9 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -15,6 +15,7 @@ */ import p5 from 'p5'; +import main from '../../main'; export default abstract class Figure { p5: p5 | null = null; @@ -29,10 +30,17 @@ export default abstract class Figure { static objectCount = 0; - constructor() { + constructor(x: number, y: number, fill?: string, stroke?: string) { Figure.objectCount++; this.uid = `object-${Figure.objectCount}` + Math.random().toString(36).slice(2, 9); + this.p5 = main.getP5Instance(); + this.x = x; + this.y = y; + this.fill = fill || 'black'; + this.stroke = stroke || 'black'; + this.previousFill = this.fill; + this.previousStroke = this.stroke; } abstract draw(): void; diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 1ccb223b4..90e3b00b2 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -28,16 +28,9 @@ export default class Line extends Figure { endY: number, stroke?: string ) { - super(); - this.p5 = main.getP5Instance(); - this.x = x; - this.y = y; + super(x, y, undefined, stroke); this.endX = endX; this.endY = endY; - this.stroke = stroke || 'black'; - this.fill = 'black'; // Line doesn't have fill - this.previousFill = this.fill; - this.previousStroke = this.stroke; } draw() { diff --git a/ee-workflow/src/components/figure/text.ts b/ee-workflow/src/components/figure/text.ts new file mode 100644 index 000000000..22406609d --- /dev/null +++ b/ee-workflow/src/components/figure/text.ts @@ -0,0 +1,70 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Figure from '.'; +import main from '../../main'; + +export default class Text extends Figure { + str: string; + size: number; + + constructor( + x: number, + y: number, + str: string, + size?: number, + fill?: string, + stroke?: string + ) { + super(x, y, fill, stroke); + this.str = str; + this.size = size || 16; + } + + draw() { + this.p5?.push(); + this.p5?.fill(this.fill); + this.p5?.stroke(this.stroke); + this.p5?.textSize(this.size); + this.p5?.text(this.str, this.x, this.y); + this.p5?.pop(); + } + + onHover() { + return; + } + + onLeave() { + return; + } + + onClick() { + // TODO: Discuss the function + } + + isHovering(): boolean { + return false; + } + + remove() { + this.p5?.push(); + this.p5?.fill(main.backgroundColor); + this.p5?.stroke(main.backgroundColor); + this.p5?.textSize(this.size); + this.p5?.text(this.str, this.x, this.y); + this.p5?.pop(); + } +} From 55c14aa4be6afc5e4cfc95cd73bbb41b23df2c81 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 23 Dec 2024 18:00:01 +0530 Subject: [PATCH 08/44] Draw figures --- ee-workflow/index.html | 2 +- ee-workflow/src/index.ts | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 ee-workflow/src/index.ts diff --git a/ee-workflow/index.html b/ee-workflow/index.html index fb5aad747..3dfe4ee7f 100644 --- a/ee-workflow/index.html +++ b/ee-workflow/index.html @@ -6,6 +6,6 @@
- + diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts new file mode 100644 index 000000000..00f722b27 --- /dev/null +++ b/ee-workflow/src/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Circle from './components/figure/circle'; +import Line from './components/figure/line'; +import main from './main'; + +const timeline = new Line(0, 200, 1000, 200, 'black'); +main.addFigure(timeline, true); + +const circles = [ + new Circle(100, 200, 75, 'gray'), + new Circle(300, 200, 75, 'gray'), + new Circle(500, 200, 75, 'gray'), + new Circle(700, 200, 75, 'gray'), +]; + +circles.forEach((circle) => main.addFigure(circle, true)); From 0d115f33656131dacf56c60de227a6056b47105e Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 24 Dec 2024 10:43:39 +0530 Subject: [PATCH 09/44] Add group class and update logic --- ee-workflow/src/components/figure/index.ts | 10 ++- ee-workflow/src/components/group.ts | 57 +++++++++++++++ ee-workflow/src/main.ts | 80 ++++++++++++++++++---- 3 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 ee-workflow/src/components/group.ts diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 49cbb2cc9..916e0c8fc 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -19,7 +19,8 @@ import main from '../../main'; export default abstract class Figure { p5: p5 | null = null; - uid = ''; + id = ''; + gid = ''; x = 0; y = 0; fill = 'black'; @@ -32,7 +33,7 @@ export default abstract class Figure { constructor(x: number, y: number, fill?: string, stroke?: string) { Figure.objectCount++; - this.uid = + this.id = `object-${Figure.objectCount}` + Math.random().toString(36).slice(2, 9); this.p5 = main.getP5Instance(); this.x = x; @@ -55,6 +56,11 @@ export default abstract class Figure { abstract remove(): void; + setGid(gid: string) { + ``; + this.gid = gid; + } + savePreviousColors() { if (this.previousFill === this.fill) { this.previousFill = this.fill; diff --git a/ee-workflow/src/components/group.ts b/ee-workflow/src/components/group.ts new file mode 100644 index 000000000..69999214c --- /dev/null +++ b/ee-workflow/src/components/group.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Figure from './figure'; + +export default class Group { + figures: Figure[]; + id: string; + throw = false; + + static groupCount = 0; + + constructor(figures: Figure[]) { + Group.groupCount++; + this.id = + `group-${Group.groupCount}` + Math.random().toString(36).slice(2, 9); + this.figures = figures; + this.figures.forEach((figure) => figure.setGid(this.id)); + } + + draw() { + this.figures.forEach((figure) => { + figure.draw(); + }); + } + + onHover() { + this.figures.forEach((figure) => { + figure.onHover(); + }); + } + + onLeave() { + this.figures.forEach((figure) => { + figure.onLeave(); + }); + } + + onClick() { + this.figures.forEach((figure) => { + figure.onClick(); + }); + } +} diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 5c59acd03..b216c127d 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -16,14 +16,17 @@ import p5 from 'p5'; import Figure from './components/figure'; +import Group from './components/group'; class Main { p5: p5; stepsQueue: Figure[] = []; + groupStepsQueue: Group[] = []; instantQueue: Figure[] = []; delay = 50; pause = false; snapshot: Figure[] = []; + groupSnapshot: Group[] = []; backgroundColor = 245; constructor() { @@ -41,6 +44,10 @@ class Main { this.p5.createCanvas(1600, 1600); this.p5.background(this.backgroundColor); } + private saveToSnapshot(object: Figure) { + this.snapshot.push(object); + object.throw = true; + } private draw() { if (this.pause) { @@ -48,15 +55,25 @@ class Main { } if (this.p5.frameCount % this.delay === 0) { - const firstObject = this.stepsQueue.shift(); - if (firstObject === undefined) { - return; + if (this.stepsQueue.length > 0) { + const firstObject =
this.stepsQueue.shift(); + + firstObject.draw(); + if (!firstObject.throw) { + this.saveToSnapshot(firstObject); + } } - firstObject.draw(); - if (!firstObject.throw) { - this.snapshot.push(firstObject); - firstObject.throw = true; + if (this.groupStepsQueue.length > 0) { + const firstGroup = this.groupStepsQueue.shift(); + + firstGroup.draw(); + if (!firstGroup.throw) { + firstGroup.figures.forEach((object) => this.saveToSnapshot(object)); + + this.groupSnapshot.push(firstGroup); + firstGroup.throw = true; + } } } @@ -73,20 +90,34 @@ class Main { } } + private isGrouped(object: Figure): Group | undefined { + if (!object.gid) { + return undefined; + } + + return this.groupSnapshot.find((group) => group.id === object.gid); + } + private onHover() { this.snapshot.forEach((object) => { - if (object.isHovering()) { - object.onHover(); + const isHovering = object.isHovering(); + const _object = this.isGrouped(object) || object; + + if (isHovering) { + _object.onHover(); } else { - object.onLeave(); + _object.onLeave(); } }); } private onClick() { this.snapshot.forEach((object) => { - if (object.isHovering()) { - object.onClick(); + const isHovering = object.isHovering(); + if (isHovering) { + const _object = this.isGrouped(object) || object; + + _object.onClick(); } }); } @@ -117,14 +148,37 @@ class Main { } } + addGroup(group: Group, instant = false) { + if (instant) { + group.figures.forEach((figure) => this.instantQueue.push(figure)); + } else { + this.groupStepsQueue.push(group); + } + } + togglePause() { this.pause = !this.pause; } removeFigure(figure: Figure) { - this.snapshot = this.snapshot.filter((f) => f.uid !== figure.uid); + this.snapshot = this.snapshot.filter((f) => f.id !== figure.id); this.reDrawAll(); } + + removeGroup(group: Group) { + let toRemove = null; + + this.groupSnapshot = this.groupSnapshot.filter((g) => { + if (g.id === group.id) { + toRemove = g; + return false; + } + + return true; + }); + + toRemove?.figures.forEach((g) => this.removeFigure(g)); + } } export default new Main(); From d54afc061449e48d5fd5e8b21a172f96c4256af3 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 24 Dec 2024 10:43:58 +0530 Subject: [PATCH 10/44] Remove typo --- ee-workflow/src/components/figure/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 916e0c8fc..71c7a5baf 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -57,7 +57,6 @@ export default abstract class Figure { abstract remove(): void; setGid(gid: string) { - ``; this.gid = gid; } From c48ba4ab6a62adb63dab85342253fdd18b5c44a1 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 24 Dec 2024 11:36:27 +0530 Subject: [PATCH 11/44] Add redraw cb to components --- ee-workflow/src/components/figure/box.ts | 18 ++++++++++++++++++ ee-workflow/src/components/figure/circle.ts | 16 ++++++++++++++++ ee-workflow/src/components/figure/image.ts | 17 +++++++++++++++++ ee-workflow/src/components/figure/index.ts | 2 ++ ee-workflow/src/components/figure/line.ts | 16 ++++++++++++++++ ee-workflow/src/components/figure/text.ts | 18 ++++++++++++++++++ ee-workflow/src/main.ts | 1 + 7 files changed, 88 insertions(+) diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 0c126583e..51d832e76 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -84,4 +84,22 @@ export default class Box extends Figure { this.p5?.rect(this.x, this.y, this.width, this.height); this.p5?.pop(); } + + reDraw( + x?: number, + y?: number, + width?: number, + height?: number, + fill?: string, + stroke?: string + ) { + this.remove(); + this.x = x ?? this.x; + this.y = y ?? this.y; + this.width = width ?? this.width; + this.height = height ?? this.height; + this.fill = fill || this.fill; + this.stroke = stroke || this.stroke; + main.reDrawAll(); + } } diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index ee56a2929..6aa3cd533 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -79,4 +79,20 @@ export default class Circle extends Figure { this.p5?.circle(this.x, this.y, this.diameter + 1); this.p5?.pop(); } + + reDraw( + x?: number, + y?: number, + diameter?: number, + fill?: string, + stroke?: string + ) { + this.remove(); + this.x = x ?? this.x; + this.y = y ?? this.y; + this.diameter = diameter ?? this.diameter; + this.fill = fill || this.fill; + this.stroke = stroke || this.stroke; + main.reDrawAll(); + } } diff --git a/ee-workflow/src/components/figure/image.ts b/ee-workflow/src/components/figure/image.ts index bf5674bbe..24aab199f 100644 --- a/ee-workflow/src/components/figure/image.ts +++ b/ee-workflow/src/components/figure/image.ts @@ -16,6 +16,7 @@ import p5 from 'p5'; import Figure from '.'; +import main from '../../main'; export default class Image extends Figure { image: p5.Image; @@ -69,4 +70,20 @@ export default class Image extends Figure { this.p5?.image(whiteImage, this.x, this.y, this.width, this.height); this.p5?.pop(); } + + reDraw( + x?: number, + y?: number, + image?: p5.Image, + width?: number, + height?: number + ) { + this.remove(); + this.x = x ?? this.x; + this.y = y ?? this.y; + this.image = image || this.image; + this.width = width ?? this.width; + this.height = height ?? this.height; + main.reDrawAll(); + } } diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 71c7a5baf..750b23db6 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -56,6 +56,8 @@ export default abstract class Figure { abstract remove(): void; + abstract reDraw(...params: Array): void; + setGid(gid: string) { this.gid = gid; } diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 90e3b00b2..b5e98afe5 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -72,4 +72,20 @@ export default class Line extends Figure { this.p5?.line(this.x, this.y, this.endX, this.endY); this.p5?.pop(); } + + reDraw( + x?: number, + y?: number, + endX?: number, + endY?: number, + stroke?: string + ) { + this.remove(); + this.x = x ?? this.x; + this.y = y ?? this.y; + this.endX = endX ?? this.endX; + this.endY = endY ?? this.endY; + this.stroke = stroke || this.stroke; + main.reDrawAll(); + } } diff --git a/ee-workflow/src/components/figure/text.ts b/ee-workflow/src/components/figure/text.ts index 22406609d..3e0ee3813 100644 --- a/ee-workflow/src/components/figure/text.ts +++ b/ee-workflow/src/components/figure/text.ts @@ -67,4 +67,22 @@ export default class Text extends Figure { this.p5?.text(this.str, this.x, this.y); this.p5?.pop(); } + + reDraw( + x?: number, + y?: number, + str?: string, + size?: number, + fill?: string, + stroke?: string + ) { + this.remove(); + this.x = x ?? this.x; + this.y = y ?? this.y; + this.str = str || this.str; + this.size = size ?? this.size; + this.fill = fill || this.fill; + this.stroke = stroke || this.stroke; + main.reDrawAll(); + } } diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index b216c127d..acbad25ff 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -44,6 +44,7 @@ class Main { this.p5.createCanvas(1600, 1600); this.p5.background(this.backgroundColor); } + private saveToSnapshot(object: Figure) { this.snapshot.push(object); object.throw = true; From 36d6e008af55fc62908864fa813623b4174db2f1 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 24 Dec 2024 11:43:53 +0530 Subject: [PATCH 12/44] Add arrow for line --- ee-workflow/src/components/figure/line.ts | 18 +++++++++++++++++- ee-workflow/src/index.ts | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index b5e98afe5..355c9b2a3 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -20,23 +20,39 @@ import main from '../../main'; export default class Line extends Figure { endX: number; endY: number; + hasArrow: boolean; constructor( x: number, y: number, endX: number, endY: number, - stroke?: string + stroke?: string, + hasArrow?: boolean ) { super(x, y, undefined, stroke); this.endX = endX; this.endY = endY; + this.hasArrow = hasArrow ?? false; } draw() { this.p5?.push(); this.p5?.stroke(this.stroke); this.p5?.line(this.x, this.y, this.endX, this.endY); + if (this.hasArrow) { + const angle = ( + this.p5?.atan2(this.y - this.endY, this.x - this.endX) + ); + this.p5?.translate(this.endX, this.endY); + this.p5?.rotate(angle - this.p5?.HALF_PI); + this.p5?.fill(this.stroke); + this.p5?.beginShape(); + this.p5?.vertex(0, 0); + this.p5?.vertex(-5, 10); + this.p5?.vertex(5, 10); + this.p5?.endShape(this.p5?.CLOSE); + } this.p5?.pop(); } diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 00f722b27..4360101db 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -18,7 +18,7 @@ import Circle from './components/figure/circle'; import Line from './components/figure/line'; import main from './main'; -const timeline = new Line(0, 200, 1000, 200, 'black'); +const timeline = new Line(0, 200, 1000, 200, 'black', true); main.addFigure(timeline, true); const circles = [ From 3797ec58c24b98c69ca85988b72a8327eaa3c88b Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 24 Dec 2024 16:21:29 +0530 Subject: [PATCH 13/44] Add Animator class to render animation, and integrate to main class --- ee-workflow/src/components/animator.ts | 47 ++++++ ee-workflow/src/components/figure/index.ts | 5 + ee-workflow/src/components/figure/text.ts | 13 +- ee-workflow/src/components/group.ts | 6 + ee-workflow/src/index.ts | 44 +++++- ee-workflow/src/main.ts | 158 ++++++++++++++++----- 6 files changed, 229 insertions(+), 44 deletions(-) create mode 100644 ee-workflow/src/components/animator.ts diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts new file mode 100644 index 000000000..f8195911d --- /dev/null +++ b/ee-workflow/src/components/animator.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Figure from './figure'; +import Group from './group'; + +export default class Animator { + id: string; + objects: Array
= []; + index = 0; + throw = false; + + static animationCounter = 0; + + constructor(objects: Array
) { + Animator.animationCounter++; + this.id = + `animation-${Animator.animationCounter}` + + Math.random().toString(36).slice(2, 9); + this.objects = objects; + this.objects.forEach((object) => object.setAid(this.id)); + } + + draw(): boolean { + if (this.index >= this.objects.length) { + this.index = 0; + return true; + } + + this.objects[this.index].draw(); + this.index++; + return false; + } +} diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 750b23db6..f063f2114 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -21,6 +21,7 @@ export default abstract class Figure { p5: p5 | null = null; id = ''; gid = ''; + aid = ''; x = 0; y = 0; fill = 'black'; @@ -62,6 +63,10 @@ export default abstract class Figure { this.gid = gid; } + setAid(aid: string) { + this.aid = aid; + } + savePreviousColors() { if (this.previousFill === this.fill) { this.previousFill = this.fill; diff --git a/ee-workflow/src/components/figure/text.ts b/ee-workflow/src/components/figure/text.ts index 3e0ee3813..34136eb59 100644 --- a/ee-workflow/src/components/figure/text.ts +++ b/ee-workflow/src/components/figure/text.ts @@ -21,15 +21,8 @@ export default class Text extends Figure { str: string; size: number; - constructor( - x: number, - y: number, - str: string, - size?: number, - fill?: string, - stroke?: string - ) { - super(x, y, fill, stroke); + constructor(x: number, y: number, str: string, size?: number, fill?: string) { + super(x, y, fill); this.str = str; this.size = size || 16; } @@ -37,8 +30,8 @@ export default class Text extends Figure { draw() { this.p5?.push(); this.p5?.fill(this.fill); - this.p5?.stroke(this.stroke); this.p5?.textSize(this.size); + this.p5?.textAlign(this.p5.CENTER, this.p5.CENTER); this.p5?.text(this.str, this.x, this.y); this.p5?.pop(); } diff --git a/ee-workflow/src/components/group.ts b/ee-workflow/src/components/group.ts index 69999214c..e415e393e 100644 --- a/ee-workflow/src/components/group.ts +++ b/ee-workflow/src/components/group.ts @@ -19,6 +19,7 @@ import Figure from './figure'; export default class Group { figures: Figure[]; id: string; + aid = ''; throw = false; static groupCount = 0; @@ -54,4 +55,9 @@ export default class Group { figure.onClick(); }); } + + setAid(aid: string) { + this.aid = aid; + this.figures.forEach((figure) => figure.setAid(aid)); + } } diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 4360101db..dd2d3bba1 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -14,11 +14,15 @@ * limitations under the License. */ +import Animator from './components/animator'; +import Box from './components/figure/box'; import Circle from './components/figure/circle'; import Line from './components/figure/line'; +import Text from './components/figure/text'; +import Group from './components/group'; import main from './main'; -const timeline = new Line(0, 200, 1000, 200, 'black', true); +const timeline = new Line(0, 200, 1000, 200, 'black'); main.addFigure(timeline, true); const circles = [ @@ -27,5 +31,41 @@ const circles = [ new Circle(500, 200, 75, 'gray'), new Circle(700, 200, 75, 'gray'), ]; - circles.forEach((circle) => main.addFigure(circle, true)); + +const textonCircles = [ + new Text(100, 75, '2024-01-01'), + new Text(100, 100, 'adv1.com'), + new Text(300, 75, '2024-01-02'), + new Text(300, 100, 'adv2.com'), + new Text(500, 75, '2024-01-03'), + new Text(500, 100, 'adv3.com'), + new Text(700, 75, '2024-01-04'), + new Text(700, 100, 'adv4.com'), +]; +textonCircles.forEach((text) => main.addFigure(text, true)); + +const circleToTextLine = [ + new Line(100, 163, 100, 110, 'black'), + new Line(300, 163, 300, 110, 'black'), + new Line(500, 163, 500, 110, 'black'), + new Line(700, 163, 700, 110, 'black'), +]; +circleToTextLine.forEach((line) => main.addFigure(line, true)); + +const firstCircleAnimations = [ + new Line(95, 237, 95, 300, 'black', true), + new Group([ + new Box(50, 300, 100, 50, 'gray'), + new Text(100, 325, 'DSP tags'), + ]), + new Line(95, 350, 95, 413, 'black', true), + new Group([new Box(50, 413, 100, 50, 'gray'), new Text(100, 438, 'DSPs')]), + new Line(105, 413, 105, 350, 'black', true), + new Line(105, 300, 105, 237, 'black', true), + new Text(170, 270, 'joinInterestGroup()', 12), +]; + +main.addAnimator(new Animator(firstCircleAnimations)); + +main.addFigure(new Line(0, 0, 0, 0)); diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index acbad25ff..f74f85559 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -17,16 +17,21 @@ import p5 from 'p5'; import Figure from './components/figure'; import Group from './components/group'; +import Animator from './components/animator'; class Main { p5: p5; stepsQueue: Figure[] = []; groupStepsQueue: Group[] = []; + animatorStepsQueue: Animator[] = []; instantQueue: Figure[] = []; - delay = 50; + groupInstantQueue: Group[] = []; + animatorInstantQueue: Animator[] = []; + delay = 100; pause = false; snapshot: Figure[] = []; groupSnapshot: Group[] = []; + animatorSnapshot: Animator[] = []; backgroundColor = 245; constructor() { @@ -50,44 +55,86 @@ class Main { object.throw = true; } - private draw() { - if (this.pause) { - return; - } + private processGroup( + queue: Figure[], + groupQueue: Group[], + shouldDraw = true + ) { + const group = groupQueue.shift(); - if (this.p5.frameCount % this.delay === 0) { - if (this.stepsQueue.length > 0) { - const firstObject =
this.stepsQueue.shift(); + if (group) { + if (shouldDraw) { + group.draw(); + } + + let toRemoveCount = group.figures.length - 1; + while (toRemoveCount > 0) { + queue.shift(); + toRemoveCount--; + } + if (!group.throw) { + group.figures.forEach((object) => this.saveToSnapshot(object)); + this.groupSnapshot.push(group); + group.throw = true; + } + } + } + + private runner(useInstantQueue = false) { + const queue = useInstantQueue ? this.instantQueue : this.stepsQueue; + const groupQueue = useInstantQueue + ? this.groupInstantQueue + : this.groupStepsQueue; + const animatorQueue = useInstantQueue + ? this.animatorInstantQueue + : this.animatorStepsQueue; + + if (queue.length > 0) { + const firstObject =
queue.shift(); + + if (firstObject.aid) { + const animator = animatorQueue[0]; + + if (animator) { + const isDone = animator.draw(); + + if (firstObject.gid) { + this.processGroup(queue, groupQueue, false); + } else { + if (!firstObject.throw) { + this.saveToSnapshot(firstObject); + } + } + + if (isDone) { + this.animatorSnapshot.push(animator); + animatorQueue.shift(); + this.reDrawAll(); + } + } + } else if (firstObject.gid) { + this.processGroup(queue, groupQueue); + } else { firstObject.draw(); if (!firstObject.throw) { this.saveToSnapshot(firstObject); } } + } + } - if (this.groupStepsQueue.length > 0) { - const firstGroup = this.groupStepsQueue.shift(); - - firstGroup.draw(); - if (!firstGroup.throw) { - firstGroup.figures.forEach((object) => this.saveToSnapshot(object)); - - this.groupSnapshot.push(firstGroup); - firstGroup.throw = true; - } - } + private draw() { + if (this.pause) { + return; } - if (this.instantQueue.length > 0) { - this.instantQueue.forEach((object) => { - object.draw(); + if (this.p5.frameCount % this.delay === 0) { + this.runner(); + } - if (!object.throw) { - this.snapshot.push(object); - object.throw = true; - } - }); - this.instantQueue = []; + while (this.instantQueue.length > 0) { + this.runner(true); } } @@ -127,6 +174,10 @@ class Main { return this.p5; } + togglePause() { + this.pause = !this.pause; + } + reDrawAll() { if (this.pause) { return; @@ -137,7 +188,9 @@ class Main { this.stepsQueue = []; this.instantQueue = []; this.snapshot.forEach((figure) => { - this.instantQueue.push(figure); + if (!(figure instanceof Animator)) { + this.instantQueue.push(figure); + } }); } @@ -151,14 +204,34 @@ class Main { addGroup(group: Group, instant = false) { if (instant) { - group.figures.forEach((figure) => this.instantQueue.push(figure)); + this.groupInstantQueue.push(group); + group.figures.forEach((figure) => this.addFigure(figure, instant)); } else { this.groupStepsQueue.push(group); + group.figures.forEach((figure) => this.addFigure(figure, instant)); } } - togglePause() { - this.pause = !this.pause; + addAnimator(animator: Animator, instant = false) { + if (instant) { + this.animatorInstantQueue.push(animator); + animator.objects.forEach((object) => { + if (object instanceof Figure) { + this.addFigure(object, instant); + } else { + this.addGroup(object as Group, instant); + } + }); + } else { + this.animatorStepsQueue.push(animator); + animator.objects.forEach((object) => { + if (object instanceof Figure) { + this.addFigure(object, instant); + } else { + this.addGroup(object as Group, instant); + } + }); + } } removeFigure(figure: Figure) { @@ -180,6 +253,27 @@ class Main { toRemove?.figures.forEach((g) => this.removeFigure(g)); } + + removeAnimator(animator: Animator) { + let toRemove = null; + + this.animatorSnapshot = this.animatorSnapshot.filter((a) => { + if (a.id === animator.id) { + toRemove = a; + return false; + } + + return true; + }); + + toRemove?.objects.forEach((object) => { + if (object instanceof Figure) { + this.removeFigure(object); + } else { + this.removeGroup(object as Group); + } + }); + } } export default new Main(); From 632acdb9fb2fde0588519a14ff753a316b822c1b Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Wed, 25 Dec 2024 18:25:17 +0530 Subject: [PATCH 14/44] Small refactoring --- ee-workflow/src/index.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index dd2d3bba1..661b1744f 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * Internal dependencies. + */ import Animator from './components/animator'; import Box from './components/figure/box'; import Circle from './components/figure/circle'; @@ -23,7 +25,6 @@ import Group from './components/group'; import main from './main'; const timeline = new Line(0, 200, 1000, 200, 'black'); -main.addFigure(timeline, true); const circles = [ new Circle(100, 200, 75, 'gray'), @@ -31,7 +32,6 @@ const circles = [ new Circle(500, 200, 75, 'gray'), new Circle(700, 200, 75, 'gray'), ]; -circles.forEach((circle) => main.addFigure(circle, true)); const textonCircles = [ new Text(100, 75, '2024-01-01'), @@ -43,7 +43,6 @@ const textonCircles = [ new Text(700, 75, '2024-01-04'), new Text(700, 100, 'adv4.com'), ]; -textonCircles.forEach((text) => main.addFigure(text, true)); const circleToTextLine = [ new Line(100, 163, 100, 110, 'black'), @@ -51,9 +50,8 @@ const circleToTextLine = [ new Line(500, 163, 500, 110, 'black'), new Line(700, 163, 700, 110, 'black'), ]; -circleToTextLine.forEach((line) => main.addFigure(line, true)); -const firstCircleAnimations = [ +const advertiserFlow = [ new Line(95, 237, 95, 300, 'black', true), new Group([ new Box(50, 300, 100, 50, 'gray'), @@ -66,6 +64,12 @@ const firstCircleAnimations = [ new Text(170, 270, 'joinInterestGroup()', 12), ]; -main.addAnimator(new Animator(firstCircleAnimations)); +// Setup timeline. +main.addFigure(timeline, true); +circles.forEach((circle) => main.addFigure(circle, true)); +textonCircles.forEach((text) => main.addFigure(text, true)); +circleToTextLine.forEach((line) => main.addFigure(line, true)); +// Setup flow. +main.addAnimator(new Animator(advertiserFlow)); main.addFigure(new Line(0, 0, 0, 0)); From cab8c5b0572b90db852709545ebbcae558671ada Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Wed, 25 Dec 2024 18:42:10 +0530 Subject: [PATCH 15/44] Add documentation to the Main class --- ee-workflow/src/main.ts | 138 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index f74f85559..badee2277 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -13,31 +13,99 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * External dependencies. + */ import p5 from 'p5'; + +/** + * Internal dependencies. + */ import Figure from './components/figure'; import Group from './components/group'; import Animator from './components/animator'; +/** + * Main class responsible for managing the rendering and interaction of figures, + * groups, and animators using p5.js. + */ class Main { + /** + * p5 instance for rendering. + */ p5: p5; + + /** + * Queue for steps to be processed. + */ stepsQueue: Figure[] = []; + + /** + * Queue for group steps to be processed. + */ groupStepsQueue: Group[] = []; + + /** + * Queue for animator steps to be processed. + */ animatorStepsQueue: Animator[] = []; + + /** + * Queue for instant steps to be processed. + */ instantQueue: Figure[] = []; + + /** + * Queue for instant group steps to be processed. + */ groupInstantQueue: Group[] = []; + + /** + * Queue for instant animator steps to be processed. + */ animatorInstantQueue: Animator[] = []; + + /** + * Delay between each frame in milliseconds. + */ delay = 100; + + /** + * Flag to pause the drawing process. + */ pause = false; + + /** + * Snapshot of figures that have been drawn. + */ snapshot: Figure[] = []; + + /** + * Snapshot of groups that have been drawn. + */ groupSnapshot: Group[] = []; + + /** + * Snapshot of animators that have been drawn. + */ animatorSnapshot: Animator[] = []; + + /** + * Background color of the canvas. + */ backgroundColor = 245; + /** + * Creates an instance of Main and initializes the p5 instance. + */ constructor() { this.p5 = new p5(this.init.bind(this)); } + /** + * Initialize. + * @param p - The p5 instance. + */ private init(p: p5) { p.setup = this.setUp.bind(this); p.draw = this.draw.bind(this); @@ -45,16 +113,29 @@ class Main { p.mouseClicked = this.onClick.bind(this); } + /** + * Sets up the canvas. + */ private setUp() { this.p5.createCanvas(1600, 1600); this.p5.background(this.backgroundColor); } + /** + * Saves a figure to the snapshot and marks it as thrown. + * @param object - The figure to save. + */ private saveToSnapshot(object: Figure) { this.snapshot.push(object); object.throw = true; } + /** + * Processes a group of figures, drawing them and saving to snapshot if necessary. + * @param queue - The queue of figures. + * @param groupQueue - The queue of groups. + * @param shouldDraw - Whether to draw the group. + */ private processGroup( queue: Figure[], groupQueue: Group[], @@ -81,6 +162,10 @@ class Main { } } + /** + * Runs the drawing process for the current queue. + * @param useInstantQueue - Whether to use the instant queue. + */ private runner(useInstantQueue = false) { const queue = useInstantQueue ? this.instantQueue : this.stepsQueue; const groupQueue = useInstantQueue @@ -124,6 +209,9 @@ class Main { } } + /** + * Draws the current frame, processing the queues. + */ private draw() { if (this.pause) { return; @@ -138,6 +226,11 @@ class Main { } } + /** + * Checks if a figure is part of a group. + * @param object - The figure to check. + * @returns The group the figure belongs to, or undefined. + */ private isGrouped(object: Figure): Group | undefined { if (!object.gid) { return undefined; @@ -146,6 +239,9 @@ class Main { return this.groupSnapshot.find((group) => group.id === object.gid); } + /** + * Handles hover events for figures. + */ private onHover() { this.snapshot.forEach((object) => { const isHovering = object.isHovering(); @@ -159,6 +255,9 @@ class Main { }); } + /** + * Handles click events for figures. + */ private onClick() { this.snapshot.forEach((object) => { const isHovering = object.isHovering(); @@ -170,14 +269,24 @@ class Main { }); } + /** + * Gets the p5 instance. + * @returns The p5 instance. + */ getP5Instance() { return this.p5; } + /** + * Toggles the pause state. + */ togglePause() { this.pause = !this.pause; } + /** + * Redraws all figures on the canvas. + */ reDrawAll() { if (this.pause) { return; @@ -194,6 +303,11 @@ class Main { }); } + /** + * Adds a figure to the queue. + * @param figure - The figure to add. + * @param instant - Whether to add to the instant queue. + */ addFigure(figure: Figure, instant = false) { if (instant) { this.instantQueue.push(figure); @@ -202,6 +316,11 @@ class Main { } } + /** + * Adds a group to the queue. + * @param group - The group to add. + * @param instant - Whether to add to the instant queue. + */ addGroup(group: Group, instant = false) { if (instant) { this.groupInstantQueue.push(group); @@ -212,6 +331,11 @@ class Main { } } + /** + * Adds an animator to the queue. + * @param animator - The animator to add. + * @param instant - Whether to add to the instant queue. + */ addAnimator(animator: Animator, instant = false) { if (instant) { this.animatorInstantQueue.push(animator); @@ -234,11 +358,19 @@ class Main { } } + /** + * Removes a figure from the snapshot. + * @param figure - The figure to remove. + */ removeFigure(figure: Figure) { this.snapshot = this.snapshot.filter((f) => f.id !== figure.id); this.reDrawAll(); } + /** + * Removes a group from the snapshot. + * @param group - The group to remove. + */ removeGroup(group: Group) { let toRemove = null; @@ -254,6 +386,10 @@ class Main { toRemove?.figures.forEach((g) => this.removeFigure(g)); } + /** + * Removes an animator from the snapshot. + * @param animator - The animator to remove. + */ removeAnimator(animator: Animator) { let toRemove = null; From 1d38fd4fe2e25f7097b07fd6cf40a0672cc6dc1c Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 26 Dec 2024 10:42:24 +0530 Subject: [PATCH 16/44] Add dummy object as placeholder for the end of animation --- ee-workflow/src/components/animator.ts | 14 ++++++++++---- ee-workflow/src/main.ts | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts index f8195911d..d8ae05b31 100644 --- a/ee-workflow/src/components/animator.ts +++ b/ee-workflow/src/components/animator.ts @@ -14,7 +14,9 @@ * limitations under the License. */ +import main from '../main'; import Figure from './figure'; +import Line from './figure/line'; import Group from './group'; export default class Animator { @@ -30,18 +32,22 @@ export default class Animator { this.id = `animation-${Animator.animationCounter}` + Math.random().toString(36).slice(2, 9); - this.objects = objects; + this.objects = [ + ...objects, + new Line(0, 0, 0, 0, main.backgroundColor.toString()), // last dummy object acts as a placeholder for the end of the animation + ]; this.objects.forEach((object) => object.setAid(this.id)); } draw(): boolean { - if (this.index >= this.objects.length) { + this.objects[this.index].draw(); + this.index++; + + if (this.index === this.objects.length) { this.index = 0; return true; } - this.objects[this.index].draw(); - this.index++; return false; } } diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index badee2277..45e3a0639 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -297,6 +297,7 @@ class Main { this.stepsQueue = []; this.instantQueue = []; this.snapshot.forEach((figure) => { + // Fix if (!(figure instanceof Animator)) { this.instantQueue.push(figure); } From 956b1020448483b6cc4b3a894bd26eb32c715ebd Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 26 Dec 2024 10:44:02 +0530 Subject: [PATCH 17/44] Handle resetting of queues separately --- ee-workflow/src/index.ts | 3 ++- ee-workflow/src/main.ts | 51 +++++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 661b1744f..1b8535376 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -72,4 +72,5 @@ circleToTextLine.forEach((line) => main.addFigure(line, true)); // Setup flow. main.addAnimator(new Animator(advertiserFlow)); -main.addFigure(new Line(0, 0, 0, 0)); + +main.addGroup(new Group([new Line(200, 200, 300, 200, 'black')])); diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 45e3a0639..48f3faf2f 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -284,24 +284,59 @@ class Main { this.pause = !this.pause; } + /** + * Resets the queues and redraws all figures on the canvas. + */ + resetAndReDrawAll() { + if (this.pause) { + return; + } + + this.stepsQueue = []; + this.instantQueue = []; + this.groupStepsQueue = []; + this.groupInstantQueue = []; + this.animatorStepsQueue = []; + this.animatorInstantQueue = []; + this.reDrawAll(); + } + /** * Redraws all figures on the canvas. + * @param animatorIdToDraw - The ID of the animator to draw. */ - reDrawAll() { + reDrawAll(animatorIdToDraw?: string) { if (this.pause) { return; } this.p5.clear(); this.p5.background(this.backgroundColor); - this.stepsQueue = []; - this.instantQueue = []; - this.snapshot.forEach((figure) => { - // Fix - if (!(figure instanceof Animator)) { - this.instantQueue.push(figure); + for (let i = 0; i < this.snapshot.length; i++) { + const figure = this.snapshot[i]; + + if (figure.aid) { + const animator = this.animatorSnapshot.find((a) => a.id === figure.aid); + + if (animator && animatorIdToDraw === animator.id) { + this.addAnimator(animator, true); + } + + const toRemoveCount = (animator?.objects.length || 1) - 1; + i += toRemoveCount; + } else if (figure.gid) { + const group = this.groupSnapshot.find((g) => g.id === figure.gid); + + if (group) { + this.addGroup(group, true); + } + + const toRemoveCount = (group?.figures.length || 1) - 1; + i += toRemoveCount; + } else { + this.addFigure(figure, true); } - }); + } } /** From 921d911d706f3cec74648fd3d78b31336c5f8527 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 26 Dec 2024 11:20:22 +0530 Subject: [PATCH 18/44] Add docs --- ee-workflow/src/components/animator.ts | 32 +++++++ ee-workflow/src/components/figure/box.ts | 15 ++++ ee-workflow/src/components/figure/circle.ts | 11 +++ ee-workflow/src/components/figure/image.ts | 24 ++++- ee-workflow/src/components/figure/index.ts | 97 ++++++++++++++++++++- ee-workflow/src/components/figure/line.ts | 20 ++++- ee-workflow/src/components/figure/text.ts | 15 ++++ ee-workflow/src/components/group.ts | 41 +++++++++ 8 files changed, 251 insertions(+), 4 deletions(-) diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts index d8ae05b31..6c3b3cc9c 100644 --- a/ee-workflow/src/components/animator.ts +++ b/ee-workflow/src/components/animator.ts @@ -14,17 +14,44 @@ * limitations under the License. */ +/** + * Internal dependencies. + */ import main from '../main'; import Figure from './figure'; import Line from './figure/line'; import Group from './group'; +/** + * Class for creating animators. + * Contains logic for rendering objects in a sequence to create an animation. + */ export default class Animator { + /** + * Unique id of the animator. + */ id: string; + + /** + * Array of objects to be animated. Can be figures or groups. + * The last object in the array is a dummy object that acts as a placeholder for the end of the animation. + */ objects: Array
= []; + + /** + * Index of the current object being animated. + */ index = 0; + + /** + * Flag to indicate if the animation should be saved in animatorSnapshot. + * If true, the animation will NOT be saved in animatorSnapshot. + */ throw = false; + /** + * Counter for the number of animations created. + */ static animationCounter = 0; constructor(objects: Array
) { @@ -39,6 +66,11 @@ export default class Animator { this.objects.forEach((object) => object.setAid(this.id)); } + /** + * Draws the current object in the animation sequence, and increments the index. + * If the index reaches the end of the sequence, it resets the index to 0. + * @returns boolean indicating if the animation has finished + */ draw(): boolean { this.objects[this.index].draw(); this.index++; diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 51d832e76..f78a9527b 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -14,11 +14,26 @@ * limitations under the License. */ +/** + * Internal dependencies. + */ import Figure from '.'; import main from '../../main'; +/** + * Class for creating a box figure. + * Contains the properties and methods that a box should have. + * Extends the Figure class to inherit the basic properties and methods. + */ export default class Box extends Figure { + /** + * Width of the box. + */ width: number; + + /** + * Height of the box. + */ height: number; constructor( diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index 6aa3cd533..36a4df7e2 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -14,10 +14,21 @@ * limitations under the License. */ +/** + * Internal dependencies. + */ import Figure from '.'; import main from '../../main'; +/** + * Class for creating a circle figure. + * Contains the properties and methods that a circle should have. + * Extends the Figure class to inherit the basic properties and methods. + */ export default class Circle extends Figure { + /** + * Diameter of the circle. + */ diameter: number; constructor( diff --git a/ee-workflow/src/components/figure/image.ts b/ee-workflow/src/components/figure/image.ts index 24aab199f..9b4e2f9ff 100644 --- a/ee-workflow/src/components/figure/image.ts +++ b/ee-workflow/src/components/figure/image.ts @@ -13,14 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * External dependencies. + */ import p5 from 'p5'; + +/** + * Internal dependencies. + */ import Figure from '.'; import main from '../../main'; +/** + * Class for creating image figures. + * Contains the properties and methods that an image should have. + * Extends the Figure class to inherit the basic properties and methods. + */ export default class Image extends Figure { + /** + * Image to be displayed. + */ image: p5.Image; + + /** + * Width of the image. + */ width: number; + + /** + * Height of the image. + */ height: number; constructor( diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index f063f2114..5dba75c38 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -13,23 +13,80 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * External dependencies. + */ import p5 from 'p5'; + +/** + * Internal dependencies. + */ import main from '../../main'; +/** + * Abstract class for creating figures. + * Contains the basic properties and methods that all figures should have. + */ export default abstract class Figure { + /** + * p5 instance. + */ p5: p5 | null = null; + + /** + * Unique id of the figure. + */ id = ''; + + /** + * Group id of the figure if it belongs to a group. + */ gid = ''; + + /** + * Animator id of the figure if it belongs to an animator. + */ aid = ''; + + /** + * x coordinate of the figure. + */ x = 0; + + /** + * y coordinate of the figure. + */ y = 0; + + /** + * Fill color of the figure. + */ fill = 'black'; + + /** + * Stroke color of the figure. + */ stroke = 'black'; + + /** + * Previous fill color of the figure to be used when the figure is unhovered. + */ previousFill = ''; + + /** + * Previous stroke color of the figure to be used when the figure is unhovered. + */ previousStroke = ''; - throw?: boolean; // Property to determine if the object should be added to the snapshot + /** + * Property to determine if the object should be added to the snapshot. + * If true, the object will not be added to the snapshot. + */ + throw?: boolean; + + /** + * The number of objects created. + */ static objectCount = 0; constructor(x: number, y: number, fill?: string, stroke?: string) { @@ -45,28 +102,61 @@ export default abstract class Figure { this.previousStroke = this.stroke; } + /** + * Method to draw the figure. + */ abstract draw(): void; + /** + * Method to handle the hover event. + */ abstract onHover(): void; + /** + * Method to handle the leave event. + */ abstract onLeave(): void; + /** + * Method to handle the click event. + */ abstract onClick(): void; + /** + * Method to check if the figure is being hovered. + */ abstract isHovering(): boolean; + /** + * Method to remove the figure. + */ abstract remove(): void; + /** + * Method to redraw the figure. + * @param params - The parameters to redraw the figure with. + */ abstract reDraw(...params: Array): void; + /** + * Set the group id of the figure. + * @param gid - The group id to set. + */ setGid(gid: string) { this.gid = gid; } + /** + * Set the animator id of the figure. + * @param aid - The animator id to set. + */ setAid(aid: string) { this.aid = aid; } + /** + * Save the previous fill and stroke colors. + */ savePreviousColors() { if (this.previousFill === this.fill) { this.previousFill = this.fill; @@ -77,6 +167,9 @@ export default abstract class Figure { } } + /** + * Reapply the previous fill and stroke colors. + */ reApplyPreviousColors() { this.fill = this.previousFill; this.previousFill = this.fill; diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 355c9b2a3..31cb0b865 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -13,13 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/** + * Internal dependencies. + */ import Figure from '.'; import main from '../../main'; +/** + * Class for creating line figures. + * Contains the properties and methods that a line should have. + * Extends the Figure class to inherit the basic properties and methods. + */ export default class Line extends Figure { + /** + * End x coordinate of the line. + */ endX: number; + + /** + * End y coordinate of the line. + */ endY: number; + + /** + * Whether the line has an arrow at the end. + */ hasArrow: boolean; constructor( diff --git a/ee-workflow/src/components/figure/text.ts b/ee-workflow/src/components/figure/text.ts index 34136eb59..3e5e05d44 100644 --- a/ee-workflow/src/components/figure/text.ts +++ b/ee-workflow/src/components/figure/text.ts @@ -14,11 +14,26 @@ * limitations under the License. */ +/** + * Internal dependencies. + */ import Figure from '.'; import main from '../../main'; +/** + * Class for creating text figures. + * Contains the properties and methods that a text should have. + * Extends the Figure class to inherit the basic properties and methods. + */ export default class Text extends Figure { + /** + * Text to be displayed. + */ str: string; + + /** + * Font size of the text. + */ size: number; constructor(x: number, y: number, str: string, size?: number, fill?: string) { diff --git a/ee-workflow/src/components/group.ts b/ee-workflow/src/components/group.ts index e415e393e..628271402 100644 --- a/ee-workflow/src/components/group.ts +++ b/ee-workflow/src/components/group.ts @@ -14,14 +14,39 @@ * limitations under the License. */ +/** + * Internal dependencies. + */ import Figure from './figure'; +/** + * Class for creating a group of figures. + */ export default class Group { + /** + * Figures in the group. + */ figures: Figure[]; + + /** + * Unique id of the group. + */ id: string; + + /** + * Animator id of the group if it belongs to an animator. + */ aid = ''; + + /** + * Property to check if the group should be saved in groupSnapshot. + * If true, the group will NOT be saved in groupSnapshot. + */ throw = false; + /** + * Counter for the number of groups created. + */ static groupCount = 0; constructor(figures: Figure[]) { @@ -32,30 +57,46 @@ export default class Group { this.figures.forEach((figure) => figure.setGid(this.id)); } + /** + * Method to draw the group. Calls the draw method of each figure in the group. + */ draw() { this.figures.forEach((figure) => { figure.draw(); }); } + /** + * Method to handle hover event. Calls the onHover method of each figure in the group. + */ onHover() { this.figures.forEach((figure) => { figure.onHover(); }); } + /** + * Method to handle leave event. Calls the onLeave method of each figure in the group. + */ onLeave() { this.figures.forEach((figure) => { figure.onLeave(); }); } + /** + * Method to handle click event. Calls the onClick method of each figure in the group. + */ onClick() { this.figures.forEach((figure) => { figure.onClick(); }); } + /** + * Method to set the animator id to the group and its figures. + * @param aid - The animator id of the group. + */ setAid(aid: string) { this.aid = aid; this.figures.forEach((figure) => figure.setAid(aid)); From e956caa52775304fadcfeda851a99ec4a3077330 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 26 Dec 2024 11:43:34 +0530 Subject: [PATCH 19/44] Remove gitignore file --- ee-workflow/.gitignore | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 ee-workflow/.gitignore diff --git a/ee-workflow/.gitignore b/ee-workflow/.gitignore deleted file mode 100644 index a547bf36d..000000000 --- a/ee-workflow/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? From 883e5b2c6058855eb947e0134644e0b2fa1d2896 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 26 Dec 2024 11:46:43 +0530 Subject: [PATCH 20/44] Remove line --- ee-workflow/src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 1b8535376..d02a1fc9f 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -72,5 +72,3 @@ circleToTextLine.forEach((line) => main.addFigure(line, true)); // Setup flow. main.addAnimator(new Animator(advertiserFlow)); - -main.addGroup(new Group([new Line(200, 200, 300, 200, 'black')])); From 2ad18600bdfbd3213049389ff7556f69e195211e Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 26 Dec 2024 14:06:56 +0530 Subject: [PATCH 21/44] Add more circles and flow, fix calculation to redraw correct elements. fix image draw cb --- ee-workflow/src/components/figure/image.ts | 5 +- ee-workflow/src/index.ts | 108 +++++++++++++++++++-- ee-workflow/src/main.ts | 16 ++- 3 files changed, 118 insertions(+), 11 deletions(-) diff --git a/ee-workflow/src/components/figure/image.ts b/ee-workflow/src/components/figure/image.ts index 9b4e2f9ff..182745b16 100644 --- a/ee-workflow/src/components/figure/image.ts +++ b/ee-workflow/src/components/figure/image.ts @@ -48,18 +48,19 @@ export default class Image extends Figure { constructor( x: number, y: number, - image: p5.Image, + imageData: string, width: number, height: number ) { super(x, y); - this.image = image; + this.image = this.p5?.loadImage(imageData); this.width = width; this.height = height; } draw() { this.p5?.push(); + this.p5?.imageMode(this.p5?.CENTER); this.p5?.image(this.image, this.x, this.y, this.width, this.height); this.p5?.pop(); } diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index d02a1fc9f..240495094 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -19,6 +19,7 @@ import Animator from './components/animator'; import Box from './components/figure/box'; import Circle from './components/figure/circle'; +import Image from './components/figure/image'; import Line from './components/figure/line'; import Text from './components/figure/text'; import Group from './components/group'; @@ -27,10 +28,10 @@ import main from './main'; const timeline = new Line(0, 200, 1000, 200, 'black'); const circles = [ - new Circle(100, 200, 75, 'gray'), - new Circle(300, 200, 75, 'gray'), - new Circle(500, 200, 75, 'gray'), - new Circle(700, 200, 75, 'gray'), + new Circle(100, 200, 75, '#EDF2EF'), + new Circle(300, 200, 75, '#EDF2EF'), + new Circle(500, 200, 75, '#EDF2EF'), + new Circle(700, 200, 75, '#EDF2EF'), ]; const textonCircles = [ @@ -51,14 +52,20 @@ const circleToTextLine = [ new Line(700, 163, 700, 110, 'black'), ]; +const userIcon = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAn9JREFUeF7tmktywjAMhp2TFU4CM7DILUpvkQXMwEmanixFnZgmxi85knAbsQQntj79kmWZxqz806zcfqMAVAErJ6AhsHIBaBLUENAQWDkBDYFXCmB/bDfXc9e/cg3iCtgf29MwmDdjzMYxvG8a83U9dydJIGIAwNvDYN49hj/Z2zTmQwqECIDR62B89kcKAjuAEuMtJQkI7AB2h3YIuH2a/Nx88Hjknhe2nImSFUDI+z7PRpTS3y7dNjt2kANZAfi8H5P1mCg/XRtul45tnWwv9hmTE9O7QwsAZiHBGQacAGC/n2X+HAABcGx5QBpA0pBS5SBD/zfJlj6Yeq7Uk75kmKOc1HpCv3MqACo/N6ElM3oAQFI51QGABRXsAk95A97zJ3cBWDhFHcApf1gjWwhYSfq2NfcUGDsgcXpfBECouMmJWW7viwCIhUIMgoTxYgCsoZknQ2iMQD9ApFPEngNcLwME+M7pCv10g+65oJcy/HHkzonF/zxGXAG1wVQAUh6B7dDu92P8z6Yec4D9TiwXsClg0gUGo4Itr4QDbHI0XF1iUgBERgeZcNQGJAAwPX+KkKMEsRhARq1PYbP3HRQgFgFAGP+o6mzBAxbZomeaICdFUm7uSPYYoiV3qXsyjCcpaSeVY+xmqRhCkQISNT2J4T7HpOYtuT8oAhDwPpvhvvOE23GGc4QkgNl1F0UywoYi1f0BWgHSbesQGKrmKQkAkJ9TymIdih7v+5NFyQ0SFQC0ARwPKICCq3RVAFaKS7q82Lmw40VCABY1lq7Y9bGPL+knokOA3QrhCRSAMPDqplMFVOcS4QWpAoSBVzedKqA6lwgvSBUgDLy66VavgG9uL15QKQoHkQAAAABJRU5ErkJggg=='; +const completedIcon = + 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDhweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSI0OHB4IiBmaWxsPSIjNTk4NUUxIj48cGF0aCBkPSJNNDgwLTgwcS04NSAwLTE1OC0zMC41VDE5NS0xOTVxLTU0LTU0LTg0LjUtMTI3VDgwLTQ4MHEwLTg0IDMwLjUtMTU3VDE5NS03NjRxNTQtNTQgMTI3LTg1dDE1OC0zMXE3NSAwIDE0MCAyNHQxMTcgNjZsLTQzIDQzcS00NC0zNS05OC01NHQtMTE2LTE5cS0xNDUgMC0yNDIuNSA5Ny41VDE0MC00ODBxMCAxNDUgOTcuNSAyNDIuNVQ0ODAtMTQwcTE0NSAwIDI0Mi41LTk3LjVUODIwLTQ4MHEwLTMwLTQuNS01OC41VDgwMi01OTRsNDYtNDZxMTYgMzcgMjQgNzd0OCA4M3EwIDg1LTMxIDE1OHQtODUgMTI3cS01NCA1NC0xMjcgODQuNVQ0ODAtODBabS01OS0yMThMMjU2LTQ2NGw0NS00NSAxMjAgMTIwIDQxNC00MTQgNDYgNDUtNDYwIDQ2MFoiLz48L3N2Zz4='; + const advertiserFlow = [ + new Image(100, 200, userIcon, 50, 50), new Line(95, 237, 95, 300, 'black', true), new Group([ - new Box(50, 300, 100, 50, 'gray'), + new Box(50, 300, 100, 50, '#EDF2EF'), new Text(100, 325, 'DSP tags'), ]), new Line(95, 350, 95, 413, 'black', true), - new Group([new Box(50, 413, 100, 50, 'gray'), new Text(100, 438, 'DSPs')]), + new Group([new Box(50, 413, 100, 50, '#EDF2EF'), new Text(100, 438, 'DSPs')]), new Line(105, 413, 105, 350, 'black', true), new Line(105, 300, 105, 237, 'black', true), new Text(170, 270, 'joinInterestGroup()', 12), @@ -72,3 +79,92 @@ circleToTextLine.forEach((line) => main.addFigure(line, true)); // Setup flow. main.addAnimator(new Animator(advertiserFlow)); + +main.addGroup( + new Group([ + new Line(0, 200, 63, 200, 'blue'), + new Circle(100, 200, 75, '#EDF2EF', 'blue'), + new Image(100, 200, completedIcon, 50, 50), + ]) +); + +const secondCircleAnimations = [ + new Image(300, 200, userIcon, 50, 50), + new Line(295, 237, 295, 300, 'black', true), + new Group([ + new Box(250, 300, 100, 50, '#EDF2EF'), + new Text(300, 325, 'DSP tags'), + ]), + new Line(295, 350, 295, 413, 'black', true), + new Group([ + new Box(250, 413, 100, 50, '#EDF2EF'), + new Text(300, 438, 'DSPs'), + ]), + new Line(305, 413, 305, 350, 'black', true), + new Line(305, 300, 305, 237, 'black', true), + new Text(370, 270, 'joinInterestGroup()', 12), +]; + +main.addAnimator(new Animator(secondCircleAnimations)); + +main.addGroup( + new Group([ + new Line(137, 200, 263, 200, 'blue'), + new Circle(300, 200, 75, '#EDF2EF', 'blue'), + new Image(300, 200, completedIcon, 50, 50), + ]) +); + +const thirdCircleAnimations = [ + new Image(500, 200, userIcon, 50, 50), + new Line(495, 237, 495, 300, 'black', true), + new Group([ + new Box(450, 300, 100, 50, '#EDF2EF'), + new Text(500, 325, 'DSP tags'), + ]), + new Line(495, 350, 495, 413, 'black', true), + new Group([ + new Box(450, 413, 100, 50, '#EDF2EF'), + new Text(500, 438, 'DSPs'), + ]), + new Line(505, 413, 505, 350, 'black', true), + new Line(505, 300, 505, 237, 'black', true), + new Text(570, 270, 'joinInterestGroup()', 12), +]; + +main.addAnimator(new Animator(thirdCircleAnimations)); + +main.addGroup( + new Group([ + new Line(337, 200, 463, 200, 'blue'), + new Circle(500, 200, 75, '#EDF2EF', 'blue'), + new Image(500, 200, completedIcon, 50, 50), + ]) +); + +const fourthCircleAnimations = [ + new Image(700, 200, userIcon, 50, 50), + new Line(695, 237, 695, 300, 'black', true), + new Group([ + new Box(650, 300, 100, 50, '#EDF2EF'), + new Text(700, 325, 'DSP tags'), + ]), + new Line(695, 350, 695, 413, 'black', true), + new Group([ + new Box(650, 413, 100, 50, '#EDF2EF'), + new Text(700, 438, 'DSPs'), + ]), + new Line(705, 413, 705, 350, 'black', true), + new Line(705, 300, 705, 237, 'black', true), + new Text(770, 270, 'joinInterestGroup()', 12), +]; + +main.addAnimator(new Animator(fourthCircleAnimations)); + +main.addGroup( + new Group([ + new Line(537, 200, 663, 200, 'blue'), + new Circle(700, 200, 75, '#EDF2EF', 'blue'), + new Image(700, 200, completedIcon, 50, 50), + ]) +); diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 48f3faf2f..580d0bcea 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -68,7 +68,7 @@ class Main { /** * Delay between each frame in milliseconds. */ - delay = 100; + delay = 50; /** * Flag to pause the drawing process. @@ -322,8 +322,18 @@ class Main { this.addAnimator(animator, true); } - const toRemoveCount = (animator?.objects.length || 1) - 1; - i += toRemoveCount; + const toRemoveCount = + animator?.objects.reduce((acc, object) => { + if (object instanceof Figure) { + acc++; + } else { + acc += object.figures.length; + } + + return acc; + }, 0) || 1; + + i += toRemoveCount - 1; } else if (figure.gid) { const group = this.groupSnapshot.find((g) => g.id === figure.gid); From 6ac43a5d5dfd941c4c198f54a065d51f44745939 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 26 Dec 2024 15:03:40 +0530 Subject: [PATCH 22/44] Call runner if animator ends --- ee-workflow/src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 580d0bcea..4141d2206 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -195,6 +195,7 @@ class Main { if (isDone) { this.animatorSnapshot.push(animator); animatorQueue.shift(); + this.runner(useInstantQueue); this.reDrawAll(); } } From 6dad0d6e67b38e5de198925dc9e8f57ee2e63bb8 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 31 Dec 2024 16:56:07 +0530 Subject: [PATCH 23/44] Add factory class for figures, add IG bubbles and add sideEffect --- ee-workflow/src/components/animator.ts | 29 ++- ee-workflow/src/components/figure/box.ts | 15 +- ee-workflow/src/components/figure/circle.ts | 15 +- .../src/components/figure/figureFactory.ts | 79 ++++++ ee-workflow/src/components/figure/image.ts | 7 +- ee-workflow/src/components/figure/index.ts | 18 +- ee-workflow/src/components/figure/line.ts | 13 +- ee-workflow/src/components/figure/text.ts | 19 +- ee-workflow/src/components/index.ts | 18 ++ ee-workflow/src/index.ts | 237 +++++++++++------- ee-workflow/src/main.ts | 29 ++- 11 files changed, 344 insertions(+), 135 deletions(-) create mode 100644 ee-workflow/src/components/figure/figureFactory.ts create mode 100644 ee-workflow/src/components/index.ts diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts index 6c3b3cc9c..9ebde063e 100644 --- a/ee-workflow/src/components/animator.ts +++ b/ee-workflow/src/components/animator.ts @@ -17,9 +17,8 @@ /** * Internal dependencies. */ -import main from '../main'; import Figure from './figure'; -import Line from './figure/line'; +import FigureFactory from './figure/figureFactory'; import Group from './group'; /** @@ -49,19 +48,24 @@ export default class Animator { */ throw = false; + /** + * Function to be executed when the animation ends. + */ + sideEffectOnEnd: (() => void) | undefined; + /** * Counter for the number of animations created. */ static animationCounter = 0; - constructor(objects: Array
) { + constructor(objects: Array
, figureFactory: FigureFactory) { Animator.animationCounter++; this.id = `animation-${Animator.animationCounter}` + Math.random().toString(36).slice(2, 9); this.objects = [ ...objects, - new Line(0, 0, 0, 0, main.backgroundColor.toString()), // last dummy object acts as a placeholder for the end of the animation + figureFactory.line(0, 0, 0, 0), // last dummy object acts as a placeholder for the end of the animation ]; this.objects.forEach((object) => object.setAid(this.id)); } @@ -72,14 +76,23 @@ export default class Animator { * @returns boolean indicating if the animation has finished */ draw(): boolean { - this.objects[this.index].draw(); - this.index++; - - if (this.index === this.objects.length) { + if (this.index === this.objects.length - 1) { this.index = 0; + this.sideEffectOnEnd?.(); return true; } + this.objects[this.index].draw(); + this.index++; + return false; } + + /** + * Sets a side effect to be executed when the animation ends + * @param sideEffect - function to be executed when the animation ends + */ + setSideEffectOnEnd(sideEffect: () => void) { + this.sideEffectOnEnd = sideEffect; + } } diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index f78a9527b..007daf407 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -18,7 +18,7 @@ * Internal dependencies. */ import Figure from '.'; -import main from '../../main'; +import Main from '../../main'; /** * Class for creating a box figure. @@ -37,6 +37,7 @@ export default class Box extends Figure { height: number; constructor( + canvasRuuner: Main, x: number, y: number, width: number, @@ -44,7 +45,7 @@ export default class Box extends Figure { fill?: string, stroke?: string ) { - super(x, y, fill, stroke); + super(canvasRuuner, x, y, fill, stroke); this.width = width; this.height = height; } @@ -60,7 +61,7 @@ export default class Box extends Figure { onHover() { this.savePreviousColors(); this.fill = 'red'; // TODO: Discuss the function - main.addFigure(this, true); + this.canvasRunner.addFigure(this, true); } onLeave() { @@ -72,7 +73,7 @@ export default class Box extends Figure { } this.reApplyPreviousColors(); - main.addFigure(this, true); + this.canvasRunner.addFigure(this, true); } onClick() { @@ -94,8 +95,8 @@ export default class Box extends Figure { remove() { this.p5?.push(); - this.p5?.fill(main.backgroundColor); - this.p5?.stroke(main.backgroundColor); + this.p5?.fill(this.canvasRunner.backgroundColor); + this.p5?.stroke(this.canvasRunner.backgroundColor); this.p5?.rect(this.x, this.y, this.width, this.height); this.p5?.pop(); } @@ -115,6 +116,6 @@ export default class Box extends Figure { this.height = height ?? this.height; this.fill = fill || this.fill; this.stroke = stroke || this.stroke; - main.reDrawAll(); + this.canvasRunner.reDrawAll(); } } diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index 36a4df7e2..c4f5c99b1 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -18,7 +18,7 @@ * Internal dependencies. */ import Figure from '.'; -import main from '../../main'; +import Main from '../../main'; /** * Class for creating a circle figure. @@ -32,13 +32,14 @@ export default class Circle extends Figure { diameter: number; constructor( + canvasRunner: Main, x: number, y: number, diameter: number, fill?: string, stroke?: string ) { - super(x, y, fill, stroke); + super(canvasRunner, x, y, fill, stroke); this.diameter = diameter; } @@ -53,7 +54,7 @@ export default class Circle extends Figure { onHover() { this.savePreviousColors(); this.fill = 'red'; // TODO: Discuss the function - main.addFigure(this, true); + this.canvasRunner.addFigure(this, true); } onLeave() { @@ -65,7 +66,7 @@ export default class Circle extends Figure { } this.reApplyPreviousColors(); - main.addFigure(this, true); + this.canvasRunner.addFigure(this, true); } onClick() { @@ -85,8 +86,8 @@ export default class Circle extends Figure { remove() { this.p5?.push(); - this.p5?.fill(main.backgroundColor); - this.p5?.stroke(main.backgroundColor); + this.p5?.fill(this.canvasRunner.backgroundColor); + this.p5?.stroke(this.canvasRunner.backgroundColor); this.p5?.circle(this.x, this.y, this.diameter + 1); this.p5?.pop(); } @@ -104,6 +105,6 @@ export default class Circle extends Figure { this.diameter = diameter ?? this.diameter; this.fill = fill || this.fill; this.stroke = stroke || this.stroke; - main.reDrawAll(); + this.canvasRunner.reDrawAll(); } } diff --git a/ee-workflow/src/components/figure/figureFactory.ts b/ee-workflow/src/components/figure/figureFactory.ts new file mode 100644 index 000000000..cd2fbe7f7 --- /dev/null +++ b/ee-workflow/src/components/figure/figureFactory.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Internal dependencies. + */ +import Main from '../../main'; +import Box from './box'; +import Circle from './circle'; +import Image from './image'; +import Line from './line'; +import Text from './text'; + +export default class FigureFactory { + private canvasRunner: Main; + + constructor(canvasRunner: Main) { + this.canvasRunner = canvasRunner; + } + + box( + x: number, + y: number, + width: number, + height: number, + fill?: string, + stroke?: string + ): Box { + return new Box(this.canvasRunner, x, y, width, height, fill, stroke); + } + + circle( + x: number, + y: number, + diameter: number, + fill?: string, + stroke?: string + ): Circle { + return new Circle(this.canvasRunner, x, y, diameter, fill, stroke); + } + + image( + x: number, + y: number, + imageData: string, + width: number, + height: number + ): Image { + return new Image(this.canvasRunner, x, y, imageData, width, height); + } + + line( + x: number, + y: number, + endX: number, + endY: number, + stroke?: string, + hasArrow?: boolean + ): Line { + return new Line(this.canvasRunner, x, y, endX, endY, stroke, hasArrow); + } + + text(x: number, y: number, text: string, size?: number, fill?: string): Text { + return new Text(this.canvasRunner, x, y, text, size, fill); + } +} diff --git a/ee-workflow/src/components/figure/image.ts b/ee-workflow/src/components/figure/image.ts index 182745b16..f54bc8ce2 100644 --- a/ee-workflow/src/components/figure/image.ts +++ b/ee-workflow/src/components/figure/image.ts @@ -22,7 +22,7 @@ import p5 from 'p5'; * Internal dependencies. */ import Figure from '.'; -import main from '../../main'; +import Main from '../../main'; /** * Class for creating image figures. @@ -46,13 +46,14 @@ export default class Image extends Figure { height: number; constructor( + canvasRunner: Main, x: number, y: number, imageData: string, width: number, height: number ) { - super(x, y); + super(canvasRunner, x, y); this.image = this.p5?.loadImage(imageData); this.width = width; this.height = height; @@ -107,6 +108,6 @@ export default class Image extends Figure { this.image = image || this.image; this.width = width ?? this.width; this.height = height ?? this.height; - main.reDrawAll(); + this.canvasRunner.reDrawAll(); } } diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 5dba75c38..26ff9dbaa 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -21,7 +21,7 @@ import p5 from 'p5'; /** * Internal dependencies. */ -import main from '../../main'; +import Main from '../../main'; /** * Abstract class for creating figures. @@ -33,6 +33,11 @@ export default abstract class Figure { */ p5: p5 | null = null; + /** + * Main class object that runs the canvas. + */ + canvasRunner: Main; + /** * Unique id of the figure. */ @@ -89,11 +94,18 @@ export default abstract class Figure { */ static objectCount = 0; - constructor(x: number, y: number, fill?: string, stroke?: string) { + constructor( + canvasRunner: Main, + x: number, + y: number, + fill?: string, + stroke?: string + ) { Figure.objectCount++; this.id = `object-${Figure.objectCount}` + Math.random().toString(36).slice(2, 9); - this.p5 = main.getP5Instance(); + this.canvasRunner = canvasRunner; + this.p5 = this.canvasRunner.getP5Instance(); this.x = x; this.y = y; this.fill = fill || 'black'; diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 31cb0b865..2e216a1f9 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -17,7 +17,7 @@ * Internal dependencies. */ import Figure from '.'; -import main from '../../main'; +import Main from '../../main'; /** * Class for creating line figures. @@ -41,6 +41,7 @@ export default class Line extends Figure { hasArrow: boolean; constructor( + canvasRunner: Main, x: number, y: number, endX: number, @@ -48,7 +49,7 @@ export default class Line extends Figure { stroke?: string, hasArrow?: boolean ) { - super(x, y, undefined, stroke); + super(canvasRunner, x, y, undefined, stroke); this.endX = endX; this.endY = endY; this.hasArrow = hasArrow ?? false; @@ -77,7 +78,7 @@ export default class Line extends Figure { onHover() { this.savePreviousColors(); this.stroke = 'red'; // TODO: Discuss the function - main.addFigure(this, true); + this.canvasRunner.addFigure(this, true); } onLeave() { @@ -89,7 +90,7 @@ export default class Line extends Figure { } this.reApplyPreviousColors(); - main.addFigure(this, true); + this.canvasRunner.addFigure(this, true); } onClick() { @@ -102,7 +103,7 @@ export default class Line extends Figure { remove() { this.p5?.push(); - this.p5?.stroke(main.backgroundColor); + this.p5?.stroke(this.canvasRunner.backgroundColor); this.p5?.line(this.x, this.y, this.endX, this.endY); this.p5?.pop(); } @@ -120,6 +121,6 @@ export default class Line extends Figure { this.endX = endX ?? this.endX; this.endY = endY ?? this.endY; this.stroke = stroke || this.stroke; - main.reDrawAll(); + this.canvasRunner.reDrawAll(); } } diff --git a/ee-workflow/src/components/figure/text.ts b/ee-workflow/src/components/figure/text.ts index 3e5e05d44..c127a2ce4 100644 --- a/ee-workflow/src/components/figure/text.ts +++ b/ee-workflow/src/components/figure/text.ts @@ -18,7 +18,7 @@ * Internal dependencies. */ import Figure from '.'; -import main from '../../main'; +import Main from '../../main'; /** * Class for creating text figures. @@ -36,8 +36,15 @@ export default class Text extends Figure { */ size: number; - constructor(x: number, y: number, str: string, size?: number, fill?: string) { - super(x, y, fill); + constructor( + canvasRunnner: Main, + x: number, + y: number, + str: string, + size?: number, + fill?: string + ) { + super(canvasRunnner, x, y, fill); this.str = str; this.size = size || 16; } @@ -69,8 +76,8 @@ export default class Text extends Figure { remove() { this.p5?.push(); - this.p5?.fill(main.backgroundColor); - this.p5?.stroke(main.backgroundColor); + this.p5?.fill(this.canvasRunner.backgroundColor); + this.p5?.stroke(this.canvasRunner.backgroundColor); this.p5?.textSize(this.size); this.p5?.text(this.str, this.x, this.y); this.p5?.pop(); @@ -91,6 +98,6 @@ export default class Text extends Figure { this.size = size ?? this.size; this.fill = fill || this.fill; this.stroke = stroke || this.stroke; - main.reDrawAll(); + this.canvasRunner.reDrawAll(); } } diff --git a/ee-workflow/src/components/index.ts b/ee-workflow/src/components/index.ts new file mode 100644 index 000000000..243292f19 --- /dev/null +++ b/ee-workflow/src/components/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as FigureFactory } from './figure/figureFactory'; +export { default as Group } from './group'; +export { default as Animator } from './animator'; diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 240495094..8bbea9640 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -16,155 +16,212 @@ /** * Internal dependencies. */ -import Animator from './components/animator'; -import Box from './components/figure/box'; -import Circle from './components/figure/circle'; -import Image from './components/figure/image'; -import Line from './components/figure/line'; -import Text from './components/figure/text'; -import Group from './components/group'; -import main from './main'; +import { Animator, FigureFactory, Group } from './components'; +import Main from './main'; -const timeline = new Line(0, 200, 1000, 200, 'black'); +const mainCanvas = new Main(); +const mainFF = new FigureFactory(mainCanvas); + +const IGCanvas = new Main(false); +IGCanvas.delay = 1; +const IGFF = new FigureFactory(IGCanvas); +IGCanvas.togglePause(); + +const timeline = mainFF.line(0, 200, 1000, 200, 'black'); const circles = [ - new Circle(100, 200, 75, '#EDF2EF'), - new Circle(300, 200, 75, '#EDF2EF'), - new Circle(500, 200, 75, '#EDF2EF'), - new Circle(700, 200, 75, '#EDF2EF'), + mainFF.circle(100, 200, 75, '#EDF2EF'), + mainFF.circle(300, 200, 75, '#EDF2EF'), + mainFF.circle(500, 200, 75, '#EDF2EF'), + mainFF.circle(700, 200, 75, '#EDF2EF'), ]; const textonCircles = [ - new Text(100, 75, '2024-01-01'), - new Text(100, 100, 'adv1.com'), - new Text(300, 75, '2024-01-02'), - new Text(300, 100, 'adv2.com'), - new Text(500, 75, '2024-01-03'), - new Text(500, 100, 'adv3.com'), - new Text(700, 75, '2024-01-04'), - new Text(700, 100, 'adv4.com'), + mainFF.text(100, 75, '2024-01-01'), + mainFF.text(100, 100, 'adv1.com'), + mainFF.text(300, 75, '2024-01-02'), + mainFF.text(300, 100, 'adv2.com'), + mainFF.text(500, 75, '2024-01-03'), + mainFF.text(500, 100, 'adv3.com'), + mainFF.text(700, 75, '2024-01-04'), + mainFF.text(700, 100, 'adv4.com'), ]; const circleToTextLine = [ - new Line(100, 163, 100, 110, 'black'), - new Line(300, 163, 300, 110, 'black'), - new Line(500, 163, 500, 110, 'black'), - new Line(700, 163, 700, 110, 'black'), + mainFF.line(100, 163, 100, 110, 'black'), + mainFF.line(300, 163, 300, 110, 'black'), + mainFF.line(500, 163, 500, 110, 'black'), + mainFF.line(700, 163, 700, 110, 'black'), ]; const userIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAn9JREFUeF7tmktywjAMhp2TFU4CM7DILUpvkQXMwEmanixFnZgmxi85knAbsQQntj79kmWZxqz806zcfqMAVAErJ6AhsHIBaBLUENAQWDkBDYFXCmB/bDfXc9e/cg3iCtgf29MwmDdjzMYxvG8a83U9dydJIGIAwNvDYN49hj/Z2zTmQwqECIDR62B89kcKAjuAEuMtJQkI7AB2h3YIuH2a/Nx88Hjknhe2nImSFUDI+z7PRpTS3y7dNjt2kANZAfi8H5P1mCg/XRtul45tnWwv9hmTE9O7QwsAZiHBGQacAGC/n2X+HAABcGx5QBpA0pBS5SBD/zfJlj6Yeq7Uk75kmKOc1HpCv3MqACo/N6ElM3oAQFI51QGABRXsAk95A97zJ3cBWDhFHcApf1gjWwhYSfq2NfcUGDsgcXpfBECouMmJWW7viwCIhUIMgoTxYgCsoZknQ2iMQD9ApFPEngNcLwME+M7pCv10g+65oJcy/HHkzonF/zxGXAG1wVQAUh6B7dDu92P8z6Yec4D9TiwXsClg0gUGo4Itr4QDbHI0XF1iUgBERgeZcNQGJAAwPX+KkKMEsRhARq1PYbP3HRQgFgFAGP+o6mzBAxbZomeaICdFUm7uSPYYoiV3qXsyjCcpaSeVY+xmqRhCkQISNT2J4T7HpOYtuT8oAhDwPpvhvvOE23GGc4QkgNl1F0UywoYi1f0BWgHSbesQGKrmKQkAkJ9TymIdih7v+5NFyQ0SFQC0ARwPKICCq3RVAFaKS7q82Lmw40VCABY1lq7Y9bGPL+knokOA3QrhCRSAMPDqplMFVOcS4QWpAoSBVzedKqA6lwgvSBUgDLy66VavgG9uL15QKQoHkQAAAABJRU5ErkJggg=='; + const completedIcon = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDhweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSI0OHB4IiBmaWxsPSIjNTk4NUUxIj48cGF0aCBkPSJNNDgwLTgwcS04NSAwLTE1OC0zMC41VDE5NS0xOTVxLTU0LTU0LTg0LjUtMTI3VDgwLTQ4MHEwLTg0IDMwLjUtMTU3VDE5NS03NjRxNTQtNTQgMTI3LTg1dDE1OC0zMXE3NSAwIDE0MCAyNHQxMTcgNjZsLTQzIDQzcS00NC0zNS05OC01NHQtMTE2LTE5cS0xNDUgMC0yNDIuNSA5Ny41VDE0MC00ODBxMCAxNDUgOTcuNSAyNDIuNVQ0ODAtMTQwcTE0NSAwIDI0Mi41LTk3LjVUODIwLTQ4MHEwLTMwLTQuNS01OC41VDgwMi01OTRsNDYtNDZxMTYgMzcgMjQgNzd0OCA4M3EwIDg1LTMxIDE1OHQtODUgMTI3cS01NCA1NC0xMjcgODQuNVQ0ODAtODBabS01OS0yMThMMjU2LTQ2NGw0NS00NSAxMjAgMTIwIDQxNC00MTQgNDYgNDUtNDYwIDQ2MFoiLz48L3N2Zz4='; const advertiserFlow = [ - new Image(100, 200, userIcon, 50, 50), - new Line(95, 237, 95, 300, 'black', true), + mainFF.image(100, 200, userIcon, 50, 50), + mainFF.line(95, 237, 95, 300, 'black', true), new Group([ - new Box(50, 300, 100, 50, '#EDF2EF'), - new Text(100, 325, 'DSP tags'), + mainFF.box(50, 300, 100, 50, '#EDF2EF'), + mainFF.text(100, 325, 'DSP tags'), ]), - new Line(95, 350, 95, 413, 'black', true), - new Group([new Box(50, 413, 100, 50, '#EDF2EF'), new Text(100, 438, 'DSPs')]), - new Line(105, 413, 105, 350, 'black', true), - new Line(105, 300, 105, 237, 'black', true), - new Text(170, 270, 'joinInterestGroup()', 12), + mainFF.line(95, 350, 95, 413, 'black', true), + new Group([ + mainFF.box(50, 413, 100, 50, '#EDF2EF'), + mainFF.text(100, 438, 'DSPs'), + ]), + mainFF.line(105, 413, 105, 350, 'black', true), + mainFF.line(105, 300, 105, 237, 'black', true), + mainFF.text(170, 270, 'joinInterestGroup()', 12), ]; // Setup timeline. -main.addFigure(timeline, true); -circles.forEach((circle) => main.addFigure(circle, true)); -textonCircles.forEach((text) => main.addFigure(text, true)); -circleToTextLine.forEach((line) => main.addFigure(line, true)); +mainCanvas.addFigure(timeline, true); +circles.forEach((circle) => mainCanvas.addFigure(circle, true)); +textonCircles.forEach((text) => mainCanvas.addFigure(text, true)); +circleToTextLine.forEach((line) => mainCanvas.addFigure(line, true)); + +// add IG bubble canvas here +// pause the main canvas operations +// add elements to IG canvas +const bubbles = []; +let startX = 170; +let startY = 270; +while (startX > 0 && startY > 0) { + const circle = IGFF.circle(startX, startY, 10, 'orange', 'black'); + bubbles.push(circle); + startX -= 1; + startY -= 1; +} + +const bubbleFlow = new Animator(bubbles, mainFF); +bubbleFlow.setSideEffectOnEnd(() => { + IGCanvas.resetAndReDrawAll(); + IGCanvas.togglePause(); + mainCanvas.togglePause(); + mainCanvas.reDrawAll(); +}); // Setup flow. -main.addAnimator(new Animator(advertiserFlow)); - -main.addGroup( +const flow = new Animator(advertiserFlow, mainFF); +flow.setSideEffectOnEnd(() => { + IGCanvas.addAnimator(bubbleFlow); + mainCanvas.togglePause(); + IGCanvas.togglePause(); +}); +mainCanvas.addAnimator(flow); + +mainCanvas.addGroup( new Group([ - new Line(0, 200, 63, 200, 'blue'), - new Circle(100, 200, 75, '#EDF2EF', 'blue'), - new Image(100, 200, completedIcon, 50, 50), + mainFF.line(0, 200, 63, 200, 'blue'), + mainFF.circle(100, 200, 75, '#EDF2EF', 'blue'), + mainFF.image(100, 200, completedIcon, 50, 50), ]) ); const secondCircleAnimations = [ - new Image(300, 200, userIcon, 50, 50), - new Line(295, 237, 295, 300, 'black', true), + mainFF.image(300, 200, userIcon, 50, 50), + mainFF.line(295, 237, 295, 300, 'black', true), new Group([ - new Box(250, 300, 100, 50, '#EDF2EF'), - new Text(300, 325, 'DSP tags'), + mainFF.box(250, 300, 100, 50, '#EDF2EF'), + mainFF.text(300, 325, 'DSP tags'), ]), - new Line(295, 350, 295, 413, 'black', true), + mainFF.line(295, 350, 295, 413, 'black', true), new Group([ - new Box(250, 413, 100, 50, '#EDF2EF'), - new Text(300, 438, 'DSPs'), + mainFF.box(250, 413, 100, 50, '#EDF2EF'), + mainFF.text(300, 438, 'DSPs'), ]), - new Line(305, 413, 305, 350, 'black', true), - new Line(305, 300, 305, 237, 'black', true), - new Text(370, 270, 'joinInterestGroup()', 12), + mainFF.line(305, 413, 305, 350, 'black', true), + mainFF.line(305, 300, 305, 237, 'black', true), + mainFF.text(370, 270, 'joinInterestGroup()', 12), ]; -main.addAnimator(new Animator(secondCircleAnimations)); - -main.addGroup( +const secondBubbles = []; +let secondStartX = 370; +let secondStartY = 270; +while (secondStartX > 0 && secondStartY > 0) { + const circle = IGFF.circle(secondStartX, secondStartY, 10, 'orange', 'black'); + secondBubbles.push(circle); + secondStartX -= 1; + secondStartY -= 1; +} + +const secondBubbleFlow = new Animator(secondBubbles, mainFF); +secondBubbleFlow.setSideEffectOnEnd(() => { + IGCanvas.resetAndReDrawAll(); + IGCanvas.togglePause(); + mainCanvas.togglePause(); + mainCanvas.reDrawAll(); +}); + +const secondFlow = new Animator(secondCircleAnimations, mainFF); +secondFlow.setSideEffectOnEnd(() => { + IGCanvas.addAnimator(secondBubbleFlow); + IGCanvas.togglePause(); + mainCanvas.togglePause(); +}); +mainCanvas.addAnimator(secondFlow); + +mainCanvas.addGroup( new Group([ - new Line(137, 200, 263, 200, 'blue'), - new Circle(300, 200, 75, '#EDF2EF', 'blue'), - new Image(300, 200, completedIcon, 50, 50), + mainFF.line(137, 200, 263, 200, 'blue'), + mainFF.circle(300, 200, 75, '#EDF2EF', 'blue'), + mainFF.image(300, 200, completedIcon, 50, 50), ]) ); const thirdCircleAnimations = [ - new Image(500, 200, userIcon, 50, 50), - new Line(495, 237, 495, 300, 'black', true), + mainFF.image(500, 200, userIcon, 50, 50), + mainFF.line(495, 237, 495, 300, 'black', true), new Group([ - new Box(450, 300, 100, 50, '#EDF2EF'), - new Text(500, 325, 'DSP tags'), + mainFF.box(450, 300, 100, 50, '#EDF2EF'), + mainFF.text(500, 325, 'DSP tags'), ]), - new Line(495, 350, 495, 413, 'black', true), + mainFF.line(495, 350, 495, 413, 'black', true), new Group([ - new Box(450, 413, 100, 50, '#EDF2EF'), - new Text(500, 438, 'DSPs'), + mainFF.box(450, 413, 100, 50, '#EDF2EF'), + mainFF.text(500, 438, 'DSPs'), ]), - new Line(505, 413, 505, 350, 'black', true), - new Line(505, 300, 505, 237, 'black', true), - new Text(570, 270, 'joinInterestGroup()', 12), + mainFF.line(505, 413, 505, 350, 'black', true), + mainFF.line(505, 300, 505, 237, 'black', true), + mainFF.text(570, 270, 'joinInterestGroup()', 12), ]; -main.addAnimator(new Animator(thirdCircleAnimations)); +mainCanvas.addAnimator(new Animator(thirdCircleAnimations, mainFF)); -main.addGroup( +mainCanvas.addGroup( new Group([ - new Line(337, 200, 463, 200, 'blue'), - new Circle(500, 200, 75, '#EDF2EF', 'blue'), - new Image(500, 200, completedIcon, 50, 50), + mainFF.line(337, 200, 463, 200, 'blue'), + mainFF.circle(500, 200, 75, '#EDF2EF', 'blue'), + mainFF.image(500, 200, completedIcon, 50, 50), ]) ); const fourthCircleAnimations = [ - new Image(700, 200, userIcon, 50, 50), - new Line(695, 237, 695, 300, 'black', true), + mainFF.image(700, 200, userIcon, 50, 50), + mainFF.line(695, 237, 695, 300, 'black', true), new Group([ - new Box(650, 300, 100, 50, '#EDF2EF'), - new Text(700, 325, 'DSP tags'), + mainFF.box(650, 300, 100, 50, '#EDF2EF'), + mainFF.text(700, 325, 'DSP tags'), ]), - new Line(695, 350, 695, 413, 'black', true), + mainFF.line(695, 350, 695, 413, 'black', true), new Group([ - new Box(650, 413, 100, 50, '#EDF2EF'), - new Text(700, 438, 'DSPs'), + mainFF.box(650, 413, 100, 50, '#EDF2EF'), + mainFF.text(700, 438, 'DSPs'), ]), - new Line(705, 413, 705, 350, 'black', true), - new Line(705, 300, 705, 237, 'black', true), - new Text(770, 270, 'joinInterestGroup()', 12), + mainFF.line(705, 413, 705, 350, 'black', true), + mainFF.line(705, 300, 705, 237, 'black', true), + mainFF.text(770, 270, 'joinInterestGroup()', 12), ]; -main.addAnimator(new Animator(fourthCircleAnimations)); +mainCanvas.addAnimator(new Animator(fourthCircleAnimations, mainFF)); -main.addGroup( +mainCanvas.addGroup( new Group([ - new Line(537, 200, 663, 200, 'blue'), - new Circle(700, 200, 75, '#EDF2EF', 'blue'), - new Image(700, 200, completedIcon, 50, 50), + mainFF.line(537, 200, 663, 200, 'blue'), + mainFF.circle(700, 200, 75, '#EDF2EF', 'blue'), + mainFF.image(700, 200, completedIcon, 50, 50), ]) ); diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 4141d2206..b4539d883 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -95,11 +95,18 @@ class Main { */ backgroundColor = 245; + /** + * Whether to keep previous steps on the canvas. + */ + keepPrevious = true; + /** * Creates an instance of Main and initializes the p5 instance. + * @param keepPrevious - Whether to keep previous figures on the canvas. */ - constructor() { + constructor(keepPrevious?: boolean) { this.p5 = new p5(this.init.bind(this)); + this.keepPrevious = keepPrevious ?? true; } /** @@ -117,8 +124,7 @@ class Main { * Sets up the canvas. */ private setUp() { - this.p5.createCanvas(1600, 1600); - this.p5.background(this.backgroundColor); + this.p5.createCanvas(1600, 1600).position(0, 0); } /** @@ -167,6 +173,10 @@ class Main { * @param useInstantQueue - Whether to use the instant queue. */ private runner(useInstantQueue = false) { + if (this.pause) { + return; + } + const queue = useInstantQueue ? this.instantQueue : this.stepsQueue; const groupQueue = useInstantQueue ? this.groupInstantQueue @@ -219,6 +229,9 @@ class Main { } if (this.p5.frameCount % this.delay === 0) { + if (!this.keepPrevious) { + this.reDrawAll(); + } this.runner(); } @@ -283,6 +296,12 @@ class Main { */ togglePause() { this.pause = !this.pause; + + if (this.pause) { + this.p5.noLoop(); + } else { + this.p5.loop(); + } } /** @@ -312,7 +331,7 @@ class Main { } this.p5.clear(); - this.p5.background(this.backgroundColor); + // this.p5.background(this.backgroundColor); for (let i = 0; i < this.snapshot.length; i++) { const figure = this.snapshot[i]; @@ -459,4 +478,4 @@ class Main { } } -export default new Main(); +export default Main; From 9064ae1676f7ef545d42f6aadba92f4d26649883 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Wed, 1 Jan 2025 12:26:41 +0530 Subject: [PATCH 24/44] Make var private and setup getter setter functions --- ee-workflow/src/components/animator.ts | 46 +++++- ee-workflow/src/components/figure/box.ts | 13 +- ee-workflow/src/components/figure/circle.ts | 11 +- ee-workflow/src/components/figure/image.ts | 20 +-- ee-workflow/src/components/figure/index.ts | 106 ++++++++----- ee-workflow/src/components/figure/line.ts | 14 +- ee-workflow/src/components/figure/text.ts | 14 +- ee-workflow/src/components/group.ts | 59 ++++++- ee-workflow/src/index.ts | 2 +- ee-workflow/src/main.ts | 166 +++++++++++++------- 10 files changed, 285 insertions(+), 166 deletions(-) diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts index 9ebde063e..b049d0606 100644 --- a/ee-workflow/src/components/animator.ts +++ b/ee-workflow/src/components/animator.ts @@ -29,29 +29,29 @@ export default class Animator { /** * Unique id of the animator. */ - id: string; + private id: string; /** * Array of objects to be animated. Can be figures or groups. * The last object in the array is a dummy object that acts as a placeholder for the end of the animation. */ - objects: Array
= []; + private objects: Array
= []; /** * Index of the current object being animated. */ - index = 0; + private index = 0; /** * Flag to indicate if the animation should be saved in animatorSnapshot. * If true, the animation will NOT be saved in animatorSnapshot. */ - throw = false; + private throw = false; /** * Function to be executed when the animation ends. */ - sideEffectOnEnd: (() => void) | undefined; + private sideEffectOnEnd: (() => void) | undefined; /** * Counter for the number of animations created. @@ -95,4 +95,40 @@ export default class Animator { setSideEffectOnEnd(sideEffect: () => void) { this.sideEffectOnEnd = sideEffect; } + + /** + * Gets the unique id of the animator. + * @returns unique id of the animator + */ + getId(): string { + return this.id; + } + + /** + * Gets the throw flag. + * @returns true if the animation will NOT be saved in animatorSnapshot. + */ + getThrow(): boolean { + return this.throw; + } + + /** + * Sets the throw flag to true. + * If true, the animation will NOT be saved in animatorSnapshot. + * @param throwFlag - boolean indicating if the animation should be saved in animatorSnapshot + */ + setThrow(throwFlag: boolean) { + this.throw = throwFlag; + } + + getObjects(): Array
{ + return this.objects; + } + + removeObject(object: Figure | Group) { + const index = this.objects.indexOf(object); + if (index > -1) { + this.objects.splice(index, 1); + } + } } diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 007daf407..2f1b4ada9 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -29,12 +29,12 @@ export default class Box extends Figure { /** * Width of the box. */ - width: number; + private width: number; /** * Height of the box. */ - height: number; + private height: number; constructor( canvasRuuner: Main, @@ -93,14 +93,6 @@ export default class Box extends Figure { ); } - remove() { - this.p5?.push(); - this.p5?.fill(this.canvasRunner.backgroundColor); - this.p5?.stroke(this.canvasRunner.backgroundColor); - this.p5?.rect(this.x, this.y, this.width, this.height); - this.p5?.pop(); - } - reDraw( x?: number, y?: number, @@ -109,7 +101,6 @@ export default class Box extends Figure { fill?: string, stroke?: string ) { - this.remove(); this.x = x ?? this.x; this.y = y ?? this.y; this.width = width ?? this.width; diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index c4f5c99b1..5ae81eae7 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -29,7 +29,7 @@ export default class Circle extends Figure { /** * Diameter of the circle. */ - diameter: number; + private diameter: number; constructor( canvasRunner: Main, @@ -84,14 +84,6 @@ export default class Circle extends Figure { ); } - remove() { - this.p5?.push(); - this.p5?.fill(this.canvasRunner.backgroundColor); - this.p5?.stroke(this.canvasRunner.backgroundColor); - this.p5?.circle(this.x, this.y, this.diameter + 1); - this.p5?.pop(); - } - reDraw( x?: number, y?: number, @@ -99,7 +91,6 @@ export default class Circle extends Figure { fill?: string, stroke?: string ) { - this.remove(); this.x = x ?? this.x; this.y = y ?? this.y; this.diameter = diameter ?? this.diameter; diff --git a/ee-workflow/src/components/figure/image.ts b/ee-workflow/src/components/figure/image.ts index f54bc8ce2..4486d566b 100644 --- a/ee-workflow/src/components/figure/image.ts +++ b/ee-workflow/src/components/figure/image.ts @@ -33,17 +33,17 @@ export default class Image extends Figure { /** * Image to be displayed. */ - image: p5.Image; + private image: p5.Image; /** * Width of the image. */ - width: number; + private width: number; /** * Height of the image. */ - height: number; + private height: number; constructor( canvasRunner: Main, @@ -82,19 +82,6 @@ export default class Image extends Figure { return false; } - remove() { - const whiteImage = this.p5?.createImage( - this.width, - this.height - ) as p5.Image; - - whiteImage.set(this.x, this.y, this.p5?.color(255) as p5.Color); - - this.p5?.push(); - this.p5?.image(whiteImage, this.x, this.y, this.width, this.height); - this.p5?.pop(); - } - reDraw( x?: number, y?: number, @@ -102,7 +89,6 @@ export default class Image extends Figure { width?: number, height?: number ) { - this.remove(); this.x = x ?? this.x; this.y = y ?? this.y; this.image = image || this.image; diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 26ff9dbaa..102df2526 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -31,85 +31,64 @@ export default abstract class Figure { /** * p5 instance. */ - p5: p5 | null = null; - - /** - * Main class object that runs the canvas. - */ - canvasRunner: Main; + protected p5: p5 | null = null; /** * Unique id of the figure. */ - id = ''; + protected id = ''; /** * Group id of the figure if it belongs to a group. */ - gid = ''; + protected gid = ''; /** * Animator id of the figure if it belongs to an animator. */ - aid = ''; - - /** - * x coordinate of the figure. - */ - x = 0; - - /** - * y coordinate of the figure. - */ - y = 0; - - /** - * Fill color of the figure. - */ - fill = 'black'; - - /** - * Stroke color of the figure. - */ - stroke = 'black'; + protected aid = ''; /** * Previous fill color of the figure to be used when the figure is unhovered. */ - previousFill = ''; + protected previousFill = ''; /** * Previous stroke color of the figure to be used when the figure is unhovered. */ - previousStroke = ''; + protected previousStroke = ''; /** * Property to determine if the object should be added to the snapshot. * If true, the object will not be added to the snapshot. */ - throw?: boolean; + protected throw = false; /** * The number of objects created. */ static objectCount = 0; + /** + * Constructor for the figure. + * @param canvasRunner - The canvas runner instance. + * @param x - The x coordinate of the figure. + * @param y - The y coordinate of the figure. + * @param fill - The fill color of the figure. + * @param stroke - The stroke color of the figure. + */ constructor( - canvasRunner: Main, - x: number, - y: number, - fill?: string, - stroke?: string + protected canvasRunner: Main, + protected x: number, + protected y: number, + protected fill: string = 'black', + protected stroke: string = 'black' ) { Figure.objectCount++; this.id = `object-${Figure.objectCount}` + Math.random().toString(36).slice(2, 9); this.canvasRunner = canvasRunner; this.p5 = this.canvasRunner.getP5Instance(); - this.x = x; - this.y = y; - this.fill = fill || 'black'; - this.stroke = stroke || 'black'; this.previousFill = this.fill; this.previousStroke = this.stroke; } @@ -142,7 +121,9 @@ export default abstract class Figure { /** * Method to remove the figure. */ - abstract remove(): void; + remove() { + this.canvasRunner.removeFigure(this); + } /** * Method to redraw the figure. @@ -150,6 +131,22 @@ export default abstract class Figure { */ abstract reDraw(...params: Array): void; + /** + * Get the unique id of the figure. + * @returns The unique id of the figure. + */ + getId(): string { + return this.id; + } + + /** + * Get the group id of the figure. + * @returns The group id of the figure. + */ + getGid(): string { + return this.gid; + } + /** * Set the group id of the figure. * @param gid - The group id to set. @@ -158,6 +155,14 @@ export default abstract class Figure { this.gid = gid; } + /** + * Get the animator id of the figure. + * @returns The animator id of the + */ + getAid(): string { + return this.aid; + } + /** * Set the animator id of the figure. * @param aid - The animator id to set. @@ -166,6 +171,23 @@ export default abstract class Figure { this.aid = aid; } + /** + * Get the throw flag. + * @returns true if the figure will NOT be saved in snapshot. + */ + getThrow(): boolean { + return this.throw; + } + + /** + * Set the throw flag to true. + * If true, the figure will NOT be saved in snapshot. + * @param throwFlag - boolean indicating if the figure should be saved in snapshot. + */ + setThrow(throwFlag: boolean) { + this.throw = throwFlag; + } + /** * Save the previous fill and stroke colors. */ diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 2e216a1f9..8732207e3 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -28,17 +28,17 @@ export default class Line extends Figure { /** * End x coordinate of the line. */ - endX: number; + private endX: number; /** * End y coordinate of the line. */ - endY: number; + private endY: number; /** * Whether the line has an arrow at the end. */ - hasArrow: boolean; + private hasArrow: boolean; constructor( canvasRunner: Main, @@ -101,13 +101,6 @@ export default class Line extends Figure { return false; // TODO: Implement if line hover is needed } - remove() { - this.p5?.push(); - this.p5?.stroke(this.canvasRunner.backgroundColor); - this.p5?.line(this.x, this.y, this.endX, this.endY); - this.p5?.pop(); - } - reDraw( x?: number, y?: number, @@ -115,7 +108,6 @@ export default class Line extends Figure { endY?: number, stroke?: string ) { - this.remove(); this.x = x ?? this.x; this.y = y ?? this.y; this.endX = endX ?? this.endX; diff --git a/ee-workflow/src/components/figure/text.ts b/ee-workflow/src/components/figure/text.ts index c127a2ce4..13d037a70 100644 --- a/ee-workflow/src/components/figure/text.ts +++ b/ee-workflow/src/components/figure/text.ts @@ -29,12 +29,12 @@ export default class Text extends Figure { /** * Text to be displayed. */ - str: string; + private str: string; /** * Font size of the text. */ - size: number; + private size: number; constructor( canvasRunnner: Main, @@ -74,15 +74,6 @@ export default class Text extends Figure { return false; } - remove() { - this.p5?.push(); - this.p5?.fill(this.canvasRunner.backgroundColor); - this.p5?.stroke(this.canvasRunner.backgroundColor); - this.p5?.textSize(this.size); - this.p5?.text(this.str, this.x, this.y); - this.p5?.pop(); - } - reDraw( x?: number, y?: number, @@ -91,7 +82,6 @@ export default class Text extends Figure { fill?: string, stroke?: string ) { - this.remove(); this.x = x ?? this.x; this.y = y ?? this.y; this.str = str || this.str; diff --git a/ee-workflow/src/components/group.ts b/ee-workflow/src/components/group.ts index 628271402..1b592d6b7 100644 --- a/ee-workflow/src/components/group.ts +++ b/ee-workflow/src/components/group.ts @@ -26,23 +26,23 @@ export default class Group { /** * Figures in the group. */ - figures: Figure[]; + private figures: Figure[]; /** * Unique id of the group. */ - id: string; + private id: string; /** * Animator id of the group if it belongs to an animator. */ - aid = ''; + private aid = ''; /** * Property to check if the group should be saved in groupSnapshot. * If true, the group will NOT be saved in groupSnapshot. */ - throw = false; + private throw = false; /** * Counter for the number of groups created. @@ -93,6 +93,22 @@ export default class Group { }); } + /** + * Method to get the id of the group. + * @returns The id of the group. + */ + getId() { + return this.id; + } + + /** + * Method to get the animator id of the group. + * @returns The animator id of the group. + */ + getAid() { + return this.aid; + } + /** * Method to set the animator id to the group and its figures. * @param aid - The animator id of the group. @@ -101,4 +117,39 @@ export default class Group { this.aid = aid; this.figures.forEach((figure) => figure.setAid(aid)); } + + /** + * Method to get throw property of the group. + * @returns The throw property of the group. + */ + getThrow() { + return this.throw; + } + + /** + * Method to set throw property of the group. + * @param throwFlag - The throw property of the group. + */ + setThrow(throwFlag: boolean) { + this.throw = throwFlag; + } + + /** + * Method to get the figures in the group. + * @returns The figures in the group. + */ + getFigures() { + return this.figures; + } + + /** + * Method to remove a figure from the group. + * @param figure - The figure to remove from the group. + */ + removeFigure(figure: Figure) { + const index = this.figures.indexOf(figure); + if (index > -1) { + this.figures.splice(index, 1); + } + } } diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 8bbea9640..ce762d3fb 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -23,7 +23,7 @@ const mainCanvas = new Main(); const mainFF = new FigureFactory(mainCanvas); const IGCanvas = new Main(false); -IGCanvas.delay = 1; +IGCanvas.setDelay(1); const IGFF = new FigureFactory(IGCanvas); IGCanvas.togglePause(); diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index b4539d883..2c69fd44a 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -33,80 +33,74 @@ class Main { /** * p5 instance for rendering. */ - p5: p5; + private p5: p5; /** * Queue for steps to be processed. */ - stepsQueue: Figure[] = []; + private stepsQueue: Figure[] = []; /** * Queue for group steps to be processed. */ - groupStepsQueue: Group[] = []; + private groupStepsQueue: Group[] = []; /** * Queue for animator steps to be processed. */ - animatorStepsQueue: Animator[] = []; + private animatorStepsQueue: Animator[] = []; /** * Queue for instant steps to be processed. */ - instantQueue: Figure[] = []; + private instantQueue: Figure[] = []; /** * Queue for instant group steps to be processed. */ - groupInstantQueue: Group[] = []; + private groupInstantQueue: Group[] = []; /** * Queue for instant animator steps to be processed. */ - animatorInstantQueue: Animator[] = []; + private animatorInstantQueue: Animator[] = []; /** * Delay between each frame in milliseconds. */ - delay = 50; + private delay = 50; /** * Flag to pause the drawing process. */ - pause = false; + private pause = false; /** * Snapshot of figures that have been drawn. */ - snapshot: Figure[] = []; + private snapshot: Figure[] = []; /** * Snapshot of groups that have been drawn. */ - groupSnapshot: Group[] = []; + private groupSnapshot: Group[] = []; /** * Snapshot of animators that have been drawn. */ - animatorSnapshot: Animator[] = []; + private animatorSnapshot: Animator[] = []; /** * Background color of the canvas. */ - backgroundColor = 245; + private backgroundColor = 255; /** - * Whether to keep previous steps on the canvas. + * Main constructor. + * @param clearOnEachStep - Whether to clear the canvas on each step (default: true). */ - keepPrevious = true; - - /** - * Creates an instance of Main and initializes the p5 instance. - * @param keepPrevious - Whether to keep previous figures on the canvas. - */ - constructor(keepPrevious?: boolean) { + constructor(private clearOnEachStep: boolean = true) { this.p5 = new p5(this.init.bind(this)); - this.keepPrevious = keepPrevious ?? true; } /** @@ -133,7 +127,7 @@ class Main { */ private saveToSnapshot(object: Figure) { this.snapshot.push(object); - object.throw = true; + object.setThrow(true); } /** @@ -154,16 +148,16 @@ class Main { group.draw(); } - let toRemoveCount = group.figures.length - 1; + let toRemoveCount = group.getFigures().length - 1; while (toRemoveCount > 0) { queue.shift(); toRemoveCount--; } - if (!group.throw) { - group.figures.forEach((object) => this.saveToSnapshot(object)); + if (!group.getThrow()) { + group.getFigures().forEach((object) => this.saveToSnapshot(object)); this.groupSnapshot.push(group); - group.throw = true; + group.setThrow(true); } } } @@ -188,16 +182,16 @@ class Main { if (queue.length > 0) { const firstObject =
queue.shift(); - if (firstObject.aid) { + if (firstObject.getAid()) { const animator = animatorQueue[0]; if (animator) { const isDone = animator.draw(); - if (firstObject.gid) { + if (firstObject.getGid()) { this.processGroup(queue, groupQueue, false); } else { - if (!firstObject.throw) { + if (!firstObject.getThrow()) { this.saveToSnapshot(firstObject); } } @@ -209,11 +203,11 @@ class Main { this.reDrawAll(); } } - } else if (firstObject.gid) { + } else if (firstObject.getGid()) { this.processGroup(queue, groupQueue); } else { firstObject.draw(); - if (!firstObject.throw) { + if (!firstObject.getThrow()) { this.saveToSnapshot(firstObject); } } @@ -229,8 +223,8 @@ class Main { } if (this.p5.frameCount % this.delay === 0) { - if (!this.keepPrevious) { - this.reDrawAll(); + if (!this.clearOnEachStep) { + this.p5.clear(); } this.runner(); } @@ -246,11 +240,13 @@ class Main { * @returns The group the figure belongs to, or undefined. */ private isGrouped(object: Figure): Group | undefined { - if (!object.gid) { + if (!object.getGid()) { return undefined; } - return this.groupSnapshot.find((group) => group.id === object.gid); + return this.groupSnapshot.find( + (group) => group.getId() === object.getGid() + ); } /** @@ -304,6 +300,46 @@ class Main { } } + /** + * Checks if the drawing process is paused. + * @returns Whether the drawing process is paused. + */ + isPaused() { + return this.pause; + } + + /** + * Gets the delay between each frame. + * @returns The delay. + */ + getDelay() { + return this.delay; + } + + /** + * Sets the delay between each frame. + * @param delay - The delay to set. + */ + setDelay(delay: number) { + this.delay = delay; + } + + /** + * Gets the background color of the canvas. + * @returns - The background color. + */ + getBackgroundColor() { + return this.backgroundColor; + } + + /** + * Sets the background color of the canvas. + * @param color - The color to set. + */ + setBackgroundColor(color: number) { + this.backgroundColor = color; + } + /** * Resets the queues and redraws all figures on the canvas. */ @@ -335,33 +371,37 @@ class Main { for (let i = 0; i < this.snapshot.length; i++) { const figure = this.snapshot[i]; - if (figure.aid) { - const animator = this.animatorSnapshot.find((a) => a.id === figure.aid); + if (figure.getAid()) { + const animator = this.animatorSnapshot.find( + (a) => a.getId() === figure.getAid() + ); - if (animator && animatorIdToDraw === animator.id) { + if (animator && animatorIdToDraw === animator.getId()) { this.addAnimator(animator, true); } const toRemoveCount = - animator?.objects.reduce((acc, object) => { + animator?.getObjects().reduce((acc, object) => { if (object instanceof Figure) { acc++; } else { - acc += object.figures.length; + acc += object.getFigures().length; } return acc; }, 0) || 1; i += toRemoveCount - 1; - } else if (figure.gid) { - const group = this.groupSnapshot.find((g) => g.id === figure.gid); + } else if (figure.getGid()) { + const group = this.groupSnapshot.find( + (g) => g.getId() === figure.getGid() + ); if (group) { this.addGroup(group, true); } - const toRemoveCount = (group?.figures.length || 1) - 1; + const toRemoveCount = (group?.getFigures().length || 1) - 1; i += toRemoveCount; } else { this.addFigure(figure, true); @@ -390,10 +430,10 @@ class Main { addGroup(group: Group, instant = false) { if (instant) { this.groupInstantQueue.push(group); - group.figures.forEach((figure) => this.addFigure(figure, instant)); + group.getFigures().forEach((figure) => this.addFigure(figure, instant)); } else { this.groupStepsQueue.push(group); - group.figures.forEach((figure) => this.addFigure(figure, instant)); + group.getFigures().forEach((figure) => this.addFigure(figure, instant)); } } @@ -405,7 +445,7 @@ class Main { addAnimator(animator: Animator, instant = false) { if (instant) { this.animatorInstantQueue.push(animator); - animator.objects.forEach((object) => { + animator.getObjects().forEach((object) => { if (object instanceof Figure) { this.addFigure(object, instant); } else { @@ -414,7 +454,7 @@ class Main { }); } else { this.animatorStepsQueue.push(animator); - animator.objects.forEach((object) => { + animator.getObjects().forEach((object) => { if (object instanceof Figure) { this.addFigure(object, instant); } else { @@ -429,7 +469,22 @@ class Main { * @param figure - The figure to remove. */ removeFigure(figure: Figure) { - this.snapshot = this.snapshot.filter((f) => f.id !== figure.id); + this.snapshot = this.snapshot.filter((f) => f.getId() !== figure.getId()); + + if (figure.getAid()) { + const animator = this.animatorSnapshot.find( + (a) => a.getId() !== figure.getAid() + ); + animator?.removeObject(figure); + } + + if (figure.getGid()) { + const group = this.groupSnapshot.find( + (g) => g.getId() === figure.getGid() + ); + group?.removeFigure(figure); + } + this.reDrawAll(); } @@ -441,7 +496,7 @@ class Main { let toRemove = null; this.groupSnapshot = this.groupSnapshot.filter((g) => { - if (g.id === group.id) { + if (g.getId() === group.getId()) { toRemove = g; return false; } @@ -449,7 +504,10 @@ class Main { return true; }); - toRemove?.figures.forEach((g) => this.removeFigure(g)); + toRemove?.getFigures().forEach((figure) => { + figure.setGid(''); + this.removeFigure(figure); + }); } /** @@ -460,7 +518,7 @@ class Main { let toRemove = null; this.animatorSnapshot = this.animatorSnapshot.filter((a) => { - if (a.id === animator.id) { + if (a.getId() === animator.getId()) { toRemove = a; return false; } @@ -468,7 +526,9 @@ class Main { return true; }); - toRemove?.objects.forEach((object) => { + toRemove?.getObjects().forEach((object) => { + object.setAid(''); + if (object instanceof Figure) { this.removeFigure(object); } else { From 5905fa092960426a15b1b24846bb574d21e707ad Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Fri, 3 Jan 2025 21:56:55 +0530 Subject: [PATCH 25/44] Add logic for travller class and integrate it to line's figurefactory function for animated line --- .../src/components/figure/figureFactory.ts | 48 ++++++- ee-workflow/src/components/figure/index.ts | 65 ++++++++- ee-workflow/src/components/figure/line.ts | 55 ++++++-- ee-workflow/src/components/traveller.ts | 44 ++++++ ee-workflow/src/index.ts | 125 +++++++++--------- ee-workflow/src/main.ts | 29 ++++ 6 files changed, 288 insertions(+), 78 deletions(-) create mode 100644 ee-workflow/src/components/traveller.ts diff --git a/ee-workflow/src/components/figure/figureFactory.ts b/ee-workflow/src/components/figure/figureFactory.ts index cd2fbe7f7..696dd8ef3 100644 --- a/ee-workflow/src/components/figure/figureFactory.ts +++ b/ee-workflow/src/components/figure/figureFactory.ts @@ -17,6 +17,7 @@ /** * Internal dependencies. */ +import Figure from '.'; import Main from '../../main'; import Box from './box'; import Circle from './circle'; @@ -68,9 +69,52 @@ export default class FigureFactory { endX: number, endY: number, stroke?: string, - hasArrow?: boolean + hasArrow?: boolean, + shouldTravel?: boolean ): Line { - return new Line(this.canvasRunner, x, y, endX, endY, stroke, hasArrow); + const line = new Line( + this.canvasRunner, + x, + y, + endX, + endY, + stroke, + hasArrow + ); + + if (shouldTravel) { + let currentX = x; + let currentY = y; + line.setShouldTravel(shouldTravel); + line.setEndX(currentX); + line.setEndY(currentY); + + const traveller = (figure: Figure) => { + const _figure = figure; + const p5 = _figure.getP5(); + + if (hasArrow) { + _figure.remove(); + } + + currentX = p5?.lerp(currentX, endX, 0.1) ?? endX; + currentY = p5?.lerp(currentY, endY, 0.1) ?? endY; + + _figure.setEndX(currentX); + _figure.setEndY(currentY); + _figure.draw(); + + if (Math.round(currentX) === endX && Math.round(currentY) === endY) { + return true; + } + + return false; + }; + + line.setTraveller(traveller); + } + + return line; } text(x: number, y: number, text: string, size?: number, fill?: string): Text { diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 102df2526..573745977 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -64,6 +64,17 @@ export default abstract class Figure { */ protected throw = false; + /** + * Property to determine if the object should travel. + * If true, the object will travel to the end coordinates. + */ + protected shouldTravel = false; + + /** + * Function to be executed when the object has to travel. + */ + protected traveller: ((figure: Figure) => boolean) | undefined; + /** * The number of objects created. */ @@ -118,6 +129,24 @@ export default abstract class Figure { */ abstract isHovering(): boolean; + /** + * Method to redraw the figure. + * @param params - The parameters to redraw the figure with. + */ + abstract reDraw(...params: Array): void; + + getP5(): p5 | null { + return this.p5; + } + + getX(): number { + return this.x; + } + + getY(): number { + return this.y; + } + /** * Method to remove the figure. */ @@ -126,10 +155,40 @@ export default abstract class Figure { } /** - * Method to redraw the figure. - * @param params - The parameters to redraw the figure with. + * Method to get the shouldTravel property. + * @returns boolean indicating if the figure should travel. */ - abstract reDraw(...params: Array): void; + getShouldTravel(): boolean { + return this.shouldTravel; + } + + /** + * Method to set shouldTravel property. + * @param shouldTravel - boolean indicating if the figure should travel. + */ + setShouldTravel(shouldTravel: boolean) { + this.shouldTravel = shouldTravel; + } + + /** + * Method to run the traveller function. + * @returns boolean indicating if the traveller function was executed + */ + runTraveller(): boolean { + if (this.traveller) { + return this.traveller(this); + } + + return true; + } + + /** + * Method to set the traveller function. + * @param traveller - The traveller function to set. + */ + setTraveller(traveller: (figure: Figure) => boolean) { + this.traveller = traveller; + } /** * Get the unique id of the figure. diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 8732207e3..9cd67fda4 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -55,26 +55,48 @@ export default class Line extends Figure { this.hasArrow = hasArrow ?? false; } + private drawArrow(size = 5) { + const angle = ( + this.p5?.atan2(this.y - this.endY, this.x - this.endX) + ); + this.p5?.translate(this.endX, this.endY); + this.p5?.rotate(angle - this.p5?.HALF_PI); + this.p5?.fill(this.stroke); + this.p5?.stroke(255); + this.p5?.beginShape(); + this.p5?.vertex(0, 0); + this.p5?.vertex(-size, size * 2); + this.p5?.vertex(size, size * 2); + this.p5?.endShape(this.p5?.CLOSE); + } + draw() { this.p5?.push(); this.p5?.stroke(this.stroke); this.p5?.line(this.x, this.y, this.endX, this.endY); - if (this.hasArrow) { - const angle = ( - this.p5?.atan2(this.y - this.endY, this.x - this.endX) - ); - this.p5?.translate(this.endX, this.endY); - this.p5?.rotate(angle - this.p5?.HALF_PI); - this.p5?.fill(this.stroke); - this.p5?.beginShape(); - this.p5?.vertex(0, 0); - this.p5?.vertex(-5, 10); - this.p5?.vertex(5, 10); - this.p5?.endShape(this.p5?.CLOSE); + const dist = this.p5?.dist(this.x, this.y, this.endX, this.endY) ?? 0; + if (this.hasArrow && dist > 10) { + this.drawArrow(); } this.p5?.pop(); } + getEndX(): number { + return this.endX; + } + + setEndX(endX: number) { + this.endX = endX; + } + + getEndY(): number { + return this.endY; + } + + setEndY(endY: number) { + this.endY = endY; + } + onHover() { this.savePreviousColors(); this.stroke = 'red'; // TODO: Discuss the function @@ -101,6 +123,15 @@ export default class Line extends Figure { return false; // TODO: Implement if line hover is needed } + remove() { + const previousStroke = this.stroke; + this.stroke = this.canvasRunner.getBackgroundColor().toString(); + + this.draw(); + + this.stroke = previousStroke; + } + reDraw( x?: number, y?: number, diff --git a/ee-workflow/src/components/traveller.ts b/ee-workflow/src/components/traveller.ts new file mode 100644 index 000000000..e71ae1219 --- /dev/null +++ b/ee-workflow/src/components/traveller.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Internal dependencies. + */ +import Figure from './figure'; + +export default class Traveller { + private figure: Figure | null = null; + + constructor(figure: Figure) { + this.figure = figure; + } + + draw() { + if (!this.figure) { + return true; + } + + if (this.figure.runTraveller()) { + return true; + } + + return false; + } + + getFigure() { + return this.figure; + } +} diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index ce762d3fb..67c44dd14 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -62,18 +62,18 @@ const completedIcon = const advertiserFlow = [ mainFF.image(100, 200, userIcon, 50, 50), - mainFF.line(95, 237, 95, 300, 'black', true), + mainFF.line(95, 237, 95, 300, 'black', true, true), new Group([ mainFF.box(50, 300, 100, 50, '#EDF2EF'), mainFF.text(100, 325, 'DSP tags'), ]), - mainFF.line(95, 350, 95, 413, 'black', true), + mainFF.line(95, 350, 95, 413, 'black', true, true), new Group([ mainFF.box(50, 413, 100, 50, '#EDF2EF'), mainFF.text(100, 438, 'DSPs'), ]), - mainFF.line(105, 413, 105, 350, 'black', true), - mainFF.line(105, 300, 105, 237, 'black', true), + mainFF.line(105, 413, 105, 350, 'black', true, true), + mainFF.line(105, 300, 105, 237, 'black', true, true), mainFF.text(170, 270, 'joinInterestGroup()', 12), ]; @@ -121,20 +121,23 @@ mainCanvas.addGroup( ]) ); +const travellerLine = mainFF.line(0, 400, 1000, 600, 'red', false, true); +mainCanvas.addFigure(travellerLine); + const secondCircleAnimations = [ mainFF.image(300, 200, userIcon, 50, 50), - mainFF.line(295, 237, 295, 300, 'black', true), + mainFF.line(295, 237, 295, 300, 'black', true, true), new Group([ mainFF.box(250, 300, 100, 50, '#EDF2EF'), mainFF.text(300, 325, 'DSP tags'), ]), - mainFF.line(295, 350, 295, 413, 'black', true), + mainFF.line(295, 350, 295, 413, 'black', true, true), new Group([ mainFF.box(250, 413, 100, 50, '#EDF2EF'), mainFF.text(300, 438, 'DSPs'), ]), - mainFF.line(305, 413, 305, 350, 'black', true), - mainFF.line(305, 300, 305, 237, 'black', true), + mainFF.line(305, 413, 305, 350, 'black', true, true), + mainFF.line(305, 300, 305, 237, 'black', true, true), mainFF.text(370, 270, 'joinInterestGroup()', 12), ]; @@ -172,56 +175,56 @@ mainCanvas.addGroup( ]) ); -const thirdCircleAnimations = [ - mainFF.image(500, 200, userIcon, 50, 50), - mainFF.line(495, 237, 495, 300, 'black', true), - new Group([ - mainFF.box(450, 300, 100, 50, '#EDF2EF'), - mainFF.text(500, 325, 'DSP tags'), - ]), - mainFF.line(495, 350, 495, 413, 'black', true), - new Group([ - mainFF.box(450, 413, 100, 50, '#EDF2EF'), - mainFF.text(500, 438, 'DSPs'), - ]), - mainFF.line(505, 413, 505, 350, 'black', true), - mainFF.line(505, 300, 505, 237, 'black', true), - mainFF.text(570, 270, 'joinInterestGroup()', 12), -]; - -mainCanvas.addAnimator(new Animator(thirdCircleAnimations, mainFF)); - -mainCanvas.addGroup( - new Group([ - mainFF.line(337, 200, 463, 200, 'blue'), - mainFF.circle(500, 200, 75, '#EDF2EF', 'blue'), - mainFF.image(500, 200, completedIcon, 50, 50), - ]) -); - -const fourthCircleAnimations = [ - mainFF.image(700, 200, userIcon, 50, 50), - mainFF.line(695, 237, 695, 300, 'black', true), - new Group([ - mainFF.box(650, 300, 100, 50, '#EDF2EF'), - mainFF.text(700, 325, 'DSP tags'), - ]), - mainFF.line(695, 350, 695, 413, 'black', true), - new Group([ - mainFF.box(650, 413, 100, 50, '#EDF2EF'), - mainFF.text(700, 438, 'DSPs'), - ]), - mainFF.line(705, 413, 705, 350, 'black', true), - mainFF.line(705, 300, 705, 237, 'black', true), - mainFF.text(770, 270, 'joinInterestGroup()', 12), -]; - -mainCanvas.addAnimator(new Animator(fourthCircleAnimations, mainFF)); - -mainCanvas.addGroup( - new Group([ - mainFF.line(537, 200, 663, 200, 'blue'), - mainFF.circle(700, 200, 75, '#EDF2EF', 'blue'), - mainFF.image(700, 200, completedIcon, 50, 50), - ]) -); +// const thirdCircleAnimations = [ +// mainFF.image(500, 200, userIcon, 50, 50), +// mainFF.line(495, 237, 495, 300, 'black', true), +// new Group([ +// mainFF.box(450, 300, 100, 50, '#EDF2EF'), +// mainFF.text(500, 325, 'DSP tags'), +// ]), +// mainFF.line(495, 350, 495, 413, 'black', true), +// new Group([ +// mainFF.box(450, 413, 100, 50, '#EDF2EF'), +// mainFF.text(500, 438, 'DSPs'), +// ]), +// mainFF.line(505, 413, 505, 350, 'black', true), +// mainFF.line(505, 300, 505, 237, 'black', true), +// mainFF.text(570, 270, 'joinInterestGroup()', 12), +// ]; + +// mainCanvas.addAnimator(new Animator(thirdCircleAnimations, mainFF)); + +// mainCanvas.addGroup( +// new Group([ +// mainFF.line(337, 200, 463, 200, 'blue'), +// mainFF.circle(500, 200, 75, '#EDF2EF', 'blue'), +// mainFF.image(500, 200, completedIcon, 50, 50), +// ]) +// ); + +// const fourthCircleAnimations = [ +// mainFF.image(700, 200, userIcon, 50, 50), +// mainFF.line(695, 237, 695, 300, 'black', true), +// new Group([ +// mainFF.box(650, 300, 100, 50, '#EDF2EF'), +// mainFF.text(700, 325, 'DSP tags'), +// ]), +// mainFF.line(695, 350, 695, 413, 'black', true), +// new Group([ +// mainFF.box(650, 413, 100, 50, '#EDF2EF'), +// mainFF.text(700, 438, 'DSPs'), +// ]), +// mainFF.line(705, 413, 705, 350, 'black', true), +// mainFF.line(705, 300, 705, 237, 'black', true), +// mainFF.text(770, 270, 'joinInterestGroup()', 12), +// ]; + +// mainCanvas.addAnimator(new Animator(fourthCircleAnimations, mainFF)); + +// mainCanvas.addGroup( +// new Group([ +// mainFF.line(537, 200, 663, 200, 'blue'), +// mainFF.circle(700, 200, 75, '#EDF2EF', 'blue'), +// mainFF.image(700, 200, completedIcon, 50, 50), +// ]) +// ); diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 2c69fd44a..70b26ebb7 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -24,6 +24,7 @@ import p5 from 'p5'; import Figure from './components/figure'; import Group from './components/group'; import Animator from './components/animator'; +import Traveller from './components/traveller'; /** * Main class responsible for managing the rendering and interaction of figures, @@ -95,6 +96,10 @@ class Main { */ private backgroundColor = 255; + private isTravelling = false; + + private traveller: Traveller | null = null; + /** * Main constructor. * @param clearOnEachStep - Whether to clear the canvas on each step (default: true). @@ -182,6 +187,14 @@ class Main { if (queue.length > 0) { const firstObject =
queue.shift(); + if (firstObject.getShouldTravel() && !useInstantQueue) { + this.isTravelling = true; + this.traveller = new Traveller(firstObject); + firstObject.setShouldTravel(false); + + return; + } + if (firstObject.getAid()) { const animator = animatorQueue[0]; @@ -218,6 +231,22 @@ class Main { * Draws the current frame, processing the queues. */ private draw() { + if (this.isTravelling) { + const done = this.traveller?.draw(); + + if (done) { + this.isTravelling = false; + const figure =
this.traveller?.getFigure(); + + this.stepsQueue.unshift(figure); + + this.traveller = null; + this.runner(); + } + + return; + } + if (this.pause) { return; } From 8465f691ed3f27e3097d323d97d23da46b79a68d Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 6 Jan 2025 20:00:57 +0530 Subject: [PATCH 26/44] Handle traveller logic for grouped figures --- ee-workflow/src/components/group.ts | 8 +++ ee-workflow/src/components/traveller.ts | 69 ++++++++++++++++++++++--- ee-workflow/src/index.ts | 8 ++- ee-workflow/src/main.ts | 21 ++++++-- 4 files changed, 95 insertions(+), 11 deletions(-) diff --git a/ee-workflow/src/components/group.ts b/ee-workflow/src/components/group.ts index 1b592d6b7..d49ac636d 100644 --- a/ee-workflow/src/components/group.ts +++ b/ee-workflow/src/components/group.ts @@ -152,4 +152,12 @@ export default class Group { this.figures.splice(index, 1); } } + + /** + * Method to get detail if any figure in the group should travel. + * @returns boolean indicating if any figure in the group should travel. + */ + getShouldTravel() { + return this.figures.some((figure) => figure.getShouldTravel()); + } } diff --git a/ee-workflow/src/components/traveller.ts b/ee-workflow/src/components/traveller.ts index e71ae1219..c8f9044ba 100644 --- a/ee-workflow/src/components/traveller.ts +++ b/ee-workflow/src/components/traveller.ts @@ -18,27 +18,82 @@ * Internal dependencies. */ import Figure from './figure'; +import Group from './group'; export default class Traveller { - private figure: Figure | null = null; + /** + * Object to be drawn. Can be a figure or a group. + */ + private object: Figure | Group | null = null; - constructor(figure: Figure) { - this.figure = figure; + /** + * Array to store the ids of the group objects that have completed travelling. + */ + private groupObjectsCompleted: string[] = []; + + constructor(object: Figure | Group) { + this.object = object; + } + + /** + * Function to manage the drawing of a group with travelling figures. + * @returns boolean indicating if the group has completed travelling. + */ + private drawGroup() { + const object = this.object; + + const nonTravellingFigures = object + .getFigures() + .filter((figure) => figure.getShouldTravel() === false); + + const travellingFigures = object + .getFigures() + .filter((figure) => figure.getShouldTravel() === true); + + nonTravellingFigures.forEach((figure) => figure.draw()); + + travellingFigures.forEach((figure) => { + if (this.groupObjectsCompleted.includes(figure.getId())) { + return; + } + + if (figure.runTraveller()) { + this.groupObjectsCompleted.push(figure.getId()); + figure.setShouldTravel(false); + } + }); + + if (this.groupObjectsCompleted.length !== travellingFigures.length) { + return false; + } + + return true; } + /** + * Function to draw the object. + * @returns boolean indicating if the object has completed travelling. + */ draw() { - if (!this.figure) { + if (!this.object) { return true; } - if (this.figure.runTraveller()) { + if (this.object instanceof Group) { + return this.drawGroup(); + } else if (this.object.runTraveller()) { + this.object.setShouldTravel(false); return true; } return false; } - getFigure() { - return this.figure; + /** + * Function to get the object. + * @returns The object to be drawn. + */ + getObject() { + return this.object; } } diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 67c44dd14..27e99f7a7 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -122,7 +122,13 @@ mainCanvas.addGroup( ); const travellerLine = mainFF.line(0, 400, 1000, 600, 'red', false, true); -mainCanvas.addFigure(travellerLine); +const travellerGroup = new Group([ + mainFF.line(0, 600, 1000, 800, 'blue', false), + mainFF.line(0, 450, 1000, 900, 'green', false, true), + mainFF.line(0, 500, 1000, 700, 'yellow', false, true), +]); + +mainCanvas.addAnimator(new Animator([travellerLine, travellerGroup], mainFF)); const secondCircleAnimations = [ mainFF.image(300, 200, userIcon, 50, 50), diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 70b26ebb7..e06ccd7ab 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -187,8 +187,19 @@ class Main { if (queue.length > 0) { const firstObject =
queue.shift(); - if (firstObject.getShouldTravel() && !useInstantQueue) { + if ( + (firstObject.getShouldTravel() || + (firstObject.getGid() && groupQueue[0].getShouldTravel())) && + !useInstantQueue + ) { this.isTravelling = true; + + if (firstObject.getGid()) { + this.traveller = new Traveller(groupQueue[0]); + + return; + } + this.traveller = new Traveller(firstObject); firstObject.setShouldTravel(false); @@ -236,9 +247,13 @@ class Main { if (done) { this.isTravelling = false; - const figure =
this.traveller?.getFigure(); + const object = this.traveller?.getObject(); - this.stepsQueue.unshift(figure); + if (object instanceof Figure) { + this.stepsQueue.unshift(object); + } else if (object?.getFigures().length) { + this.stepsQueue.unshift(object.getFigures()[0]); + } this.traveller = null; this.runner(); From f2d4de9a0e32526188240224eb9e9f43f0335f90 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 7 Jan 2025 10:44:33 +0530 Subject: [PATCH 27/44] Use lerp for bubble travel --- ee-workflow/src/index.ts | 16 ++++++++++------ ee-workflow/src/main.ts | 8 ++++---- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 27e99f7a7..b726aec69 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -89,11 +89,13 @@ circleToTextLine.forEach((line) => mainCanvas.addFigure(line, true)); const bubbles = []; let startX = 170; let startY = 270; -while (startX > 0 && startY > 0) { +let lerpSpeed = 0.01; +while (Math.floor(startX) > 0 && Math.floor(startY) > 0) { const circle = IGFF.circle(startX, startY, 10, 'orange', 'black'); bubbles.push(circle); - startX -= 1; - startY -= 1; + startX = IGCanvas.getP5Instance().lerp(startX, 0, lerpSpeed); + startY = IGCanvas.getP5Instance().lerp(startY, 0, lerpSpeed); + lerpSpeed += 0.0003; } const bubbleFlow = new Animator(bubbles, mainFF); @@ -150,11 +152,13 @@ const secondCircleAnimations = [ const secondBubbles = []; let secondStartX = 370; let secondStartY = 270; -while (secondStartX > 0 && secondStartY > 0) { +lerpSpeed = 0.01; +while (Math.floor(secondStartX) > 0 && Math.floor(secondStartY) > 0) { const circle = IGFF.circle(secondStartX, secondStartY, 10, 'orange', 'black'); secondBubbles.push(circle); - secondStartX -= 1; - secondStartY -= 1; + secondStartX = IGCanvas.getP5Instance().lerp(secondStartX, 0, lerpSpeed); + secondStartY = IGCanvas.getP5Instance().lerp(secondStartY, 0, lerpSpeed); + lerpSpeed += 0.0003; } const secondBubbleFlow = new Animator(secondBubbles, mainFF); diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index e06ccd7ab..697b68348 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -242,6 +242,10 @@ class Main { * Draws the current frame, processing the queues. */ private draw() { + if (this.pause) { + return; + } + if (this.isTravelling) { const done = this.traveller?.draw(); @@ -262,10 +266,6 @@ class Main { return; } - if (this.pause) { - return; - } - if (this.p5.frameCount % this.delay === 0) { if (!this.clearOnEachStep) { this.p5.clear(); From 660d5561ebf60b8d61b03fe4c2d2c27a43be39bc Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 7 Jan 2025 13:44:14 +0530 Subject: [PATCH 28/44] Fix jitter after redrawAll --- ee-workflow/src/main.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 697b68348..63529f59c 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -223,7 +223,6 @@ class Main { if (isDone) { this.animatorSnapshot.push(animator); animatorQueue.shift(); - this.runner(useInstantQueue); this.reDrawAll(); } } @@ -410,8 +409,6 @@ class Main { return; } - this.p5.clear(); - // this.p5.background(this.backgroundColor); for (let i = 0; i < this.snapshot.length; i++) { const figure = this.snapshot[i]; @@ -451,6 +448,8 @@ class Main { this.addFigure(figure, true); } } + + this.p5.clear(); } /** From 8de30fac95bcf14989dc9e3fbaa5bb6ef78d35f6 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 7 Jan 2025 13:45:49 +0530 Subject: [PATCH 29/44] Add comments --- ee-workflow/src/main.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 63529f59c..b5019d6fe 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -96,8 +96,14 @@ class Main { */ private backgroundColor = 255; + /** + * Flag to indicate if the canvas is drawing a figure as an animation. + */ private isTravelling = false; + /** + * Traveller object to manage the animation of a figure. + */ private traveller: Traveller | null = null; /** From b2c4fa6d00006f7050107ec94e9e662124a3e015 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 7 Jan 2025 15:25:21 +0530 Subject: [PATCH 30/44] Change events name according to the p5 --- ee-workflow/src/components/figure/box.ts | 4 ++-- ee-workflow/src/components/figure/circle.ts | 4 ++-- ee-workflow/src/components/figure/image.ts | 4 ++-- ee-workflow/src/components/figure/index.ts | 4 ++-- ee-workflow/src/components/figure/line.ts | 4 ++-- ee-workflow/src/components/figure/text.ts | 4 ++-- ee-workflow/src/components/group.ts | 12 ++++++------ ee-workflow/src/main.ts | 12 ++++++------ 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 2f1b4ada9..302d21090 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -58,7 +58,7 @@ export default class Box extends Figure { this.p5?.pop(); } - onHover() { + mouseMoved() { this.savePreviousColors(); this.fill = 'red'; // TODO: Discuss the function this.canvasRunner.addFigure(this, true); @@ -76,7 +76,7 @@ export default class Box extends Figure { this.canvasRunner.addFigure(this, true); } - onClick() { + mouseClicked() { // TODO: Discuss the function } diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index 5ae81eae7..b8e191b84 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -51,7 +51,7 @@ export default class Circle extends Figure { this.p5?.pop(); } - onHover() { + mouseMoved() { this.savePreviousColors(); this.fill = 'red'; // TODO: Discuss the function this.canvasRunner.addFigure(this, true); @@ -69,7 +69,7 @@ export default class Circle extends Figure { this.canvasRunner.addFigure(this, true); } - onClick() { + mouseClicked() { // TODO: Discuss the function } diff --git a/ee-workflow/src/components/figure/image.ts b/ee-workflow/src/components/figure/image.ts index 4486d566b..d9da976a1 100644 --- a/ee-workflow/src/components/figure/image.ts +++ b/ee-workflow/src/components/figure/image.ts @@ -66,7 +66,7 @@ export default class Image extends Figure { this.p5?.pop(); } - onHover() { + mouseMoved() { return; } @@ -74,7 +74,7 @@ export default class Image extends Figure { return; } - onClick() { + mouseClicked() { // TODO: Discuss the function } diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 573745977..149d47707 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -112,7 +112,7 @@ export default abstract class Figure { /** * Method to handle the hover event. */ - abstract onHover(): void; + abstract mouseMoved(): void; /** * Method to handle the leave event. @@ -122,7 +122,7 @@ export default abstract class Figure { /** * Method to handle the click event. */ - abstract onClick(): void; + abstract mouseClicked(): void; /** * Method to check if the figure is being hovered. diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 9cd67fda4..26c899069 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -97,7 +97,7 @@ export default class Line extends Figure { this.endY = endY; } - onHover() { + mouseMoved() { this.savePreviousColors(); this.stroke = 'red'; // TODO: Discuss the function this.canvasRunner.addFigure(this, true); @@ -115,7 +115,7 @@ export default class Line extends Figure { this.canvasRunner.addFigure(this, true); } - onClick() { + mouseClicked() { // TODO: Discuss the function } diff --git a/ee-workflow/src/components/figure/text.ts b/ee-workflow/src/components/figure/text.ts index 13d037a70..808e4b737 100644 --- a/ee-workflow/src/components/figure/text.ts +++ b/ee-workflow/src/components/figure/text.ts @@ -58,7 +58,7 @@ export default class Text extends Figure { this.p5?.pop(); } - onHover() { + mouseMoved() { return; } @@ -66,7 +66,7 @@ export default class Text extends Figure { return; } - onClick() { + mouseClicked() { // TODO: Discuss the function } diff --git a/ee-workflow/src/components/group.ts b/ee-workflow/src/components/group.ts index d49ac636d..c80da47a3 100644 --- a/ee-workflow/src/components/group.ts +++ b/ee-workflow/src/components/group.ts @@ -67,11 +67,11 @@ export default class Group { } /** - * Method to handle hover event. Calls the onHover method of each figure in the group. + * Method to handle hover event. Calls the mouseMoved method of each figure in the group. */ - onHover() { + mouseMoved() { this.figures.forEach((figure) => { - figure.onHover(); + figure.mouseMoved(); }); } @@ -85,11 +85,11 @@ export default class Group { } /** - * Method to handle click event. Calls the onClick method of each figure in the group. + * Method to handle click event. Calls the mouseClicked method of each figure in the group. */ - onClick() { + mouseClicked() { this.figures.forEach((figure) => { - figure.onClick(); + figure.mouseClicked(); }); } diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index b5019d6fe..85776281f 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -121,8 +121,8 @@ class Main { private init(p: p5) { p.setup = this.setUp.bind(this); p.draw = this.draw.bind(this); - p.mouseMoved = this.onHover.bind(this); - p.mouseClicked = this.onClick.bind(this); + p.mouseMoved = this.mouseMoved.bind(this); + p.mouseClicked = this.mouseClicked.bind(this); } /** @@ -301,13 +301,13 @@ class Main { /** * Handles hover events for figures. */ - private onHover() { + private mouseMoved() { this.snapshot.forEach((object) => { const isHovering = object.isHovering(); const _object = this.isGrouped(object) || object; if (isHovering) { - _object.onHover(); + _object.mouseMoved(); } else { _object.onLeave(); } @@ -317,13 +317,13 @@ class Main { /** * Handles click events for figures. */ - private onClick() { + private mouseClicked() { this.snapshot.forEach((object) => { const isHovering = object.isHovering(); if (isHovering) { const _object = this.isGrouped(object) || object; - _object.onClick(); + _object.mouseClicked(); } }); } From 63a7b1ab8cc853dcc152e244dd88f44d1bbd3efc Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 7 Jan 2025 16:31:50 +0530 Subject: [PATCH 31/44] Add capability to pass custom id to figure --- ee-workflow/src/components/animator.ts | 7 +- ee-workflow/src/components/figure/box.ts | 3 +- ee-workflow/src/components/figure/circle.ts | 3 +- .../src/components/figure/figureFactory.ts | 122 +++-- ee-workflow/src/components/figure/image.ts | 5 +- ee-workflow/src/components/figure/index.ts | 5 +- ee-workflow/src/components/figure/line.ts | 3 +- ee-workflow/src/components/figure/text.ts | 3 +- ee-workflow/src/index.ts | 436 +++++++++++++----- 9 files changed, 439 insertions(+), 148 deletions(-) diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts index b049d0606..aa28a5899 100644 --- a/ee-workflow/src/components/animator.ts +++ b/ee-workflow/src/components/animator.ts @@ -65,7 +65,12 @@ export default class Animator { Math.random().toString(36).slice(2, 9); this.objects = [ ...objects, - figureFactory.line(0, 0, 0, 0), // last dummy object acts as a placeholder for the end of the animation + figureFactory.line({ + x: 0, + y: 0, + endX: 0, + endY: 0, + }), // last dummy object acts as a placeholder for the end of the animation ]; this.objects.forEach((object) => object.setAid(this.id)); } diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 302d21090..67bf835cb 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -42,10 +42,11 @@ export default class Box extends Figure { y: number, width: number, height: number, + id?: string, fill?: string, stroke?: string ) { - super(canvasRuuner, x, y, fill, stroke); + super(canvasRuuner, x, y, id, fill, stroke); this.width = width; this.height = height; } diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index b8e191b84..f71353302 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -36,10 +36,11 @@ export default class Circle extends Figure { x: number, y: number, diameter: number, + id?: string, fill?: string, stroke?: string ) { - super(canvasRunner, x, y, fill, stroke); + super(canvasRunner, x, y, id, fill, stroke); this.diameter = diameter; } diff --git a/ee-workflow/src/components/figure/figureFactory.ts b/ee-workflow/src/components/figure/figureFactory.ts index 696dd8ef3..119a0536b 100644 --- a/ee-workflow/src/components/figure/figureFactory.ts +++ b/ee-workflow/src/components/figure/figureFactory.ts @@ -32,52 +32,88 @@ export default class FigureFactory { this.canvasRunner = canvasRunner; } - box( - x: number, - y: number, - width: number, - height: number, - fill?: string, - stroke?: string - ): Box { - return new Box(this.canvasRunner, x, y, width, height, fill, stroke); + box({ + id, + x, + y, + width, + height, + fill, + stroke, + }: { + id?: string; + x: number; + y: number; + width: number; + height: number; + fill?: string; + stroke?: string; + }): Box { + return new Box(this.canvasRunner, x, y, width, height, id, fill, stroke); } - circle( - x: number, - y: number, - diameter: number, - fill?: string, - stroke?: string - ): Circle { - return new Circle(this.canvasRunner, x, y, diameter, fill, stroke); + circle({ + id, + x, + y, + diameter, + fill, + stroke, + }: { + id?: string; + x: number; + y: number; + diameter: number; + fill?: string; + stroke?: string; + }): Circle { + return new Circle(this.canvasRunner, x, y, diameter, id, fill, stroke); } - image( - x: number, - y: number, - imageData: string, - width: number, - height: number - ): Image { - return new Image(this.canvasRunner, x, y, imageData, width, height); + image({ + id, + x, + y, + imageData, + width, + height, + }: { + id?: string; + x: number; + y: number; + imageData: string; + width: number; + height: number; + }): Image { + return new Image(this.canvasRunner, x, y, imageData, width, height, id); } - line( - x: number, - y: number, - endX: number, - endY: number, - stroke?: string, - hasArrow?: boolean, - shouldTravel?: boolean - ): Line { + line({ + id, + x, + y, + endX, + endY, + stroke, + hasArrow, + shouldTravel, + }: { + id?: string; + x: number; + y: number; + endX: number; + endY: number; + stroke?: string; + hasArrow?: boolean; + shouldTravel?: boolean; + }): Line { const line = new Line( this.canvasRunner, x, y, endX, endY, + id, stroke, hasArrow ); @@ -117,7 +153,21 @@ export default class FigureFactory { return line; } - text(x: number, y: number, text: string, size?: number, fill?: string): Text { - return new Text(this.canvasRunner, x, y, text, size, fill); + text({ + id, + x, + y, + text, + size, + fill, + }: { + id?: string; + x: number; + y: number; + text: string; + size?: number; + fill?: string; + }): Text { + return new Text(this.canvasRunner, x, y, text, id, size, fill); } } diff --git a/ee-workflow/src/components/figure/image.ts b/ee-workflow/src/components/figure/image.ts index d9da976a1..f5178ec38 100644 --- a/ee-workflow/src/components/figure/image.ts +++ b/ee-workflow/src/components/figure/image.ts @@ -51,9 +51,10 @@ export default class Image extends Figure { y: number, imageData: string, width: number, - height: number + height: number, + id?: string ) { - super(canvasRunner, x, y); + super(canvasRunner, x, y, id); this.image = this.p5?.loadImage(imageData); this.width = width; this.height = height; diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 149d47707..dfb6a57c2 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -85,6 +85,7 @@ export default abstract class Figure { * @param canvasRunner - The canvas runner instance. * @param x - The x coordinate of the figure. * @param y - The y coordinate of the figure. + * @param id - The unique id of the figure. * @param fill - The fill color of the figure. * @param stroke - The stroke color of the figure. */ @@ -92,12 +93,12 @@ export default abstract class Figure { protected canvasRunner: Main, protected x: number, protected y: number, + id?: string, protected fill: string = 'black', protected stroke: string = 'black' ) { Figure.objectCount++; - this.id = - `object-${Figure.objectCount}` + Math.random().toString(36).slice(2, 9); + this.id = id || `object-${Figure.objectCount}`; this.canvasRunner = canvasRunner; this.p5 = this.canvasRunner.getP5Instance(); this.previousFill = this.fill; diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 26c899069..0b67b9e1b 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -46,10 +46,11 @@ export default class Line extends Figure { y: number, endX: number, endY: number, + id?: string, stroke?: string, hasArrow?: boolean ) { - super(canvasRunner, x, y, undefined, stroke); + super(canvasRunner, x, y, id, undefined, stroke); this.endX = endX; this.endY = endY; this.hasArrow = hasArrow ?? false; diff --git a/ee-workflow/src/components/figure/text.ts b/ee-workflow/src/components/figure/text.ts index 808e4b737..ae867baf4 100644 --- a/ee-workflow/src/components/figure/text.ts +++ b/ee-workflow/src/components/figure/text.ts @@ -41,10 +41,11 @@ export default class Text extends Figure { x: number, y: number, str: string, + id?: string, size?: number, fill?: string ) { - super(canvasRunnner, x, y, fill); + super(canvasRunnner, x, y, id, fill); this.str = str; this.size = size || 16; } diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index b726aec69..08beea9af 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -27,31 +27,113 @@ IGCanvas.setDelay(1); const IGFF = new FigureFactory(IGCanvas); IGCanvas.togglePause(); -const timeline = mainFF.line(0, 200, 1000, 200, 'black'); +const timeline = mainFF.line({ + x: 0, + y: 200, + endX: 1000, + endY: 200, + stroke: 'black', +}); const circles = [ - mainFF.circle(100, 200, 75, '#EDF2EF'), - mainFF.circle(300, 200, 75, '#EDF2EF'), - mainFF.circle(500, 200, 75, '#EDF2EF'), - mainFF.circle(700, 200, 75, '#EDF2EF'), + mainFF.circle({ + x: 100, + y: 200, + diameter: 75, + fill: '#EDF2EF', + }), + mainFF.circle({ + x: 300, + y: 200, + diameter: 75, + fill: '#EDF2EF', + }), + mainFF.circle({ + x: 500, + y: 200, + diameter: 75, + fill: '#EDF2EF', + }), + mainFF.circle({ + x: 700, + y: 200, + diameter: 75, + fill: '#EDF2EF', + }), ]; const textonCircles = [ - mainFF.text(100, 75, '2024-01-01'), - mainFF.text(100, 100, 'adv1.com'), - mainFF.text(300, 75, '2024-01-02'), - mainFF.text(300, 100, 'adv2.com'), - mainFF.text(500, 75, '2024-01-03'), - mainFF.text(500, 100, 'adv3.com'), - mainFF.text(700, 75, '2024-01-04'), - mainFF.text(700, 100, 'adv4.com'), + mainFF.text({ + x: 100, + y: 75, + text: '2024-01-01', + }), + mainFF.text({ + x: 100, + y: 100, + text: 'adv1.com', + }), + mainFF.text({ + x: 300, + y: 75, + text: '2024-01-02', + }), + mainFF.text({ + x: 300, + y: 100, + text: 'adv2.com', + }), + mainFF.text({ + x: 500, + y: 75, + text: '2024-01-03', + }), + mainFF.text({ + x: 500, + y: 100, + text: 'adv3.com', + }), + mainFF.text({ + x: 700, + y: 75, + text: '2024-01-04', + }), + mainFF.text({ + x: 700, + y: 100, + text: 'adv4.com', + }), ]; const circleToTextLine = [ - mainFF.line(100, 163, 100, 110, 'black'), - mainFF.line(300, 163, 300, 110, 'black'), - mainFF.line(500, 163, 500, 110, 'black'), - mainFF.line(700, 163, 700, 110, 'black'), + mainFF.line({ + x: 100, + y: 163, + endX: 100, + endY: 110, + stroke: 'black', + }), + mainFF.line({ + x: 300, + y: 163, + endX: 300, + endY: 110, + stroke: 'black', + }), + mainFF.line({ + x: 500, + y: 163, + endX: 500, + endY: 110, + stroke: 'black', + }), + mainFF.line({ + x: 700, + y: 163, + endX: 700, + endY: 110, + stroke: 'black', + }), ]; const userIcon = @@ -61,20 +143,83 @@ const completedIcon = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDhweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSI0OHB4IiBmaWxsPSIjNTk4NUUxIj48cGF0aCBkPSJNNDgwLTgwcS04NSAwLTE1OC0zMC41VDE5NS0xOTVxLTU0LTU0LTg0LjUtMTI3VDgwLTQ4MHEwLTg0IDMwLjUtMTU3VDE5NS03NjRxNTQtNTQgMTI3LTg1dDE1OC0zMXE3NSAwIDE0MCAyNHQxMTcgNjZsLTQzIDQzcS00NC0zNS05OC01NHQtMTE2LTE5cS0xNDUgMC0yNDIuNSA5Ny41VDE0MC00ODBxMCAxNDUgOTcuNSAyNDIuNVQ0ODAtMTQwcTE0NSAwIDI0Mi41LTk3LjVUODIwLTQ4MHEwLTMwLTQuNS01OC41VDgwMi01OTRsNDYtNDZxMTYgMzcgMjQgNzd0OCA4M3EwIDg1LTMxIDE1OHQtODUgMTI3cS01NCA1NC0xMjcgODQuNVQ0ODAtODBabS01OS0yMThMMjU2LTQ2NGw0NS00NSAxMjAgMTIwIDQxNC00MTQgNDYgNDUtNDYwIDQ2MFoiLz48L3N2Zz4='; const advertiserFlow = [ - mainFF.image(100, 200, userIcon, 50, 50), - mainFF.line(95, 237, 95, 300, 'black', true, true), + mainFF.image({ + x: 100, + y: 200, + imageData: userIcon, + width: 50, + height: 50, + }), + mainFF.line({ + x: 95, + y: 237, + endX: 95, + endY: 300, + stroke: 'black', + hasArrow: true, + shouldTravel: true, + }), new Group([ - mainFF.box(50, 300, 100, 50, '#EDF2EF'), - mainFF.text(100, 325, 'DSP tags'), + mainFF.box({ + x: 50, + y: 300, + width: 100, + height: 50, + fill: '#EDF2EF', + }), + mainFF.text({ + x: 100, + y: 325, + text: 'DSP tags', + }), ]), - mainFF.line(95, 350, 95, 413, 'black', true, true), + mainFF.line({ + x: 95, + y: 350, + endX: 95, + endY: 413, + stroke: 'black', + hasArrow: true, + shouldTravel: true, + }), new Group([ - mainFF.box(50, 413, 100, 50, '#EDF2EF'), - mainFF.text(100, 438, 'DSPs'), + mainFF.box({ + x: 50, + y: 413, + width: 100, + height: 50, + fill: '#EDF2EF', + }), + mainFF.text({ + x: 100, + y: 438, + text: 'DSPs', + }), ]), - mainFF.line(105, 413, 105, 350, 'black', true, true), - mainFF.line(105, 300, 105, 237, 'black', true, true), - mainFF.text(170, 270, 'joinInterestGroup()', 12), + mainFF.line({ + x: 105, + y: 413, + endX: 105, + endY: 350, + stroke: 'black', + hasArrow: true, + shouldTravel: true, + }), + mainFF.line({ + x: 105, + y: 300, + endX: 105, + endY: 237, + stroke: 'black', + hasArrow: true, + shouldTravel: true, + }), + mainFF.text({ + x: 170, + y: 270, + text: 'joinInterestGroup()', + size: 12, + }), ]; // Setup timeline. @@ -91,7 +236,13 @@ let startX = 170; let startY = 270; let lerpSpeed = 0.01; while (Math.floor(startX) > 0 && Math.floor(startY) > 0) { - const circle = IGFF.circle(startX, startY, 10, 'orange', 'black'); + const circle = IGFF.circle({ + x: startX, + y: startY, + diameter: 10, + fill: 'orange', + stroke: 'black', + }); bubbles.push(circle); startX = IGCanvas.getP5Instance().lerp(startX, 0, lerpSpeed); startY = IGCanvas.getP5Instance().lerp(startY, 0, lerpSpeed); @@ -117,36 +268,144 @@ mainCanvas.addAnimator(flow); mainCanvas.addGroup( new Group([ - mainFF.line(0, 200, 63, 200, 'blue'), - mainFF.circle(100, 200, 75, '#EDF2EF', 'blue'), - mainFF.image(100, 200, completedIcon, 50, 50), + mainFF.line({ + x: 0, + y: 200, + endX: 63, + endY: 200, + stroke: 'blue', + }), + mainFF.circle({ + x: 100, + y: 200, + diameter: 75, + fill: '#EDF2EF', + stroke: 'blue', + }), + mainFF.image({ + x: 100, + y: 200, + imageData: completedIcon, + width: 50, + height: 50, + }), ]) ); -const travellerLine = mainFF.line(0, 400, 1000, 600, 'red', false, true); +const travellerLine = mainFF.line({ + x: 0, + y: 400, + endX: 1000, + endY: 400, + stroke: 'red', + shouldTravel: true, +}); const travellerGroup = new Group([ - mainFF.line(0, 600, 1000, 800, 'blue', false), - mainFF.line(0, 450, 1000, 900, 'green', false, true), - mainFF.line(0, 500, 1000, 700, 'yellow', false, true), + mainFF.line({ + x: 0, + y: 600, + endX: 1000, + endY: 800, + stroke: 'blue', + }), + mainFF.line({ + x: 0, + y: 450, + endX: 1000, + endY: 900, + stroke: 'green', + shouldTravel: true, + }), + mainFF.line({ + x: 0, + y: 500, + endX: 1000, + endY: 700, + stroke: 'yellow', + shouldTravel: true, + }), ]); mainCanvas.addAnimator(new Animator([travellerLine, travellerGroup], mainFF)); const secondCircleAnimations = [ - mainFF.image(300, 200, userIcon, 50, 50), - mainFF.line(295, 237, 295, 300, 'black', true, true), + mainFF.image({ + x: 300, + y: 200, + imageData: userIcon, + width: 50, + height: 50, + }), + mainFF.line({ + x: 295, + y: 237, + endX: 295, + endY: 300, + stroke: 'black', + hasArrow: true, + shouldTravel: true, + }), new Group([ - mainFF.box(250, 300, 100, 50, '#EDF2EF'), - mainFF.text(300, 325, 'DSP tags'), + mainFF.box({ + x: 250, + y: 300, + width: 100, + height: 50, + fill: '#EDF2EF', + }), + mainFF.text({ + x: 300, + y: 325, + text: 'DSP tags', + }), ]), - mainFF.line(295, 350, 295, 413, 'black', true, true), + mainFF.line({ + x: 295, + y: 350, + endX: 295, + endY: 413, + stroke: 'black', + hasArrow: true, + shouldTravel: true, + }), new Group([ - mainFF.box(250, 413, 100, 50, '#EDF2EF'), - mainFF.text(300, 438, 'DSPs'), + mainFF.box({ + x: 250, + y: 413, + width: 100, + height: 50, + fill: '#EDF2EF', + }), + mainFF.text({ + x: 300, + y: 438, + text: 'DSPs', + }), ]), - mainFF.line(305, 413, 305, 350, 'black', true, true), - mainFF.line(305, 300, 305, 237, 'black', true, true), - mainFF.text(370, 270, 'joinInterestGroup()', 12), + mainFF.line({ + x: 305, + y: 413, + endX: 305, + endY: 350, + stroke: 'black', + hasArrow: true, + shouldTravel: true, + }), + mainFF.line({ + x: 305, + y: 300, + endX: 305, + endY: 237, + stroke: 'black', + hasArrow: true, + shouldTravel: true, + }), + mainFF.text({ + x: 370, + y: 270, + text: 'joinInterestGroup()', + size: 12, + }), ]; const secondBubbles = []; @@ -154,7 +413,14 @@ let secondStartX = 370; let secondStartY = 270; lerpSpeed = 0.01; while (Math.floor(secondStartX) > 0 && Math.floor(secondStartY) > 0) { - const circle = IGFF.circle(secondStartX, secondStartY, 10, 'orange', 'black'); + const circle = IGFF.circle({ + x: secondStartX, + y: secondStartY, + diameter: 10, + fill: 'orange', + stroke: 'black', + }); + secondBubbles.push(circle); secondStartX = IGCanvas.getP5Instance().lerp(secondStartX, 0, lerpSpeed); secondStartY = IGCanvas.getP5Instance().lerp(secondStartY, 0, lerpSpeed); @@ -179,62 +445,26 @@ mainCanvas.addAnimator(secondFlow); mainCanvas.addGroup( new Group([ - mainFF.line(137, 200, 263, 200, 'blue'), - mainFF.circle(300, 200, 75, '#EDF2EF', 'blue'), - mainFF.image(300, 200, completedIcon, 50, 50), + mainFF.line({ + x: 137, + y: 200, + endX: 263, + endY: 200, + stroke: 'blue', + }), + mainFF.circle({ + x: 300, + y: 200, + diameter: 75, + fill: '#EDF2EF', + stroke: 'blue', + }), + mainFF.image({ + x: 300, + y: 200, + imageData: completedIcon, + width: 50, + height: 50, + }), ]) ); - -// const thirdCircleAnimations = [ -// mainFF.image(500, 200, userIcon, 50, 50), -// mainFF.line(495, 237, 495, 300, 'black', true), -// new Group([ -// mainFF.box(450, 300, 100, 50, '#EDF2EF'), -// mainFF.text(500, 325, 'DSP tags'), -// ]), -// mainFF.line(495, 350, 495, 413, 'black', true), -// new Group([ -// mainFF.box(450, 413, 100, 50, '#EDF2EF'), -// mainFF.text(500, 438, 'DSPs'), -// ]), -// mainFF.line(505, 413, 505, 350, 'black', true), -// mainFF.line(505, 300, 505, 237, 'black', true), -// mainFF.text(570, 270, 'joinInterestGroup()', 12), -// ]; - -// mainCanvas.addAnimator(new Animator(thirdCircleAnimations, mainFF)); - -// mainCanvas.addGroup( -// new Group([ -// mainFF.line(337, 200, 463, 200, 'blue'), -// mainFF.circle(500, 200, 75, '#EDF2EF', 'blue'), -// mainFF.image(500, 200, completedIcon, 50, 50), -// ]) -// ); - -// const fourthCircleAnimations = [ -// mainFF.image(700, 200, userIcon, 50, 50), -// mainFF.line(695, 237, 695, 300, 'black', true), -// new Group([ -// mainFF.box(650, 300, 100, 50, '#EDF2EF'), -// mainFF.text(700, 325, 'DSP tags'), -// ]), -// mainFF.line(695, 350, 695, 413, 'black', true), -// new Group([ -// mainFF.box(650, 413, 100, 50, '#EDF2EF'), -// mainFF.text(700, 438, 'DSPs'), -// ]), -// mainFF.line(705, 413, 705, 350, 'black', true), -// mainFF.line(705, 300, 705, 237, 'black', true), -// mainFF.text(770, 270, 'joinInterestGroup()', 12), -// ]; - -// mainCanvas.addAnimator(new Animator(fourthCircleAnimations, mainFF)); - -// mainCanvas.addGroup( -// new Group([ -// mainFF.line(537, 200, 663, 200, 'blue'), -// mainFF.circle(700, 200, 75, '#EDF2EF', 'blue'), -// mainFF.image(700, 200, completedIcon, 50, 50), -// ]) -// ); From 0027917057006f66f3b78e62a9f60426c59b5244 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 7 Jan 2025 16:33:19 +0530 Subject: [PATCH 32/44] Add optional id param to group and animator class --- ee-workflow/src/components/animator.ts | 9 +++++++-- ee-workflow/src/components/group.ts | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts index aa28a5899..f5a10db16 100644 --- a/ee-workflow/src/components/animator.ts +++ b/ee-workflow/src/components/animator.ts @@ -58,11 +58,16 @@ export default class Animator { */ static animationCounter = 0; - constructor(objects: Array
, figureFactory: FigureFactory) { + constructor( + objects: Array
, + figureFactory: FigureFactory, + id?: string + ) { Animator.animationCounter++; this.id = + id || `animation-${Animator.animationCounter}` + - Math.random().toString(36).slice(2, 9); + Math.random().toString(36).slice(2, 9); this.objects = [ ...objects, figureFactory.line({ diff --git a/ee-workflow/src/components/group.ts b/ee-workflow/src/components/group.ts index c80da47a3..fc39e72ef 100644 --- a/ee-workflow/src/components/group.ts +++ b/ee-workflow/src/components/group.ts @@ -49,9 +49,10 @@ export default class Group { */ static groupCount = 0; - constructor(figures: Figure[]) { + constructor(figures: Figure[], id?: string) { Group.groupCount++; this.id = + id || `group-${Group.groupCount}` + Math.random().toString(36).slice(2, 9); this.figures = figures; this.figures.forEach((figure) => figure.setGid(this.id)); From 3c4cf2d66c7823276e2931cc85267cf3bc2dda82 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 7 Jan 2025 16:37:34 +0530 Subject: [PATCH 33/44] Update aid to animatorId, gid to GroupId and update the getter/setters --- ee-workflow/src/components/animator.ts | 2 +- ee-workflow/src/components/figure/index.ts | 24 +++++++-------- ee-workflow/src/components/group.ts | 16 +++++----- ee-workflow/src/main.ts | 34 +++++++++++----------- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts index f5a10db16..baa342d01 100644 --- a/ee-workflow/src/components/animator.ts +++ b/ee-workflow/src/components/animator.ts @@ -77,7 +77,7 @@ export default class Animator { endY: 0, }), // last dummy object acts as a placeholder for the end of the animation ]; - this.objects.forEach((object) => object.setAid(this.id)); + this.objects.forEach((object) => object.setAnimatorId(this.id)); } /** diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index dfb6a57c2..f0bd8da5e 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -41,12 +41,12 @@ export default abstract class Figure { /** * Group id of the figure if it belongs to a group. */ - protected gid = ''; + protected groupId = ''; /** * Animator id of the figure if it belongs to an animator. */ - protected aid = ''; + protected animatorId = ''; /** * Previous fill color of the figure to be used when the figure is unhovered. @@ -203,32 +203,32 @@ export default abstract class Figure { * Get the group id of the figure. * @returns The group id of the figure. */ - getGid(): string { - return this.gid; + getGroupId(): string { + return this.groupId; } /** * Set the group id of the figure. - * @param gid - The group id to set. + * @param groupId - The group id to set. */ - setGid(gid: string) { - this.gid = gid; + setGroupId(groupId: string) { + this.groupId = groupId; } /** * Get the animator id of the figure. * @returns The animator id of the */ - getAid(): string { - return this.aid; + getAnimatorId(): string { + return this.animatorId; } /** * Set the animator id of the figure. - * @param aid - The animator id to set. + * @param animatorId - The animator id to set. */ - setAid(aid: string) { - this.aid = aid; + setAnimatorId(animatorId: string) { + this.animatorId = animatorId; } /** diff --git a/ee-workflow/src/components/group.ts b/ee-workflow/src/components/group.ts index fc39e72ef..ace468564 100644 --- a/ee-workflow/src/components/group.ts +++ b/ee-workflow/src/components/group.ts @@ -36,7 +36,7 @@ export default class Group { /** * Animator id of the group if it belongs to an animator. */ - private aid = ''; + private animatorId = ''; /** * Property to check if the group should be saved in groupSnapshot. @@ -55,7 +55,7 @@ export default class Group { id || `group-${Group.groupCount}` + Math.random().toString(36).slice(2, 9); this.figures = figures; - this.figures.forEach((figure) => figure.setGid(this.id)); + this.figures.forEach((figure) => figure.setGroupId(this.id)); } /** @@ -106,17 +106,17 @@ export default class Group { * Method to get the animator id of the group. * @returns The animator id of the group. */ - getAid() { - return this.aid; + getAnimatorId() { + return this.animatorId; } /** * Method to set the animator id to the group and its figures. - * @param aid - The animator id of the group. + * @param animatorId - The animator id of the group. */ - setAid(aid: string) { - this.aid = aid; - this.figures.forEach((figure) => figure.setAid(aid)); + setAnimatorId(animatorId: string) { + this.animatorId = animatorId; + this.figures.forEach((figure) => figure.setAnimatorId(animatorId)); } /** diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 85776281f..ab27e9b2d 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -195,12 +195,12 @@ class Main { if ( (firstObject.getShouldTravel() || - (firstObject.getGid() && groupQueue[0].getShouldTravel())) && + (firstObject.getGroupId() && groupQueue[0].getShouldTravel())) && !useInstantQueue ) { this.isTravelling = true; - if (firstObject.getGid()) { + if (firstObject.getGroupId()) { this.traveller = new Traveller(groupQueue[0]); return; @@ -212,13 +212,13 @@ class Main { return; } - if (firstObject.getAid()) { + if (firstObject.getAnimatorId()) { const animator = animatorQueue[0]; if (animator) { const isDone = animator.draw(); - if (firstObject.getGid()) { + if (firstObject.getGroupId()) { this.processGroup(queue, groupQueue, false); } else { if (!firstObject.getThrow()) { @@ -232,7 +232,7 @@ class Main { this.reDrawAll(); } } - } else if (firstObject.getGid()) { + } else if (firstObject.getGroupId()) { this.processGroup(queue, groupQueue); } else { firstObject.draw(); @@ -289,12 +289,12 @@ class Main { * @returns The group the figure belongs to, or undefined. */ private isGrouped(object: Figure): Group | undefined { - if (!object.getGid()) { + if (!object.getGroupId()) { return undefined; } return this.groupSnapshot.find( - (group) => group.getId() === object.getGid() + (group) => group.getId() === object.getGroupId() ); } @@ -418,9 +418,9 @@ class Main { for (let i = 0; i < this.snapshot.length; i++) { const figure = this.snapshot[i]; - if (figure.getAid()) { + if (figure.getAnimatorId()) { const animator = this.animatorSnapshot.find( - (a) => a.getId() === figure.getAid() + (a) => a.getId() === figure.getAnimatorId() ); if (animator && animatorIdToDraw === animator.getId()) { @@ -439,9 +439,9 @@ class Main { }, 0) || 1; i += toRemoveCount - 1; - } else if (figure.getGid()) { + } else if (figure.getGroupId()) { const group = this.groupSnapshot.find( - (g) => g.getId() === figure.getGid() + (g) => g.getId() === figure.getGroupId() ); if (group) { @@ -520,16 +520,16 @@ class Main { removeFigure(figure: Figure) { this.snapshot = this.snapshot.filter((f) => f.getId() !== figure.getId()); - if (figure.getAid()) { + if (figure.getAnimatorId()) { const animator = this.animatorSnapshot.find( - (a) => a.getId() !== figure.getAid() + (a) => a.getId() !== figure.getAnimatorId() ); animator?.removeObject(figure); } - if (figure.getGid()) { + if (figure.getGroupId()) { const group = this.groupSnapshot.find( - (g) => g.getId() === figure.getGid() + (g) => g.getId() === figure.getGroupId() ); group?.removeFigure(figure); } @@ -554,7 +554,7 @@ class Main { }); toRemove?.getFigures().forEach((figure) => { - figure.setGid(''); + figure.setGroupId(''); this.removeFigure(figure); }); } @@ -576,7 +576,7 @@ class Main { }); toRemove?.getObjects().forEach((object) => { - object.setAid(''); + object.setAnimatorId(''); if (object instanceof Figure) { this.removeFigure(object); From a2b189cabf5a1e3d6f90254a2558da7d5970c2f5 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 7 Jan 2025 17:01:29 +0530 Subject: [PATCH 34/44] Add tags var, callbacks and update figure factory --- ee-workflow/src/components/figure/box.ts | 5 +- ee-workflow/src/components/figure/circle.ts | 5 +- .../src/components/figure/figureFactory.ts | 84 ++++++++++++------- ee-workflow/src/components/figure/image.ts | 5 +- ee-workflow/src/components/figure/index.ts | 38 ++++++++- ee-workflow/src/components/figure/line.ts | 5 +- ee-workflow/src/components/figure/text.ts | 5 +- 7 files changed, 105 insertions(+), 42 deletions(-) diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 67bf835cb..384d47378 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -44,9 +44,10 @@ export default class Box extends Figure { height: number, id?: string, fill?: string, - stroke?: string + stroke?: string, + tags?: string[] ) { - super(canvasRuuner, x, y, id, fill, stroke); + super(canvasRuuner, x, y, id, fill, stroke, tags); this.width = width; this.height = height; } diff --git a/ee-workflow/src/components/figure/circle.ts b/ee-workflow/src/components/figure/circle.ts index f71353302..deaaf2870 100644 --- a/ee-workflow/src/components/figure/circle.ts +++ b/ee-workflow/src/components/figure/circle.ts @@ -38,9 +38,10 @@ export default class Circle extends Figure { diameter: number, id?: string, fill?: string, - stroke?: string + stroke?: string, + tags?: string[] ) { - super(canvasRunner, x, y, id, fill, stroke); + super(canvasRunner, x, y, id, fill, stroke, tags); this.diameter = diameter; } diff --git a/ee-workflow/src/components/figure/figureFactory.ts b/ee-workflow/src/components/figure/figureFactory.ts index 119a0536b..d3eeae39b 100644 --- a/ee-workflow/src/components/figure/figureFactory.ts +++ b/ee-workflow/src/components/figure/figureFactory.ts @@ -25,6 +25,15 @@ import Image from './image'; import Line from './line'; import Text from './text'; +type FigureParams = { + id?: string; + x: number; + y: number; + fill?: string; + stroke?: string; + tags?: string[]; +}; + export default class FigureFactory { private canvasRunner: Main; @@ -40,16 +49,22 @@ export default class FigureFactory { height, fill, stroke, - }: { - id?: string; - x: number; - y: number; + tags, + }: FigureParams & { width: number; height: number; - fill?: string; - stroke?: string; }): Box { - return new Box(this.canvasRunner, x, y, width, height, id, fill, stroke); + return new Box( + this.canvasRunner, + x, + y, + width, + height, + id, + fill, + stroke, + tags + ); } circle({ @@ -59,15 +74,20 @@ export default class FigureFactory { diameter, fill, stroke, - }: { - id?: string; - x: number; - y: number; + tags, + }: FigureParams & { diameter: number; - fill?: string; - stroke?: string; }): Circle { - return new Circle(this.canvasRunner, x, y, diameter, id, fill, stroke); + return new Circle( + this.canvasRunner, + x, + y, + diameter, + id, + fill, + stroke, + tags + ); } image({ @@ -77,15 +97,22 @@ export default class FigureFactory { imageData, width, height, - }: { - id?: string; - x: number; - y: number; + tags, + }: FigureParams & { imageData: string; width: number; height: number; }): Image { - return new Image(this.canvasRunner, x, y, imageData, width, height, id); + return new Image( + this.canvasRunner, + x, + y, + imageData, + width, + height, + id, + tags + ); } line({ @@ -97,13 +124,10 @@ export default class FigureFactory { stroke, hasArrow, shouldTravel, - }: { - id?: string; - x: number; - y: number; + tags, + }: FigureParams & { endX: number; endY: number; - stroke?: string; hasArrow?: boolean; shouldTravel?: boolean; }): Line { @@ -115,7 +139,8 @@ export default class FigureFactory { endY, id, stroke, - hasArrow + hasArrow, + tags ); if (shouldTravel) { @@ -160,14 +185,11 @@ export default class FigureFactory { text, size, fill, - }: { - id?: string; - x: number; - y: number; + tags, + }: FigureParams & { text: string; size?: number; - fill?: string; }): Text { - return new Text(this.canvasRunner, x, y, text, id, size, fill); + return new Text(this.canvasRunner, x, y, text, id, size, fill, tags); } } diff --git a/ee-workflow/src/components/figure/image.ts b/ee-workflow/src/components/figure/image.ts index f5178ec38..4f9045747 100644 --- a/ee-workflow/src/components/figure/image.ts +++ b/ee-workflow/src/components/figure/image.ts @@ -52,9 +52,10 @@ export default class Image extends Figure { imageData: string, width: number, height: number, - id?: string + id?: string, + tags?: string[] ) { - super(canvasRunner, x, y, id); + super(canvasRunner, x, y, id, undefined, undefined, tags); this.image = this.p5?.loadImage(imageData); this.width = width; this.height = height; diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index f0bd8da5e..89558ecd9 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -88,6 +88,7 @@ export default abstract class Figure { * @param id - The unique id of the figure. * @param fill - The fill color of the figure. * @param stroke - The stroke color of the figure. + * @param tags - The tags of the figure for bucketing. */ constructor( protected canvasRunner: Main, @@ -95,7 +96,8 @@ export default abstract class Figure { protected y: number, id?: string, protected fill: string = 'black', - protected stroke: string = 'black' + protected stroke: string = 'black', + protected tags: string[] = [] ) { Figure.objectCount++; this.id = id || `object-${Figure.objectCount}`; @@ -248,6 +250,40 @@ export default abstract class Figure { this.throw = throwFlag; } + /** + * Get the tags of the figure. + * @returns The tags of the figure. + */ + getTags(): string[] { + return this.tags; + } + + /** + * Set the tags of the figure. + * @param tags - The tags to set. + */ + setTags(tags: string[]) { + this.tags = tags; + } + + /** + * Add a tag to the figure. + * @param tag - The tag to add. + */ + addTag(tag: string) { + if (!this.tags.includes(tag)) { + this.tags.push(tag); + } + } + + /** + * Remove a tag from the figure. + * @param tag - The tag to remove. + */ + removeTag(tag: string) { + this.tags = this.tags.filter((t) => t !== tag); + } + /** * Save the previous fill and stroke colors. */ diff --git a/ee-workflow/src/components/figure/line.ts b/ee-workflow/src/components/figure/line.ts index 0b67b9e1b..3c82b36e4 100644 --- a/ee-workflow/src/components/figure/line.ts +++ b/ee-workflow/src/components/figure/line.ts @@ -48,9 +48,10 @@ export default class Line extends Figure { endY: number, id?: string, stroke?: string, - hasArrow?: boolean + hasArrow?: boolean, + tags?: string[] ) { - super(canvasRunner, x, y, id, undefined, stroke); + super(canvasRunner, x, y, id, undefined, stroke, tags); this.endX = endX; this.endY = endY; this.hasArrow = hasArrow ?? false; diff --git a/ee-workflow/src/components/figure/text.ts b/ee-workflow/src/components/figure/text.ts index ae867baf4..f688ab1e0 100644 --- a/ee-workflow/src/components/figure/text.ts +++ b/ee-workflow/src/components/figure/text.ts @@ -43,9 +43,10 @@ export default class Text extends Figure { str: string, id?: string, size?: number, - fill?: string + fill?: string, + tags?: string[] ) { - super(canvasRunnner, x, y, id, fill); + super(canvasRunnner, x, y, id, fill, undefined, tags); this.str = str; this.size = size || 16; } From 6bf4d001ff3014f5d26fedbd4d374aeac618f26b Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 7 Jan 2025 17:02:15 +0530 Subject: [PATCH 35/44] Update condition check for undefined case --- ee-workflow/src/main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index ab27e9b2d..f11c44123 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -194,9 +194,9 @@ class Main { const firstObject =
queue.shift(); if ( + !useInstantQueue && (firstObject.getShouldTravel() || - (firstObject.getGroupId() && groupQueue[0].getShouldTravel())) && - !useInstantQueue + (firstObject.getGroupId() && groupQueue?.[0]?.getShouldTravel())) ) { this.isTravelling = true; From 499f1963d2506b490835c6aa66f25836d8d13f78 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Wed, 8 Jan 2025 10:57:51 +0530 Subject: [PATCH 36/44] Update callback name for resetting queue and redrawing and update calls --- ee-workflow/src/index.ts | 4 ++-- ee-workflow/src/main.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 08beea9af..fa0004217 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -251,7 +251,7 @@ while (Math.floor(startX) > 0 && Math.floor(startY) > 0) { const bubbleFlow = new Animator(bubbles, mainFF); bubbleFlow.setSideEffectOnEnd(() => { - IGCanvas.resetAndReDrawAll(); + IGCanvas.resetQueuesAndReDrawAll(); IGCanvas.togglePause(); mainCanvas.togglePause(); mainCanvas.reDrawAll(); @@ -429,7 +429,7 @@ while (Math.floor(secondStartX) > 0 && Math.floor(secondStartY) > 0) { const secondBubbleFlow = new Animator(secondBubbles, mainFF); secondBubbleFlow.setSideEffectOnEnd(() => { - IGCanvas.resetAndReDrawAll(); + IGCanvas.resetQueuesAndReDrawAll(); IGCanvas.togglePause(); mainCanvas.togglePause(); mainCanvas.reDrawAll(); diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index f11c44123..e53c9d90d 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -392,7 +392,7 @@ class Main { /** * Resets the queues and redraws all figures on the canvas. */ - resetAndReDrawAll() { + resetQueuesAndReDrawAll() { if (this.pause) { return; } From 7d5b8ccafd251060fbc645ccfd1c5d671028aa36 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 9 Jan 2025 17:13:52 +0530 Subject: [PATCH 37/44] Add logic to handle prev button api, where figures are considered as checkpoints, add logic to reset traveller to default, add checkpoints in animation, and custom click cb for figurefactory --- ee-workflow/src/components/animator.ts | 7 + ee-workflow/src/components/figure/box.ts | 10 +- .../src/components/figure/figureFactory.ts | 14 +- ee-workflow/src/components/figure/index.ts | 48 +++ ee-workflow/src/index.ts | 236 ++++++++------- ee-workflow/src/main.ts | 281 ++++++++++++++++-- 6 files changed, 455 insertions(+), 141 deletions(-) diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts index baa342d01..52e59daf1 100644 --- a/ee-workflow/src/components/animator.ts +++ b/ee-workflow/src/components/animator.ts @@ -98,6 +98,13 @@ export default class Animator { return false; } + /** + * Resets the index of the animator to 0. + */ + resetIndex() { + this.index = 0; + } + /** * Sets a side effect to be executed when the animation ends * @param sideEffect - function to be executed when the animation ends diff --git a/ee-workflow/src/components/figure/box.ts b/ee-workflow/src/components/figure/box.ts index 384d47378..073083196 100644 --- a/ee-workflow/src/components/figure/box.ts +++ b/ee-workflow/src/components/figure/box.ts @@ -36,6 +36,11 @@ export default class Box extends Figure { */ private height: number; + /** + * Callback defined by the user to be executed when the box is clicked. + */ + private customMouseClicked: (() => void) | undefined; + constructor( canvasRuuner: Main, x: number, @@ -45,11 +50,13 @@ export default class Box extends Figure { id?: string, fill?: string, stroke?: string, - tags?: string[] + tags?: string[], + mouseClicked?: () => void ) { super(canvasRuuner, x, y, id, fill, stroke, tags); this.width = width; this.height = height; + this.customMouseClicked = mouseClicked; } draw() { @@ -80,6 +87,7 @@ export default class Box extends Figure { mouseClicked() { // TODO: Discuss the function + this.customMouseClicked?.(); } isHovering(): boolean { diff --git a/ee-workflow/src/components/figure/figureFactory.ts b/ee-workflow/src/components/figure/figureFactory.ts index d3eeae39b..ea0d6a4bf 100644 --- a/ee-workflow/src/components/figure/figureFactory.ts +++ b/ee-workflow/src/components/figure/figureFactory.ts @@ -32,6 +32,7 @@ type FigureParams = { fill?: string; stroke?: string; tags?: string[]; + mouseClicked?: () => void; }; export default class FigureFactory { @@ -50,6 +51,7 @@ export default class FigureFactory { fill, stroke, tags, + mouseClicked, }: FigureParams & { width: number; height: number; @@ -63,7 +65,8 @@ export default class FigureFactory { id, fill, stroke, - tags + tags, + mouseClicked ); } @@ -172,7 +175,16 @@ export default class FigureFactory { return false; }; + const resetTravel = (figure: Figure) => { + const _figure = figure; + currentX = x; + currentY = y; + _figure.setEndX(currentX); + _figure.setEndY(currentY); + }; + line.setTraveller(traveller); + line.setResetTravel(resetTravel); } return line; diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index 89558ecd9..eeb26bf10 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -75,6 +75,13 @@ export default abstract class Figure { */ protected traveller: ((figure: Figure) => boolean) | undefined; + /** + * Function to be executed when the object has to reset travel. + */ + protected resetTravel: ((figure: Figure) => void) | undefined; + + protected isCheckpoint = false; + /** * The number of objects created. */ @@ -157,6 +164,14 @@ export default abstract class Figure { this.canvasRunner.removeFigure(this); } + /** + * Method to get whether the figure have traveller function. + * @returns boolean indicating if the figure have traveller function. + */ + getCanTravel(): boolean { + return this.traveller !== undefined; + } + /** * Method to get the shouldTravel property. * @returns boolean indicating if the figure should travel. @@ -185,6 +200,15 @@ export default abstract class Figure { return true; } + /** + * Method to call the reset traveller function. + */ + resetTraveller() { + if (this.resetTravel) { + this.resetTravel(this); + } + } + /** * Method to set the traveller function. * @param traveller - The traveller function to set. @@ -193,6 +217,14 @@ export default abstract class Figure { this.traveller = traveller; } + /** + * Method to set the reset traveller function. + * @param resetTravel - The reset traveller function to set. + */ + setResetTravel(resetTravel: (figure: Figure) => void) { + this.resetTravel = resetTravel; + } + /** * Get the unique id of the figure. * @returns The unique id of the figure. @@ -284,6 +316,22 @@ export default abstract class Figure { this.tags = this.tags.filter((t) => t !== tag); } + /** + * Set the figure as a checkpoint. + * @param isCheckpoint - boolean indicating if the figure is a checkpoint. + */ + setCheckpoint(isCheckpoint: boolean) { + this.isCheckpoint = isCheckpoint; + } + + /** + * Get whether the figure is a checkpoint. + * @returns - boolean indicating if the figure is a checkpoint. + */ + getIsCheckpoint() { + return this.isCheckpoint; + } + /** * Save the previous fill and stroke colors. */ diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index fa0004217..1027e626e 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -27,113 +27,130 @@ IGCanvas.setDelay(1); const IGFF = new FigureFactory(IGCanvas); IGCanvas.togglePause(); +const button = mainFF.box({ + x: 10, + y: 10, + width: 100, + height: 50, + fill: 'blue', + stroke: 'black', + mouseClicked: () => { + mainCanvas.loadPreviousCheckpoint(); + }, +}); + +mainCanvas.addFigure(button); + const timeline = mainFF.line({ x: 0, y: 200, endX: 1000, endY: 200, stroke: 'black', + shouldTravel: true, }); const circles = [ - mainFF.circle({ - x: 100, - y: 200, - diameter: 75, - fill: '#EDF2EF', - }), - mainFF.circle({ - x: 300, - y: 200, - diameter: 75, - fill: '#EDF2EF', - }), - mainFF.circle({ - x: 500, - y: 200, - diameter: 75, - fill: '#EDF2EF', - }), - mainFF.circle({ - x: 700, - y: 200, - diameter: 75, - fill: '#EDF2EF', - }), -]; - -const textonCircles = [ - mainFF.text({ - x: 100, - y: 75, - text: '2024-01-01', - }), - mainFF.text({ - x: 100, - y: 100, - text: 'adv1.com', - }), - mainFF.text({ - x: 300, - y: 75, - text: '2024-01-02', - }), - mainFF.text({ - x: 300, - y: 100, - text: 'adv2.com', - }), - mainFF.text({ - x: 500, - y: 75, - text: '2024-01-03', - }), - mainFF.text({ - x: 500, - y: 100, - text: 'adv3.com', - }), - mainFF.text({ - x: 700, - y: 75, - text: '2024-01-04', - }), - mainFF.text({ - x: 700, - y: 100, - text: 'adv4.com', - }), -]; - -const circleToTextLine = [ - mainFF.line({ - x: 100, - y: 163, - endX: 100, - endY: 110, - stroke: 'black', - }), - mainFF.line({ - x: 300, - y: 163, - endX: 300, - endY: 110, - stroke: 'black', - }), - mainFF.line({ - x: 500, - y: 163, - endX: 500, - endY: 110, - stroke: 'black', - }), - mainFF.line({ - x: 700, - y: 163, - endX: 700, - endY: 110, - stroke: 'black', - }), + new Group([ + mainFF.circle({ + x: 100, + y: 200, + diameter: 75, + fill: '#EDF2EF', + }), + mainFF.text({ + x: 100, + y: 75, + text: '2024-01-01', + }), + mainFF.text({ + x: 100, + y: 100, + text: 'adv1.com', + }), + mainFF.line({ + x: 100, + y: 163, + endX: 100, + endY: 110, + stroke: 'black', + }), + ]), + new Group([ + mainFF.circle({ + x: 300, + y: 200, + diameter: 75, + fill: '#EDF2EF', + }), + mainFF.text({ + x: 300, + y: 75, + text: '2024-01-02', + }), + mainFF.text({ + x: 300, + y: 100, + text: 'adv2.com', + }), + mainFF.line({ + x: 300, + y: 163, + endX: 300, + endY: 110, + stroke: 'black', + }), + ]), + new Group([ + mainFF.circle({ + x: 500, + y: 200, + diameter: 75, + fill: '#EDF2EF', + }), + mainFF.text({ + x: 500, + y: 75, + text: '2024-01-03', + }), + mainFF.text({ + x: 500, + y: 100, + text: 'adv3.com', + }), + mainFF.line({ + x: 500, + y: 163, + endX: 500, + endY: 110, + stroke: 'black', + }), + ]), + new Group([ + mainFF.circle({ + x: 700, + y: 200, + diameter: 75, + fill: '#EDF2EF', + }), + mainFF.text({ + x: 700, + y: 75, + text: '2024-01-04', + }), + mainFF.text({ + x: 700, + y: 100, + text: 'adv4.com', + }), + mainFF.line({ + x: 700, + y: 163, + endX: 700, + endY: 110, + stroke: 'black', + }), + ]), ]; const userIcon = @@ -223,10 +240,8 @@ const advertiserFlow = [ ]; // Setup timeline. -mainCanvas.addFigure(timeline, true); -circles.forEach((circle) => mainCanvas.addFigure(circle, true)); -textonCircles.forEach((text) => mainCanvas.addFigure(text, true)); -circleToTextLine.forEach((line) => mainCanvas.addFigure(line, true)); +mainCanvas.addFigure(timeline); +circles.forEach((circle) => mainCanvas.addGroup(circle)); // add IG bubble canvas here // pause the main canvas operations @@ -264,7 +279,7 @@ flow.setSideEffectOnEnd(() => { mainCanvas.togglePause(); IGCanvas.togglePause(); }); -mainCanvas.addAnimator(flow); +mainCanvas.addAnimator(flow, false, true); mainCanvas.addGroup( new Group([ @@ -289,7 +304,8 @@ mainCanvas.addGroup( width: 50, height: 50, }), - ]) + ]), + false ); const travellerLine = mainFF.line({ @@ -300,6 +316,7 @@ const travellerLine = mainFF.line({ stroke: 'red', shouldTravel: true, }); + const travellerGroup = new Group([ mainFF.line({ x: 0, @@ -326,7 +343,11 @@ const travellerGroup = new Group([ }), ]); -mainCanvas.addAnimator(new Animator([travellerLine, travellerGroup], mainFF)); +mainCanvas.addAnimator( + new Animator([travellerLine, travellerGroup], mainFF), + false, + true +); const secondCircleAnimations = [ mainFF.image({ @@ -441,7 +462,7 @@ secondFlow.setSideEffectOnEnd(() => { IGCanvas.togglePause(); mainCanvas.togglePause(); }); -mainCanvas.addAnimator(secondFlow); +mainCanvas.addAnimator(secondFlow, false, true); mainCanvas.addGroup( new Group([ @@ -466,5 +487,6 @@ mainCanvas.addGroup( width: 50, height: 50, }), - ]) + ]), + false ); diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index e53c9d90d..7e7b00729 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -91,6 +91,11 @@ class Main { */ private animatorSnapshot: Animator[] = []; + /** + * Set of checkpoints to be used for save points that can be loaded. + */ + private checkpoints: Set = new Set(); + /** * Background color of the canvas. */ @@ -178,10 +183,6 @@ class Main { * @param useInstantQueue - Whether to use the instant queue. */ private runner(useInstantQueue = false) { - if (this.pause) { - return; - } - const queue = useInstantQueue ? this.instantQueue : this.stepsQueue; const groupQueue = useInstantQueue ? this.groupInstantQueue @@ -240,7 +241,28 @@ class Main { this.saveToSnapshot(firstObject); } } + + if (firstObject.getIsCheckpoint()) { + this.checkpoints.add(firstObject.getId()); + } + } + } + + private runTraveller() { + const done = this.traveller?.draw(); + + if (done) { + this.isTravelling = false; + const object = this.traveller?.getObject(); + + if (object instanceof Figure) { + this.stepsQueue.unshift(object); + } else if (object?.getFigures().length) { + this.stepsQueue.unshift(object.getFigures()[0]); + } } + + return done; } /** @@ -252,26 +274,11 @@ class Main { } if (this.isTravelling) { - const done = this.traveller?.draw(); - - if (done) { - this.isTravelling = false; - const object = this.traveller?.getObject(); - - if (object instanceof Figure) { - this.stepsQueue.unshift(object); - } else if (object?.getFigures().length) { - this.stepsQueue.unshift(object.getFigures()[0]); - } - + if (this.runTraveller()) { this.traveller = null; this.runner(); } - - return; - } - - if (this.p5.frameCount % this.delay === 0) { + } else if (this.p5.frameCount % this.delay === 0) { if (!this.clearOnEachStep) { this.p5.clear(); } @@ -357,6 +364,197 @@ class Main { return this.pause; } + /** + * If a animator is still rendering, it will be reset and the already rendered figures/group will be removed from the snapshot and readded to the queue. + * @param checkpoint - The checkpoint to load. + * @returns - Whether the checkpoint was part of the animator. + */ + private handleAnimatorOnPreviousCheckpointLoad(checkpoint: string): boolean { + if (this.snapshot.length === 0) { + return false; + } + + const lastSnapshotObject = this.snapshot[this.snapshot.length - 1]; + + if (!lastSnapshotObject.getAnimatorId()) { + return false; + } + + const animatorInSnapshot = this.animatorSnapshot.find( + (animator) => animator.getId() === lastSnapshotObject.getAnimatorId() + ); + + if (animatorInSnapshot) { + return false; + } + + let isCheckpointLoaded = false; + const toBeLoadedFigures = []; + let found = false; + + while (!found && this.snapshot.length) { + const figure = this.snapshot.pop(); + + if ( + figure && + figure.getAnimatorId() !== lastSnapshotObject.getAnimatorId() + ) { + this.snapshot.push(figure); + found = true; + break; + } + + if (figure) { + toBeLoadedFigures.push(figure); + figure.setThrow(false); + + if (figure.getCanTravel()) { + figure.setShouldTravel(true); + figure.resetTraveller(); + } + + if (figure.getId()) { + isCheckpointLoaded = figure.getId() === checkpoint; + } + } + } + + const toBeLoadedGroups = []; + found = false; + + while (!found && this.groupSnapshot.length) { + const group = this.groupSnapshot.pop(); + + if ( + group && + group.getAnimatorId() !== lastSnapshotObject.getAnimatorId() + ) { + this.groupSnapshot.push(group); + found = true; + break; + } + + if (group) { + toBeLoadedGroups.push(group); + group.setThrow(false); + } + } + + this.stepsQueue.unshift(...toBeLoadedFigures.reverse()); + this.groupStepsQueue.unshift(...toBeLoadedGroups.reverse()); + this.animatorStepsQueue?.[0].resetIndex(); + + return isCheckpointLoaded; + } + + /** + * Loads the previous checkpoint. This will re-render instantly all figures up to the previous checkpoint and start the queue from there. + */ + loadPreviousCheckpoint() { + const checkpoint = Array.from(this.checkpoints).pop(); + + if (!checkpoint) { + return; + } + + this.checkpoints.delete(checkpoint); + this.togglePause(); + + while (this.instantQueue.length) { + this.runner(true); + } + + if (this.isTravelling) { + while (this.isTravelling) { + this.runTraveller(); + } + + const object = this.traveller?.getObject(); + + if (object instanceof Group) { + object?.getFigures().forEach((figure) => { + if (figure.getCanTravel()) { + figure?.resetTraveller(); + figure?.setShouldTravel(true); + } + }); + } else { + object?.resetTraveller(); + object?.setShouldTravel(true); + } + + this.traveller = null; + } + + if (this.handleAnimatorOnPreviousCheckpointLoad(checkpoint)) { + this.togglePause(); + this.reDrawAll(); + return; + } + + const toBeLoadedObjects = []; + const toCheckGroups = new Set(); + const toCheckAnimators = new Set(); + let found = false; + + while (!found && this.snapshot.length) { + const figure = this.snapshot.pop(); + + if (figure?.getId() === checkpoint) { + found = true; + } + + if (figure) { + toBeLoadedObjects.push(figure); + figure.setThrow(false); + + if (figure.getCanTravel()) { + figure.setShouldTravel(true); + figure.resetTraveller(); + } + + if (figure.getGroupId()) { + toCheckGroups.add(figure.getGroupId()); + } + + if (figure.getAnimatorId()) { + toCheckAnimators.add(figure.getAnimatorId()); + } + } + } + + const toBeLoadedGroups = []; + + while (this.groupSnapshot.length && toCheckGroups.size) { + const group = this.groupSnapshot.pop(); + + if (group) { + toCheckGroups.delete(group?.getId()); + toBeLoadedGroups.push(group); + group.setThrow(false); + } + } + + const toBeLoadedAnimators = []; + + while (this.animatorSnapshot.length && toCheckAnimators.size) { + const animator = this.animatorSnapshot.pop(); + + if (animator) { + toCheckAnimators.delete(animator?.getId()); + toBeLoadedAnimators.push(animator); + animator.setThrow(false); + } + } + + this.stepsQueue.unshift(...toBeLoadedObjects.reverse()); + this.groupStepsQueue.unshift(...toBeLoadedGroups.reverse()); + this.animatorStepsQueue.unshift(...toBeLoadedAnimators.reverse()); + + this.togglePause(); + this.reDrawAll(); + } + /** * Gets the delay between each frame. * @returns The delay. @@ -462,8 +660,13 @@ class Main { * Adds a figure to the queue. * @param figure - The figure to add. * @param instant - Whether to add to the instant queue. + * @param isCheckpoint - Whether to add as a checkpoint. */ - addFigure(figure: Figure, instant = false) { + addFigure(figure: Figure, instant = false, isCheckpoint = false) { + if (isCheckpoint) { + figure.setCheckpoint(true); + } + if (instant) { this.instantQueue.push(figure); } else { @@ -475,14 +678,23 @@ class Main { * Adds a group to the queue. * @param group - The group to add. * @param instant - Whether to add to the instant queue. + * @param isCheckpoint - Whether to add as a checkpoint. */ - addGroup(group: Group, instant = false) { + addGroup(group: Group, instant = false, isCheckpoint = false) { if (instant) { this.groupInstantQueue.push(group); - group.getFigures().forEach((figure) => this.addFigure(figure, instant)); + group + .getFigures() + .forEach((figure, index) => + this.addFigure(figure, instant, index === 0 ? isCheckpoint : false) + ); } else { this.groupStepsQueue.push(group); - group.getFigures().forEach((figure) => this.addFigure(figure, instant)); + group + .getFigures() + .forEach((figure, index) => + this.addFigure(figure, instant, index === 0 ? isCheckpoint : false) + ); } } @@ -490,24 +702,29 @@ class Main { * Adds an animator to the queue. * @param animator - The animator to add. * @param instant - Whether to add to the instant queue. + * @param isCheckpoint - Whether to add as a checkpoint. */ - addAnimator(animator: Animator, instant = false) { + addAnimator(animator: Animator, instant = false, isCheckpoint = false) { if (instant) { this.animatorInstantQueue.push(animator); - animator.getObjects().forEach((object) => { + animator.getObjects().forEach((object, index) => { + const _isCheckpoint = isCheckpoint && index === 0; + if (object instanceof Figure) { - this.addFigure(object, instant); + this.addFigure(object, instant, _isCheckpoint); } else { - this.addGroup(object as Group, instant); + this.addGroup(object as Group, instant, _isCheckpoint); } }); } else { this.animatorStepsQueue.push(animator); - animator.getObjects().forEach((object) => { + animator.getObjects().forEach((object, index) => { + const _isCheckpoint = isCheckpoint && index === 0; + if (object instanceof Figure) { - this.addFigure(object, instant); + this.addFigure(object, instant, _isCheckpoint); } else { - this.addGroup(object as Group, instant); + this.addGroup(object as Group, instant, _isCheckpoint); } }); } From bc11f28313456270bfce1626fc8a49a86ce3d08c Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Fri, 10 Jan 2025 15:26:48 +0530 Subject: [PATCH 38/44] Add next checkpoint navigation api, add complete travel travel to fast pace to end point, and flag to skipping drawing figures --- ee-workflow/src/components/animator.ts | 14 ++++- .../src/components/figure/figureFactory.ts | 11 ++++ ee-workflow/src/components/figure/index.ts | 30 ++++++++++ ee-workflow/src/components/traveller.ts | 20 +++++++ ee-workflow/src/index.ts | 51 ++++++++++++---- ee-workflow/src/main.ts | 60 ++++++++++++++----- 6 files changed, 155 insertions(+), 31 deletions(-) diff --git a/ee-workflow/src/components/animator.ts b/ee-workflow/src/components/animator.ts index 52e59daf1..dfaaa1a4f 100644 --- a/ee-workflow/src/components/animator.ts +++ b/ee-workflow/src/components/animator.ts @@ -83,16 +83,24 @@ export default class Animator { /** * Draws the current object in the animation sequence, and increments the index. * If the index reaches the end of the sequence, it resets the index to 0. + * @param skipDraw - boolean indicating if the draw function should be skipped * @returns boolean indicating if the animation has finished */ - draw(): boolean { + draw(skipDraw = false): boolean { if (this.index === this.objects.length - 1) { this.index = 0; - this.sideEffectOnEnd?.(); + + if (!skipDraw) { + this.sideEffectOnEnd?.(); + } + return true; } - this.objects[this.index].draw(); + if (!skipDraw) { + this.objects[this.index].draw(); + } + this.index++; return false; diff --git a/ee-workflow/src/components/figure/figureFactory.ts b/ee-workflow/src/components/figure/figureFactory.ts index ea0d6a4bf..cf8238c7f 100644 --- a/ee-workflow/src/components/figure/figureFactory.ts +++ b/ee-workflow/src/components/figure/figureFactory.ts @@ -183,8 +183,19 @@ export default class FigureFactory { _figure.setEndY(currentY); }; + const completeTravel = (figure: Figure, skipDraw: boolean) => { + const _figure = figure; + _figure.setEndX(endX); + _figure.setEndY(endY); + + if (!skipDraw) { + _figure.draw(); + } + }; + line.setTraveller(traveller); line.setResetTravel(resetTravel); + line.setCompleteTravel(completeTravel); } return line; diff --git a/ee-workflow/src/components/figure/index.ts b/ee-workflow/src/components/figure/index.ts index eeb26bf10..9052266d3 100644 --- a/ee-workflow/src/components/figure/index.ts +++ b/ee-workflow/src/components/figure/index.ts @@ -80,6 +80,16 @@ export default abstract class Figure { */ protected resetTravel: ((figure: Figure) => void) | undefined; + /** + * Function to complete the travel of the object to the end coordinates at once. + */ + protected completeTravel: + | ((figure: Figure, skipDraw: boolean) => void) + | undefined; + + /** + * Whether the figure is a checkpoint. + */ protected isCheckpoint = false; /** @@ -209,6 +219,16 @@ export default abstract class Figure { } } + /** + * Method to call the complete traveller function. + * @param skipDraw - boolean indicating if the draw function should be skipped + */ + completeTraveller(skipDraw = false) { + if (this.completeTravel) { + this.completeTravel(this, skipDraw); + } + } + /** * Method to set the traveller function. * @param traveller - The traveller function to set. @@ -225,6 +245,16 @@ export default abstract class Figure { this.resetTravel = resetTravel; } + /** + * Method to set the complete traveller function. + * @param completeTravel - The complete traveller function to set. + */ + setCompleteTravel( + completeTravel: (figure: Figure, skipDraw: boolean) => void + ) { + this.completeTravel = completeTravel; + } + /** * Get the unique id of the figure. * @returns The unique id of the figure. diff --git a/ee-workflow/src/components/traveller.ts b/ee-workflow/src/components/traveller.ts index c8f9044ba..bce1e64c1 100644 --- a/ee-workflow/src/components/traveller.ts +++ b/ee-workflow/src/components/traveller.ts @@ -96,4 +96,24 @@ export default class Traveller { getObject() { return this.object; } + + /** + * Function to complete the travelling of the object. + * @param skipDraw - boolean indicating if the draw function should be skipped + */ + completeTravelling(skipDraw = false) { + if (!this.object) { + return; + } + + if (this.object instanceof Group) { + this.object.getFigures().forEach((figure) => { + figure.completeTraveller(skipDraw); + figure.setShouldTravel(false); + }); + } else { + this.object?.completeTraveller(skipDraw); + this.object?.setShouldTravel(false); + } + } } diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 1027e626e..f61b6ebf5 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -27,19 +27,46 @@ IGCanvas.setDelay(1); const IGFF = new FigureFactory(IGCanvas); IGCanvas.togglePause(); -const button = mainFF.box({ - x: 10, - y: 10, - width: 100, - height: 50, - fill: 'blue', - stroke: 'black', - mouseClicked: () => { - mainCanvas.loadPreviousCheckpoint(); - }, -}); +const prevbutton = new Group([ + mainFF.box({ + x: 10, + y: 10, + width: 100, + height: 50, + fill: 'blue', + stroke: 'black', + mouseClicked: () => { + mainCanvas.loadPreviousCheckpoint(); + }, + }), + mainFF.text({ + x: 60, + y: 40, + text: 'Previous', + }), +]); + +const nextbutton = new Group([ + mainFF.box({ + x: 150, + y: 10, + width: 100, + height: 50, + fill: 'blue', + stroke: 'black', + mouseClicked: () => { + mainCanvas.loadNextCheckpoint(); + }, + }), + mainFF.text({ + x: 200, + y: 40, + text: 'Next', + }), +]); -mainCanvas.addFigure(button); +mainCanvas.addGroup(prevbutton); +mainCanvas.addGroup(nextbutton); const timeline = mainFF.line({ x: 0, diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 7e7b00729..7eca8fe71 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -181,8 +181,9 @@ class Main { /** * Runs the drawing process for the current queue. * @param useInstantQueue - Whether to use the instant queue. + * @param skipDraw - Whether to skip drawing. */ - private runner(useInstantQueue = false) { + private runner(useInstantQueue = false, skipDraw = false) { const queue = useInstantQueue ? this.instantQueue : this.stepsQueue; const groupQueue = useInstantQueue ? this.groupInstantQueue @@ -195,32 +196,31 @@ class Main { const firstObject =
queue.shift(); if ( - !useInstantQueue && - (firstObject.getShouldTravel() || - (firstObject.getGroupId() && groupQueue?.[0]?.getShouldTravel())) + firstObject.getShouldTravel() || + (firstObject.getGroupId() && groupQueue?.[0]?.getShouldTravel()) ) { this.isTravelling = true; + this.traveller = new Traveller( + firstObject.getGroupId() ? groupQueue[0] : firstObject + ); - if (firstObject.getGroupId()) { - this.traveller = new Traveller(groupQueue[0]); - + if (skipDraw || useInstantQueue) { + this.traveller.completeTravelling(skipDraw); + this.traveller = null; + this.isTravelling = false; + } else { return; } - - this.traveller = new Traveller(firstObject); - firstObject.setShouldTravel(false); - - return; } if (firstObject.getAnimatorId()) { const animator = animatorQueue[0]; if (animator) { - const isDone = animator.draw(); + const isDone = animator.draw(skipDraw); if (firstObject.getGroupId()) { - this.processGroup(queue, groupQueue, false); + this.processGroup(queue, groupQueue, !skipDraw); } else { if (!firstObject.getThrow()) { this.saveToSnapshot(firstObject); @@ -234,9 +234,12 @@ class Main { } } } else if (firstObject.getGroupId()) { - this.processGroup(queue, groupQueue); + this.processGroup(queue, groupQueue, !skipDraw); } else { - firstObject.draw(); + if (!skipDraw) { + firstObject.draw(); + } + if (!firstObject.getThrow()) { this.saveToSnapshot(firstObject); } @@ -282,6 +285,7 @@ class Main { if (!this.clearOnEachStep) { this.p5.clear(); } + this.runner(); } @@ -555,6 +559,30 @@ class Main { this.reDrawAll(); } + loadNextCheckpoint() { + this.togglePause(); + + while (this.instantQueue.length) { + this.runner(true); + } + + if (this.isTravelling) { + while (this.isTravelling) { + this.runTraveller(); + } + + this.traveller = null; + this.runner(false, true); + } + + while (this.stepsQueue.length && !this.stepsQueue[0].getIsCheckpoint()) { + this.runner(false, true); + } + + this.togglePause(); + this.reDrawAll(); + } + /** * Gets the delay between each frame. * @returns The delay. From 310fed46dc9e9469a9a225124c81f63ab50d39ff Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Fri, 10 Jan 2025 15:39:10 +0530 Subject: [PATCH 39/44] If current checkpoint is last element of snapshot, skip this checkpoint on previous navigation function call --- ee-workflow/src/main.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 7eca8fe71..49b3ffb5d 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -451,17 +451,38 @@ class Main { return isCheckpointLoaded; } + /** + * Pops the previous checkpoint from the stack. + * @returns The previous checkpoint. + */ + private popPreviousCheckpoint(): string | undefined { + let checkpoint = Array.from(this.checkpoints).pop(); + + if (!checkpoint) { + return undefined; + } + + this.checkpoints.delete(checkpoint); + + if (checkpoint === this.snapshot?.[this.snapshot.length - 1]?.getId()) { + checkpoint = this.popPreviousCheckpoint(); + } + + return checkpoint; + } + /** * Loads the previous checkpoint. This will re-render instantly all figures up to the previous checkpoint and start the queue from there. */ loadPreviousCheckpoint() { - const checkpoint = Array.from(this.checkpoints).pop(); + const checkpoint = this.popPreviousCheckpoint(); if (!checkpoint) { return; } this.checkpoints.delete(checkpoint); + this.togglePause(); while (this.instantQueue.length) { From 701ce4617d2ab6308ccf344297484584e53a1452 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Fri, 10 Jan 2025 16:21:11 +0530 Subject: [PATCH 40/44] Handle reset function --- ee-workflow/src/index.ts | 75 ++++++++++++++++++++-------------------- ee-workflow/src/main.ts | 11 ++++-- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index f61b6ebf5..29a103836 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -27,46 +27,45 @@ IGCanvas.setDelay(1); const IGFF = new FigureFactory(IGCanvas); IGCanvas.togglePause(); -const prevbutton = new Group([ - mainFF.box({ - x: 10, - y: 10, - width: 100, - height: 50, - fill: 'blue', - stroke: 'black', - mouseClicked: () => { - mainCanvas.loadPreviousCheckpoint(); - }, - }), - mainFF.text({ - x: 60, - y: 40, - text: 'Previous', - }), -]); +const prevbutton = mainFF.box({ + x: 10, + y: 10, + width: 100, + height: 50, + fill: 'blue', + stroke: 'black', + mouseClicked: () => { + mainCanvas.loadPreviousCheckpoint(); + }, +}); -const nextbutton = new Group([ - mainFF.box({ - x: 150, - y: 10, - width: 100, - height: 50, - fill: 'blue', - stroke: 'black', - mouseClicked: () => { - mainCanvas.loadNextCheckpoint(); - }, - }), - mainFF.text({ - x: 200, - y: 40, - text: 'Next', - }), -]); +const nextbutton = mainFF.box({ + x: 150, + y: 10, + width: 100, + height: 50, + fill: 'blue', + stroke: 'black', + mouseClicked: () => { + mainCanvas.loadNextCheckpoint(); + }, +}); + +const reset = mainFF.box({ + x: 290, + y: 10, + width: 100, + height: 50, + fill: 'blue', + stroke: 'black', + mouseClicked: () => { + mainCanvas.reset(); + }, +}); -mainCanvas.addGroup(prevbutton); -mainCanvas.addGroup(nextbutton); +mainCanvas.addFigure(reset, false, true); +mainCanvas.addFigure(prevbutton); +mainCanvas.addFigure(nextbutton); const timeline = mainFF.line({ x: 0, diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index 49b3ffb5d..f294a0387 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -368,6 +368,15 @@ class Main { return this.pause; } + /** + * Resets the drawing process to the first checkpoint. + */ + reset() { + while (this.checkpoints.size) { + this.loadPreviousCheckpoint(); + } + } + /** * If a animator is still rendering, it will be reset and the already rendered figures/group will be removed from the snapshot and readded to the queue. * @param checkpoint - The checkpoint to load. @@ -481,8 +490,6 @@ class Main { return; } - this.checkpoints.delete(checkpoint); - this.togglePause(); while (this.instantQueue.length) { From d7cbe8f3777fc1889bacbb9802ac2758e375d519 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 20 Jan 2025 16:14:10 +0530 Subject: [PATCH 41/44] Update logic for handling mouse move event in main class, use vars to store previous objects and take action according to them --- ee-workflow/src/components/group.ts | 11 ++++++- ee-workflow/src/index.ts | 22 ++++++------- ee-workflow/src/main.ts | 50 +++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/ee-workflow/src/components/group.ts b/ee-workflow/src/components/group.ts index ace468564..853538a7c 100644 --- a/ee-workflow/src/components/group.ts +++ b/ee-workflow/src/components/group.ts @@ -17,6 +17,7 @@ /** * Internal dependencies. */ +import Main from '../main'; import Figure from './figure'; /** @@ -38,6 +39,11 @@ export default class Group { */ private animatorId = ''; + /** + * Canvas runner instance. + */ + private canvasRunner: Main; + /** * Property to check if the group should be saved in groupSnapshot. * If true, the group will NOT be saved in groupSnapshot. @@ -49,8 +55,9 @@ export default class Group { */ static groupCount = 0; - constructor(figures: Figure[], id?: string) { + constructor(canvasRunner: Main, figures: Figure[], id?: string) { Group.groupCount++; + this.canvasRunner = canvasRunner; this.id = id || `group-${Group.groupCount}` + Math.random().toString(36).slice(2, 9); @@ -74,6 +81,7 @@ export default class Group { this.figures.forEach((figure) => { figure.mouseMoved(); }); + this.canvasRunner.addGroup(this, true); } /** @@ -83,6 +91,7 @@ export default class Group { this.figures.forEach((figure) => { figure.onLeave(); }); + this.canvasRunner.addGroup(this, true); } /** diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 29a103836..45764a628 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -77,7 +77,7 @@ const timeline = mainFF.line({ }); const circles = [ - new Group([ + new Group(mainCanvas, [ mainFF.circle({ x: 100, y: 200, @@ -102,7 +102,7 @@ const circles = [ stroke: 'black', }), ]), - new Group([ + new Group(mainCanvas, [ mainFF.circle({ x: 300, y: 200, @@ -127,7 +127,7 @@ const circles = [ stroke: 'black', }), ]), - new Group([ + new Group(mainCanvas, [ mainFF.circle({ x: 500, y: 200, @@ -152,7 +152,7 @@ const circles = [ stroke: 'black', }), ]), - new Group([ + new Group(mainCanvas, [ mainFF.circle({ x: 700, y: 200, @@ -202,7 +202,7 @@ const advertiserFlow = [ hasArrow: true, shouldTravel: true, }), - new Group([ + new Group(mainCanvas, [ mainFF.box({ x: 50, y: 300, @@ -225,7 +225,7 @@ const advertiserFlow = [ hasArrow: true, shouldTravel: true, }), - new Group([ + new Group(mainCanvas, [ mainFF.box({ x: 50, y: 413, @@ -308,7 +308,7 @@ flow.setSideEffectOnEnd(() => { mainCanvas.addAnimator(flow, false, true); mainCanvas.addGroup( - new Group([ + new Group(mainCanvas, [ mainFF.line({ x: 0, y: 200, @@ -343,7 +343,7 @@ const travellerLine = mainFF.line({ shouldTravel: true, }); -const travellerGroup = new Group([ +const travellerGroup = new Group(mainCanvas, [ mainFF.line({ x: 0, y: 600, @@ -392,7 +392,7 @@ const secondCircleAnimations = [ hasArrow: true, shouldTravel: true, }), - new Group([ + new Group(mainCanvas, [ mainFF.box({ x: 250, y: 300, @@ -415,7 +415,7 @@ const secondCircleAnimations = [ hasArrow: true, shouldTravel: true, }), - new Group([ + new Group(mainCanvas, [ mainFF.box({ x: 250, y: 413, @@ -491,7 +491,7 @@ secondFlow.setSideEffectOnEnd(() => { mainCanvas.addAnimator(secondFlow, false, true); mainCanvas.addGroup( - new Group([ + new Group(mainCanvas, [ mainFF.line({ x: 137, y: 200, diff --git a/ee-workflow/src/main.ts b/ee-workflow/src/main.ts index f294a0387..1048a05bb 100644 --- a/ee-workflow/src/main.ts +++ b/ee-workflow/src/main.ts @@ -111,6 +111,16 @@ class Main { */ private traveller: Traveller | null = null; + /** + * Last hovered group. + */ + private hoveredGroup: Group | null = null; + + /** + * Last hovered figure. + */ + private hoveredFigure: Figure | null = null; + /** * Main constructor. * @param clearOnEachStep - Whether to clear the canvas on each step (default: true). @@ -314,13 +324,47 @@ class Main { */ private mouseMoved() { this.snapshot.forEach((object) => { + if (object.getAnimatorId()) { + return; + } + const isHovering = object.isHovering(); + + if (!isHovering) { + if (this.hoveredFigure?.getId() === object.getId()) { + this.hoveredFigure.onLeave(); + this.hoveredFigure = null; + + if (this.hoveredGroup) { + this.hoveredGroup.onLeave(); + this.hoveredGroup = null; + } + } + + return; + } + + if (isHovering && this.hoveredFigure?.getId() === object.getId()) { + return; + } + const _object = this.isGrouped(object) || object; + if ( + isHovering && + _object instanceof Group && + this.hoveredGroup?.getId() === _object.getId() + ) { + this.hoveredFigure = object; + return; + } + if (isHovering) { - _object.mouseMoved(); - } else { - _object.onLeave(); + this.hoveredFigure = object; + this.hoveredGroup = _object instanceof Group ? _object : null; + + this.hoveredFigure?.mouseMoved(); + this.hoveredGroup?.mouseMoved(); } }); } From c9271615eae4c30158f906aa86426dc730b7b9da Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 20 Jan 2025 18:20:55 +0530 Subject: [PATCH 42/44] Add next coordinate flag and calculation logic --- .../src/components/figure/figureFactory.ts | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/ee-workflow/src/components/figure/figureFactory.ts b/ee-workflow/src/components/figure/figureFactory.ts index cf8238c7f..8b761509a 100644 --- a/ee-workflow/src/components/figure/figureFactory.ts +++ b/ee-workflow/src/components/figure/figureFactory.ts @@ -33,11 +33,14 @@ type FigureParams = { stroke?: string; tags?: string[]; mouseClicked?: () => void; + nextTipDirection?: 'up' | 'down' | 'left' | 'right'; }; export default class FigureFactory { private canvasRunner: Main; + private nextCoordinates: { x: number; y: number } = { x: 0, y: 0 }; + constructor(canvasRunner: Main) { this.canvasRunner = canvasRunner; } @@ -51,11 +54,33 @@ export default class FigureFactory { fill, stroke, tags, + nextTipDirection, mouseClicked, }: FigureParams & { width: number; height: number; }): Box { + if (nextTipDirection) { + const newX = + x + + (nextTipDirection === 'left' + ? 0 + : nextTipDirection === 'right' + ? width + : width / 2); + const newY = + y + + (nextTipDirection === 'up' + ? 0 + : nextTipDirection === 'down' + ? height + : height / 2); + + this.nextCoordinates = { x: newX, y: newY }; + } else { + this.nextCoordinates = { x: x + width / 2, y: y + height / 2 }; + } + return new Box( this.canvasRunner, x, @@ -78,9 +103,31 @@ export default class FigureFactory { fill, stroke, tags, + nextTipDirection, }: FigureParams & { diameter: number; }): Circle { + if (nextTipDirection) { + const newX = + x + + (nextTipDirection === 'left' + ? -diameter / 2 + : nextTipDirection === 'right' + ? diameter / 2 + : 0); + const newY = + y + + (nextTipDirection === 'up' + ? -diameter / 2 + : nextTipDirection === 'down' + ? diameter / 2 + : 0); + + this.nextCoordinates = { x: newX, y: newY }; + } else { + this.nextCoordinates = { x: x, y: y }; + } + return new Circle( this.canvasRunner, x, @@ -101,11 +148,33 @@ export default class FigureFactory { width, height, tags, + nextTipDirection, }: FigureParams & { imageData: string; width: number; height: number; }): Image { + if (nextTipDirection) { + const newX = + x + + (nextTipDirection === 'left' + ? -width / 2 + : nextTipDirection === 'right' + ? width / 2 + : 0); + const newY = + y + + (nextTipDirection === 'up' + ? -height / 2 + : nextTipDirection === 'down' + ? height / 2 + : 0); + + this.nextCoordinates = { x: newX, y: newY }; + } else { + this.nextCoordinates = { x: x, y: y }; + } + return new Image( this.canvasRunner, x, @@ -128,12 +197,34 @@ export default class FigureFactory { hasArrow, shouldTravel, tags, + nextTipDirection, }: FigureParams & { endX: number; endY: number; hasArrow?: boolean; shouldTravel?: boolean; }): Line { + if (nextTipDirection) { + const newX = + x + + (nextTipDirection === 'left' + ? 0 + : nextTipDirection === 'right' + ? endX - x + : (endX - x) / 2); + const newY = + y + + (nextTipDirection === 'up' + ? 0 + : nextTipDirection === 'down' + ? endY - y + : (endY - y) / 2); + + this.nextCoordinates = { x: newX, y: newY }; + } else { + this.nextCoordinates = { x: endX, y: endY }; + } + const line = new Line( this.canvasRunner, x, @@ -209,10 +300,35 @@ export default class FigureFactory { size, fill, tags, + nextTipDirection, }: FigureParams & { text: string; size?: number; }): Text { + const textLength = this.canvasRunner.getP5Instance().textWidth(text); + const textHeight = size ?? 16; + if (nextTipDirection) { + const newX = + x + + (nextTipDirection === 'left' + ? -textLength / 2 + : nextTipDirection === 'right' + ? textLength / 2 + : 0); + + const newY = + y + + (nextTipDirection === 'up' + ? -textHeight / 2 + : nextTipDirection === 'down' + ? textHeight / 2 + : 0); + + this.nextCoordinates = { x: newX, y: newY }; + } else { + this.nextCoordinates = { x: x + textLength / 2, y: y + textHeight / 2 }; + } + return new Text(this.canvasRunner, x, y, text, id, size, fill, tags); } } From 2fde050a07d5f781a89ebc70f94fe5f9276cb6bd Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 20 Jan 2025 19:10:38 +0530 Subject: [PATCH 43/44] Calculate next tips for five directions and not rely on params --- .../src/components/figure/figureFactory.ts | 174 +++++++----------- 1 file changed, 64 insertions(+), 110 deletions(-) diff --git a/ee-workflow/src/components/figure/figureFactory.ts b/ee-workflow/src/components/figure/figureFactory.ts index 8b761509a..873ac464a 100644 --- a/ee-workflow/src/components/figure/figureFactory.ts +++ b/ee-workflow/src/components/figure/figureFactory.ts @@ -32,19 +32,35 @@ type FigureParams = { fill?: string; stroke?: string; tags?: string[]; + nextTipHelper: (x: number, y: number, ...args: any) => void; mouseClicked?: () => void; - nextTipDirection?: 'up' | 'down' | 'left' | 'right'; }; export default class FigureFactory { private canvasRunner: Main; - private nextCoordinates: { x: number; y: number } = { x: 0, y: 0 }; + private nextCoordinates: { + left: { x: number; y: number }; + right: { x: number; y: number }; + up: { x: number; y: number }; + down: { x: number; y: number }; + middle: { x: number; y: number }; + }; constructor(canvasRunner: Main) { this.canvasRunner = canvasRunner; } + private boxNextTips(x: number, y: number, width: number, height: number) { + this.nextCoordinates = { + left: { x, y: height / 2 }, + right: { x: width, y: height / 2 }, + up: { x: width / 2, y }, + down: { x: width / 2, y: height }, + middle: { x: x + width / 2, y: y + height / 2 }, + }; + } + box({ id, x, @@ -54,32 +70,12 @@ export default class FigureFactory { fill, stroke, tags, - nextTipDirection, mouseClicked, }: FigureParams & { width: number; height: number; }): Box { - if (nextTipDirection) { - const newX = - x + - (nextTipDirection === 'left' - ? 0 - : nextTipDirection === 'right' - ? width - : width / 2); - const newY = - y + - (nextTipDirection === 'up' - ? 0 - : nextTipDirection === 'down' - ? height - : height / 2); - - this.nextCoordinates = { x: newX, y: newY }; - } else { - this.nextCoordinates = { x: x + width / 2, y: y + height / 2 }; - } + this.boxNextTips(x, y, width, height); return new Box( this.canvasRunner, @@ -95,6 +91,16 @@ export default class FigureFactory { ); } + private circleNextTips(x: number, y: number, diameter: number) { + this.nextCoordinates = { + left: { x: x - diameter / 2, y }, + right: { x: x + diameter / 2, y }, + up: { x, y: y - diameter / 2 }, + down: { x, y: y + diameter / 2 }, + middle: { x, y }, + }; + } + circle({ id, x, @@ -103,30 +109,10 @@ export default class FigureFactory { fill, stroke, tags, - nextTipDirection, }: FigureParams & { diameter: number; }): Circle { - if (nextTipDirection) { - const newX = - x + - (nextTipDirection === 'left' - ? -diameter / 2 - : nextTipDirection === 'right' - ? diameter / 2 - : 0); - const newY = - y + - (nextTipDirection === 'up' - ? -diameter / 2 - : nextTipDirection === 'down' - ? diameter / 2 - : 0); - - this.nextCoordinates = { x: newX, y: newY }; - } else { - this.nextCoordinates = { x: x, y: y }; - } + this.circleNextTips(x, y, diameter); return new Circle( this.canvasRunner, @@ -140,6 +126,16 @@ export default class FigureFactory { ); } + private imageNextTips(x: number, y: number, width: number, height: number) { + this.nextCoordinates = { + left: { x: x - width / 2, y }, + right: { x: x + width / 2, y }, + up: { x, y: y - height / 2 }, + down: { x, y: y + height / 2 }, + middle: { x, y }, + }; + } + image({ id, x, @@ -148,32 +144,12 @@ export default class FigureFactory { width, height, tags, - nextTipDirection, }: FigureParams & { imageData: string; width: number; height: number; }): Image { - if (nextTipDirection) { - const newX = - x + - (nextTipDirection === 'left' - ? -width / 2 - : nextTipDirection === 'right' - ? width / 2 - : 0); - const newY = - y + - (nextTipDirection === 'up' - ? -height / 2 - : nextTipDirection === 'down' - ? height / 2 - : 0); - - this.nextCoordinates = { x: newX, y: newY }; - } else { - this.nextCoordinates = { x: x, y: y }; - } + this.imageNextTips(x, y, width, height); return new Image( this.canvasRunner, @@ -187,6 +163,16 @@ export default class FigureFactory { ); } + private lineNextTips(x: number, y: number, endX: number, endY: number) { + this.nextCoordinates = { + left: { x, y }, + right: { x: endX, y: endY }, + up: { x, y }, + down: { x: endX, y: endY }, + middle: { x: (x + endX) / 2, y: (y + endY) / 2 }, + }; + } + line({ id, x, @@ -197,33 +183,13 @@ export default class FigureFactory { hasArrow, shouldTravel, tags, - nextTipDirection, }: FigureParams & { endX: number; endY: number; hasArrow?: boolean; shouldTravel?: boolean; }): Line { - if (nextTipDirection) { - const newX = - x + - (nextTipDirection === 'left' - ? 0 - : nextTipDirection === 'right' - ? endX - x - : (endX - x) / 2); - const newY = - y + - (nextTipDirection === 'up' - ? 0 - : nextTipDirection === 'down' - ? endY - y - : (endY - y) / 2); - - this.nextCoordinates = { x: newX, y: newY }; - } else { - this.nextCoordinates = { x: endX, y: endY }; - } + this.lineNextTips(x, y, endX, endY); const line = new Line( this.canvasRunner, @@ -292,6 +258,17 @@ export default class FigureFactory { return line; } + private textNextTips(x: number, y: number) { + // p5.textWidth is giving error, so for now everything has same points + this.nextCoordinates = { + left: { x, y }, + right: { x, y }, + up: { x, y }, + down: { x, y }, + middle: { x, y }, + }; + } + text({ id, x, @@ -300,34 +277,11 @@ export default class FigureFactory { size, fill, tags, - nextTipDirection, }: FigureParams & { text: string; size?: number; }): Text { - const textLength = this.canvasRunner.getP5Instance().textWidth(text); - const textHeight = size ?? 16; - if (nextTipDirection) { - const newX = - x + - (nextTipDirection === 'left' - ? -textLength / 2 - : nextTipDirection === 'right' - ? textLength / 2 - : 0); - - const newY = - y + - (nextTipDirection === 'up' - ? -textHeight / 2 - : nextTipDirection === 'down' - ? textHeight / 2 - : 0); - - this.nextCoordinates = { x: newX, y: newY }; - } else { - this.nextCoordinates = { x: x + textLength / 2, y: y + textHeight / 2 }; - } + this.textNextTips(x, y); return new Text(this.canvasRunner, x, y, text, id, size, fill, tags); } From cea7fac7af5a6f2c83abf1fc2d53c84a28fde596 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 21 Jan 2025 11:06:15 +0530 Subject: [PATCH 44/44] Add next tip helper to return five possible coordinates from last figure render --- .../src/components/figure/figureFactory.ts | 108 +++++++++++++----- ee-workflow/src/index.ts | 5 +- 2 files changed, 84 insertions(+), 29 deletions(-) diff --git a/ee-workflow/src/components/figure/figureFactory.ts b/ee-workflow/src/components/figure/figureFactory.ts index 873ac464a..d1830d4cc 100644 --- a/ee-workflow/src/components/figure/figureFactory.ts +++ b/ee-workflow/src/components/figure/figureFactory.ts @@ -25,32 +25,63 @@ import Image from './image'; import Line from './line'; import Text from './text'; +type NextCoordinates = { + left: { x: number; y: number }; + right: { x: number; y: number }; + up: { x: number; y: number }; + down: { x: number; y: number }; + middle: { x: number; y: number }; +}; + type FigureParams = { id?: string; - x: number; - y: number; + x?: number; + y?: number; fill?: string; stroke?: string; tags?: string[]; - nextTipHelper: (x: number, y: number, ...args: any) => void; + nextTipHelper?: ( + nextCoordinates: NextCoordinates, + ...args: any + ) => { x: number; y: number }; mouseClicked?: () => void; }; export default class FigureFactory { private canvasRunner: Main; - private nextCoordinates: { - left: { x: number; y: number }; - right: { x: number; y: number }; - up: { x: number; y: number }; - down: { x: number; y: number }; - middle: { x: number; y: number }; + private nextCoordinates: NextCoordinates = { + left: { x: 0, y: 0 }, + right: { x: 0, y: 0 }, + up: { x: 0, y: 0 }, + down: { x: 0, y: 0 }, + middle: { x: 0, y: 0 }, }; constructor(canvasRunner: Main) { this.canvasRunner = canvasRunner; } + private nextTip( + x?: number, + y?: number, + nextTipHelper?: FigureParams['nextTipHelper'] + ) { + const possibleX = x ?? 0; + const possibleY = y ?? 0; + + if (x === undefined || y === undefined) { + if (!nextTipHelper) { + throw new Error('x and y are required for figure'); + } + + const coordinates = nextTipHelper(this.nextCoordinates); + return { possibleX: coordinates.x, possibleY: coordinates.y }; + } + + return { possibleX, possibleY }; + } + private boxNextTips(x: number, y: number, width: number, height: number) { this.nextCoordinates = { left: { x, y: height / 2 }, @@ -70,17 +101,19 @@ export default class FigureFactory { fill, stroke, tags, + nextTipHelper, mouseClicked, }: FigureParams & { width: number; height: number; }): Box { - this.boxNextTips(x, y, width, height); + const { possibleX, possibleY } = this.nextTip(x, y, nextTipHelper); + this.boxNextTips(possibleX, possibleY, width, height); return new Box( this.canvasRunner, - x, - y, + possibleX, + possibleY, width, height, id, @@ -109,15 +142,18 @@ export default class FigureFactory { fill, stroke, tags, + nextTipHelper, }: FigureParams & { diameter: number; }): Circle { - this.circleNextTips(x, y, diameter); + const { possibleX, possibleY } = this.nextTip(x, y, nextTipHelper); + + this.circleNextTips(possibleX, possibleY, diameter); return new Circle( this.canvasRunner, - x, - y, + possibleX, + possibleY, diameter, id, fill, @@ -144,17 +180,20 @@ export default class FigureFactory { width, height, tags, + nextTipHelper, }: FigureParams & { imageData: string; width: number; height: number; }): Image { - this.imageNextTips(x, y, width, height); + const { possibleX, possibleY } = this.nextTip(x, y, nextTipHelper); + + this.imageNextTips(possibleX, possibleY, width, height); return new Image( this.canvasRunner, - x, - y, + possibleX, + possibleY, imageData, width, height, @@ -183,18 +222,21 @@ export default class FigureFactory { hasArrow, shouldTravel, tags, + nextTipHelper, }: FigureParams & { endX: number; endY: number; hasArrow?: boolean; shouldTravel?: boolean; }): Line { - this.lineNextTips(x, y, endX, endY); + const { possibleX, possibleY } = this.nextTip(x, y, nextTipHelper); + + this.lineNextTips(possibleX, possibleY, endX, endY); const line = new Line( this.canvasRunner, - x, - y, + possibleX, + possibleY, endX, endY, id, @@ -204,8 +246,8 @@ export default class FigureFactory { ); if (shouldTravel) { - let currentX = x; - let currentY = y; + let currentX = possibleX; + let currentY = possibleY; line.setShouldTravel(shouldTravel); line.setEndX(currentX); line.setEndY(currentY); @@ -234,8 +276,8 @@ export default class FigureFactory { const resetTravel = (figure: Figure) => { const _figure = figure; - currentX = x; - currentY = y; + currentX = possibleX; + currentY = possibleY; _figure.setEndX(currentX); _figure.setEndY(currentY); }; @@ -277,12 +319,24 @@ export default class FigureFactory { size, fill, tags, + nextTipHelper, }: FigureParams & { text: string; size?: number; }): Text { - this.textNextTips(x, y); + const { possibleX, possibleY } = this.nextTip(x, y, nextTipHelper); + + this.textNextTips(possibleX, possibleY); - return new Text(this.canvasRunner, x, y, text, id, size, fill, tags); + return new Text( + this.canvasRunner, + possibleX, + possibleY, + text, + id, + size, + fill, + tags + ); } } diff --git a/ee-workflow/src/index.ts b/ee-workflow/src/index.ts index 45764a628..d5a16040b 100644 --- a/ee-workflow/src/index.ts +++ b/ee-workflow/src/index.ts @@ -40,8 +40,6 @@ const prevbutton = mainFF.box({ }); const nextbutton = mainFF.box({ - x: 150, - y: 10, width: 100, height: 50, fill: 'blue', @@ -49,6 +47,9 @@ const nextbutton = mainFF.box({ mouseClicked: () => { mainCanvas.loadNextCheckpoint(); }, + nextTipHelper: (nextCoordinates) => { + return { x: nextCoordinates.right.x + 40, y: nextCoordinates.up.y }; + }, }); const reset = mainFF.box({