From 2e8df0ad7922489a13669a2d31aba7aab9617339 Mon Sep 17 00:00:00 2001 From: "Daniel B." Date: Wed, 12 Mar 2025 20:26:34 -0600 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=A7=B9=20Chore:=20Refactor=20Particle?= =?UTF-8?q?s=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement strategy design pattern --- .vscode/settings.json | 1 + src/app/layout.jsx | 18 +- src/frontend/ui/components/atoms/Head.jsx | 4 +- .../molecules}/Canvas/index.css | 0 .../molecules}/Canvas/index.jsx | 2 +- .../molecules}/Canvas/layout.jsx | 2 +- .../molecules}/Model/index.jsx | 13 +- src/frontend/ui/models/Graph/index.jsx | 21 ++ .../Particles/components/atoms/Box/index.jsx | 24 +++ .../components/atoms/Camera/index.jsx | 24 +++ .../components/atoms/Camera/logic/setup.js | 16 ++ .../components/atoms/Circle/index.jsx | 29 +++ .../components/atoms/Circle/logic/update.js | 8 + .../components/atoms/Controls/index.jsx | 10 + .../components/atoms/Mouse/logic/handler.js | 34 ++++ .../components/atoms/Mouse/logic/listener.js | 13 ++ .../components/atoms/Mouse/logic/update.js | 10 + .../components/atoms/Mouse/updates/color.js | 11 ++ .../atoms/Mouse/updates/position.js | 11 ++ .../atoms/Particles/effects/chaos/effect.js | 21 ++ .../atoms/Particles/effects/chaos/update.js | 8 + .../atoms/Particles/effects/color/effect.js | 11 ++ .../atoms/Particles/effects/color/update.js | 11 ++ .../Particles/effects/expansion/effect.js | 19 ++ .../Particles/effects/flotation/effect.js | 19 ++ .../Particles/effects/interpolation/effect.js | 20 ++ .../Particles/effects/position/effect.js | 24 +++ .../Particles/effects/position/update.js | 11 ++ .../atoms/Particles/effects/resize/resize.js | 22 +++ .../components/atoms/Particles/index.jsx | 16 ++ .../components/atoms/Particles/logic/setup.js | 65 +++++++ .../atoms/Particles/logic/update.js | 41 ++++ .../components/atoms/Plane/index.jsx | 23 +++ .../components/atoms/Plane/logic/setup.js | 9 + .../components/atoms/RayLine/index.jsx | 19 ++ .../components/atoms/RayLine/logic/line.js | 12 ++ .../components/atoms/Raycaster/index.jsx | 8 + .../components/atoms/Raycaster/logic/setup.js | 8 + .../atoms/Raycaster/logic/update.js | 12 ++ .../components/molecules/ParticleSystem.jsx | 42 ++++ .../components/organisms/ParticlesScene.jsx | 44 +++++ .../ui/models/Particles/config/index.js | 61 ++++++ .../hooks.jsx => Particles/hooks/useArgs.jsx} | 9 +- src/frontend/ui/models/Particles/index.jsx | 23 +++ .../models/Particles/logic/effects/manager.js | 31 +++ .../Particles/logic/effects/strategy.js | 13 ++ .../Particles/logic/handlers/manager.js | 59 ++++++ .../Particles/logic/handlers/strategy.js | 14 ++ .../ui/models/Particles/logic/index.js | 61 ++++++ .../Particles/logic/listeners/manager.js | 38 ++++ .../Particles/logic/listeners/resize.js | 11 ++ .../Particles/logic/listeners/strategy.js | 22 +++ .../models/Particles/logic/setups/manager.js | 33 ++++ .../models/Particles/logic/setups/strategy.js | 13 ++ .../models/Particles/logic/updates/manager.js | 55 ++++++ .../Particles/logic/updates/strategy.js | 13 ++ .../ui/models/molecule/Particles/effects.js | 124 ------------ .../ui/models/molecule/Particles/index.jsx | 160 --------------- .../ui/models/molecule/Particles/logic.js | 133 ------------- .../ui/models/molecule/Particles/setups.js | 131 ------------- .../ui/models/molecule/Particles/updates.js | 182 ------------------ 61 files changed, 1151 insertions(+), 751 deletions(-) create mode 100644 .vscode/settings.json rename src/frontend/ui/{models/atoms => components/molecules}/Canvas/index.css (100%) rename src/frontend/ui/{models/atoms => components/molecules}/Canvas/index.jsx (92%) rename src/frontend/ui/{models/atoms => components/molecules}/Canvas/layout.jsx (93%) rename src/frontend/ui/{models/atoms => components/molecules}/Model/index.jsx (68%) create mode 100644 src/frontend/ui/models/Graph/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/Box/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Controls/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/effect.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/update.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/effect.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/update.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/expansion/effect.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation/effect.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation/effect.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/effect.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/update.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/resize/resize.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/RayLine/logic/line.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Raycaster/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js create mode 100644 src/frontend/ui/models/Particles/components/molecules/ParticleSystem.jsx create mode 100644 src/frontend/ui/models/Particles/components/organisms/ParticlesScene.jsx create mode 100644 src/frontend/ui/models/Particles/config/index.js rename src/frontend/ui/models/{molecule/Particles/hooks.jsx => Particles/hooks/useArgs.jsx} (88%) create mode 100644 src/frontend/ui/models/Particles/index.jsx create mode 100644 src/frontend/ui/models/Particles/logic/effects/manager.js create mode 100644 src/frontend/ui/models/Particles/logic/effects/strategy.js create mode 100644 src/frontend/ui/models/Particles/logic/handlers/manager.js create mode 100644 src/frontend/ui/models/Particles/logic/handlers/strategy.js create mode 100644 src/frontend/ui/models/Particles/logic/index.js create mode 100644 src/frontend/ui/models/Particles/logic/listeners/manager.js create mode 100644 src/frontend/ui/models/Particles/logic/listeners/resize.js create mode 100644 src/frontend/ui/models/Particles/logic/listeners/strategy.js create mode 100644 src/frontend/ui/models/Particles/logic/setups/manager.js create mode 100644 src/frontend/ui/models/Particles/logic/setups/strategy.js create mode 100644 src/frontend/ui/models/Particles/logic/updates/manager.js create mode 100644 src/frontend/ui/models/Particles/logic/updates/strategy.js delete mode 100644 src/frontend/ui/models/molecule/Particles/effects.js delete mode 100644 src/frontend/ui/models/molecule/Particles/index.jsx delete mode 100644 src/frontend/ui/models/molecule/Particles/logic.js delete mode 100644 src/frontend/ui/models/molecule/Particles/setups.js delete mode 100644 src/frontend/ui/models/molecule/Particles/updates.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/app/layout.jsx b/src/app/layout.jsx index 5030cfc..5251ab1 100644 --- a/src/app/layout.jsx +++ b/src/app/layout.jsx @@ -26,11 +26,11 @@ import Head from "@semantyk/frontend/ui/components/atoms/Head"; import Body from "@semantyk/frontend/ui/components/molecules/Body"; import { getLang } from "@semantyk/frontend/logic/services/getLang"; import Content from "@semantyk/frontend/ui/components/molecules/Content"; -import Model from "@semantyk/frontend/ui/models/atoms/Model"; +import Model from "@semantyk/frontend/ui/components/molecules/Model"; //* Main -export async function generateMetadata() {return await getMetadata();} +export async function generateMetadata() { return await getMetadata(); } export default function RootLayout({ children }) { // Logic @@ -39,13 +39,13 @@ export default function RootLayout({ children }) { return ( // TODO: Add logic for dynamic language - - - - - {children} - - + + + + + {children} + + ); } \ No newline at end of file diff --git a/src/frontend/ui/components/atoms/Head.jsx b/src/frontend/ui/components/atoms/Head.jsx index 2bfad07..0c880a8 100644 --- a/src/frontend/ui/components/atoms/Head.jsx +++ b/src/frontend/ui/components/atoms/Head.jsx @@ -22,7 +22,5 @@ import Analytics from "@semantyk/frontend/logic/analytics/Analytics"; //* Main export default function Head() { // Return - return (<> - - ); + return ; } \ No newline at end of file diff --git a/src/frontend/ui/models/atoms/Canvas/index.css b/src/frontend/ui/components/molecules/Canvas/index.css similarity index 100% rename from src/frontend/ui/models/atoms/Canvas/index.css rename to src/frontend/ui/components/molecules/Canvas/index.css diff --git a/src/frontend/ui/models/atoms/Canvas/index.jsx b/src/frontend/ui/components/molecules/Canvas/index.jsx similarity index 92% rename from src/frontend/ui/models/atoms/Canvas/index.jsx rename to src/frontend/ui/components/molecules/Canvas/index.jsx index ff58031..a1bd7b3 100644 --- a/src/frontend/ui/models/atoms/Canvas/index.jsx +++ b/src/frontend/ui/components/molecules/Canvas/index.jsx @@ -19,7 +19,7 @@ //* Imports import React from "react"; //* Local Imports -import CanvasLayout from "@semantyk/frontend/ui/models/atoms/Canvas/layout"; +import CanvasLayout from "@semantyk/frontend/ui/components/molecules/Canvas/layout"; //* Main export default function Canvas({ children }) { diff --git a/src/frontend/ui/models/atoms/Canvas/layout.jsx b/src/frontend/ui/components/molecules/Canvas/layout.jsx similarity index 93% rename from src/frontend/ui/models/atoms/Canvas/layout.jsx rename to src/frontend/ui/components/molecules/Canvas/layout.jsx index b0f8f69..b9cea6d 100644 --- a/src/frontend/ui/models/atoms/Canvas/layout.jsx +++ b/src/frontend/ui/components/molecules/Canvas/layout.jsx @@ -18,7 +18,7 @@ import React from "react"; import { Canvas } from "@react-three/fiber"; //* Local Imports -import "@semantyk/frontend/ui/models/atoms/Canvas/index.css"; +import "@semantyk/frontend/ui/components/molecules/Canvas/index.css"; //* Main export default function CanvasLayout(props) { diff --git a/src/frontend/ui/models/atoms/Model/index.jsx b/src/frontend/ui/components/molecules/Model/index.jsx similarity index 68% rename from src/frontend/ui/models/atoms/Model/index.jsx rename to src/frontend/ui/components/molecules/Model/index.jsx index 230a9f9..80020bc 100644 --- a/src/frontend/ui/models/atoms/Model/index.jsx +++ b/src/frontend/ui/components/molecules/Model/index.jsx @@ -7,7 +7,7 @@ * @file: This file contains the logic for a generic Three.js model component. * * @created: Jul 17, 2024 - * @modified: Mar 7, 2025 + * @modified: Mar 12, 2025 * * @author: Semantyk Team * @maintainer: Daniel Bakas @@ -20,15 +20,20 @@ //* Imports import React from "react"; -import Canvas from "@semantyk/frontend/ui/models/atoms/Canvas"; -import ParticlesModel from "@semantyk/frontend/ui/models/molecule/Particles"; +import Canvas from "@semantyk/frontend/ui/components/molecules/Canvas"; +import ParticlesModel from "@semantyk/frontend/ui/models/Particles"; +import GraphModel from "@semantyk/frontend/ui/models/Graph"; +import { usePathname } from "next/navigation"; //* Main export default function Model() { + // Logic + const pathname = usePathname(); // Return return ( - + {pathname === "/" && } + {pathname === "/knowledge" && } ); }; \ No newline at end of file diff --git a/src/frontend/ui/models/Graph/index.jsx b/src/frontend/ui/models/Graph/index.jsx new file mode 100644 index 0000000..e2a95ba --- /dev/null +++ b/src/frontend/ui/models/Graph/index.jsx @@ -0,0 +1,21 @@ +/** + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + * # `index.jsx` | `GraphModel` + * @organization: Semantyk + * @project: Client + * + * @file: This file contains the logic for the Graph model. + * + * @created: Mar 13, 2025 + * @modified: Mar 13, 2025 + * + * @author: Semantyk Team + * @maintainer: Daniel Bakas + * + * @copyright: Semantyk © 2025. All rights reserved. + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + */ + +//* Main +export default function GraphModel() { +}; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Box/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Box/index.jsx new file mode 100644 index 0000000..a29a3bd --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Box/index.jsx @@ -0,0 +1,24 @@ +/** + * Box.jsx + * Atom component for the box in the Particles model + */ + +//* Main +export default function Box({ config, data, refs }) { + // Props + const { + general: { showHelpers } + } = config; + // Return + return ( + + + + + ); +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx new file mode 100644 index 0000000..636282d --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx @@ -0,0 +1,24 @@ +/** + * Camera.jsx + * Atom component for the perspective camera in the Particles model + */ + +import { PerspectiveCamera } from "@react-three/drei"; +import { CameraHelper } from "three"; +import { useHelper } from "@react-three/drei"; + +export default function Camera({ config, refs }) { + // Props + const { + general: { showHelpers } + } = config; + // Logic + useHelper(showHelpers && refs.camera, CameraHelper); + + return ( + + ); +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js new file mode 100644 index 0000000..93ada07 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js @@ -0,0 +1,16 @@ +import { SetupStrategy } from '../../../../logic/setups/strategy'; + +export class CameraSetup extends SetupStrategy { + apply({ config, data: { unit }, refs: { camera } }) { + const { camera: { margin } } = config; + + const aspectRatio = window.innerWidth / window.innerHeight; + let x = (1 + margin) / ((aspectRatio >= 1) ? 2 : (2 * aspectRatio)); + const fx = 2 * Math.atan(x) * (180 / Math.PI); + + camera.current.fov = fx; + camera.current.aspect = aspectRatio; + camera.current.position.z = unit / 2; + camera.current.updateProjectionMatrix(); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx new file mode 100644 index 0000000..a253cd2 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx @@ -0,0 +1,29 @@ +/** + * Circle.jsx + * Atom component for the circle in the Particles model + */ + +//* Main +export default function Circle({ config, data, refs }) { + // Props + const { + general: { showHelpers }, + animations: { chaos: { radius } } + } = config; + // Return + return ( + + + + + ); +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js new file mode 100644 index 0000000..c00e734 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js @@ -0,0 +1,8 @@ +import { UpdateStrategy } from '../../../../logic/updates/strategy'; + +export class CircleUpdate extends UpdateStrategy { + apply({ objects, refs, target }) { + objects.raycaster.ray.intersectPlane(refs.plane.current, target); + refs.circle.current.position.copy(target); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Controls/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Controls/index.jsx new file mode 100644 index 0000000..bac1f01 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Controls/index.jsx @@ -0,0 +1,10 @@ +//* Imports +import { OrbitControls } from "@react-three/drei"; + +//* Main +export default function Controls({ config }) { + // Props + const { general: { showHelpers } } = config; + // Return + return showHelpers && ; +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js new file mode 100644 index 0000000..ec9c24d --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js @@ -0,0 +1,34 @@ +/** + * Mouse handler strategy for particle system + */ + +import { HandlerStrategy } from '../../../../logic/handlers/strategy'; +import { updateOnMouseMove } from '../../../../logic/index'; + +export class MouseHandler extends HandlerStrategy { + /** + * Handle mouse/touch move events + * @param {MouseEvent|TouchEvent} event - Mouse or touch event + * @param {Object} args - Particle system arguments + */ + handle(event, args) { + const { mouse, moveMouseTimeout } = args.refs; + clearTimeout(moveMouseTimeout.current); + mouse.current.isMoving = true; + moveMouseTimeout.current = setTimeout(() => mouse.current.isMoving = false, 1); + + let clientX, clientY; + if (event.type === "mousemove") { + clientX = event.clientX; + clientY = event.clientY; + } else if (event.type === "touchmove") { + clientX = event.touches[0].clientX; + clientY = event.touches[0].clientY; + } + + updateOnMouseMove({ + events: { mousemove: { clientX, clientY } }, + ...args + }); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js new file mode 100644 index 0000000..f5bdaa9 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js @@ -0,0 +1,13 @@ +import { ListenerStrategy } from '../../../../logic/listeners/strategy'; + +export class MouseListener extends ListenerStrategy { + add({ handleMouseMove }) { + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("touchmove", handleMouseMove); + } + + remove({ handleMouseMove }) { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("touchmove", handleMouseMove); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js new file mode 100644 index 0000000..564efd0 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js @@ -0,0 +1,10 @@ +import { onMouseMove } from "@semantyk/frontend/logic/services/callbacks"; +import { UpdateStrategy } from '../../../../logic/updates/strategy'; + +export class MouseUpdate extends UpdateStrategy { + apply({ refs, events }) { + const { x, y } = onMouseMove(events.mousemove); + refs.mouse.current.x = x * 2 - 1; + refs.mouse.current.y = -y * 2 + 1; + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js new file mode 100644 index 0000000..57bd581 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js @@ -0,0 +1,11 @@ +import { Color } from 'three'; +import { EffectManager } from '../../../../../logic/effects/manager'; +import { UpdateStrategy } from '../../../../../logic/updates/strategy'; + +export class ColorUpdate extends UpdateStrategy { + apply({ i, colors, particles, ...args }) { + const final = new Color(); + EffectManager.addEffect("color", { colors, particles, i, final, ...args }); + colors.set(final.toArray(), i * 3); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js new file mode 100644 index 0000000..18e09e8 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js @@ -0,0 +1,11 @@ +import { Vector3 } from 'three'; +import { EffectManager } from '../../../../../logic/effects/manager'; +import { UpdateStrategy } from '../../../../../logic/updates/strategy'; + +export class PositionUpdate extends UpdateStrategy { + apply({ object, positions, i, ...args }) { + const final = new Vector3(); + EffectManager.addEffect("position", { positions, object, i, final, ...args }); + positions.set(final.toArray(), i * 3); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/effect.js new file mode 100644 index 0000000..59bacc9 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/effect.js @@ -0,0 +1,21 @@ +import { EffectStrategy } from '../../../../../logic/effects/strategy'; + +export class ChaosEffect extends EffectStrategy { + apply({ config, data, i, idxs, mouse, particles }) { + const { unit } = data; + const { animations: { chaos, order } } = config; + + let magnitude; + let currentChaos = particles.data.chaotic[i]; + + if (idxs.has(i) && mouse.current.isMoving) { + currentChaos += chaos.magnitude; + magnitude = Math.min(currentChaos, 1); + } else { + currentChaos -= order.magnitude / unit; + magnitude = Math.max(currentChaos, 0); + } + + particles.data.chaotic[i] = magnitude; + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/update.js new file mode 100644 index 0000000..1771b66 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/update.js @@ -0,0 +1,8 @@ +import { UpdateStrategy } from '../../../../../logic/updates/strategy'; +import { EffectManager } from '../../../../../logic/effects/manager'; + +export class ChaosUpdate extends UpdateStrategy { + apply(params) { + EffectManager.addEffect("chaos", params); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/effect.js new file mode 100644 index 0000000..5db63fe --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/effect.js @@ -0,0 +1,11 @@ +import { Color } from 'three'; +import { EffectStrategy } from '../../../../../logic/effects/strategy'; + +export class ColorEffect extends EffectStrategy { + apply({ particles, i, final, colors }) { + const chaoticValue = particles.data.chaotic[i]; + const target = new Color(1, 0, 0); + final.lerp(target, chaoticValue); + colors.set(final.toArray(), i * 3); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/update.js new file mode 100644 index 0000000..57bd581 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/update.js @@ -0,0 +1,11 @@ +import { Color } from 'three'; +import { EffectManager } from '../../../../../logic/effects/manager'; +import { UpdateStrategy } from '../../../../../logic/updates/strategy'; + +export class ColorUpdate extends UpdateStrategy { + apply({ i, colors, particles, ...args }) { + const final = new Color(); + EffectManager.addEffect("color", { colors, particles, i, final, ...args }); + colors.set(final.toArray(), i * 3); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/expansion/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/expansion/effect.js new file mode 100644 index 0000000..ca14ba3 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/expansion/effect.js @@ -0,0 +1,19 @@ +import { Vector3 } from 'three'; +import { EffectStrategy } from '../../../../../logic/effects/strategy'; + +export class ExpansionEffect extends EffectStrategy { + apply({ config, object, i, final }) { + const { animations: { expansion } } = config; + const chaosValue = object.data.chaotic[i]; + const { ideal } = object.data.positions; + const positions = object.geometry.attributes.position.array; + + const source = new Vector3().fromArray(positions, i * 3); + const target = new Vector3().fromArray(ideal, i * 3); + const effect = new Vector3().subVectors(source, target); + effect.multiplyScalar(chaosValue); + effect.multiplyScalar(expansion.magnitude); + + final.add(effect); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation/effect.js new file mode 100644 index 0000000..a3d280e --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation/effect.js @@ -0,0 +1,19 @@ +import { Vector3 } from 'three'; +import { EffectStrategy } from '../../../../../logic/effects/strategy'; + +export class FlotationEffect extends EffectStrategy { + apply({ config, object, i, final, time }) { + const { offsets } = object.data.positions; + const { animations: { flotation } } = config; + + const vector = new Vector3().fromArray(offsets, i * 3); + vector.addScalar(time * flotation.speed); + const effect = new Vector3( + Math.sin(vector.x), + Math.sin(vector.y) + ); + effect.multiplyScalar(flotation.magnitude); + + final.add(effect); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation/effect.js new file mode 100644 index 0000000..3c138c3 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation/effect.js @@ -0,0 +1,20 @@ +import { Vector3 } from 'three'; +import { ease } from "@semantyk/frontend/ui/models/Particles/logic"; +import { EffectStrategy } from '../../../../../logic/effects/strategy'; + +export class InterpolationEffect extends EffectStrategy { + apply({ config, time, object, i, final }) { + const { ideal, initial } = object.data.positions; + const { animations: { interpolation: { duration } } } = config; + + const source = new Vector3().fromArray(initial, i * 3); + const target = new Vector3().fromArray(ideal, i * 3); + + const easedTime = ease(time, duration); + source.multiplyScalar(1 - easedTime); + target.multiplyScalar(easedTime); + + final.add(source); + final.add(target); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/effect.js new file mode 100644 index 0000000..8962ab4 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/effect.js @@ -0,0 +1,24 @@ +import { EffectStrategy } from '../../../../../logic/effects/strategy'; +import { InterpolationEffect } from '../interpolation/effect'; +import { FlotationEffect } from '../flotation/effect'; +import { ExpansionEffect } from '../expansion/effect'; + +export class PositionEffect extends EffectStrategy { + constructor() { + super(); + this.interpolationEffect = new InterpolationEffect(); + this.flotationEffect = new FlotationEffect(); + this.expansionEffect = new ExpansionEffect(); + } + + apply(args) { + const { animations: { interpolation } } = args.config; + + this.interpolationEffect.apply(args); + this.flotationEffect.apply(args); + + if (args.time >= interpolation.duration) { + this.expansionEffect.apply(args); + } + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/update.js new file mode 100644 index 0000000..18e09e8 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/update.js @@ -0,0 +1,11 @@ +import { Vector3 } from 'three'; +import { EffectManager } from '../../../../../logic/effects/manager'; +import { UpdateStrategy } from '../../../../../logic/updates/strategy'; + +export class PositionUpdate extends UpdateStrategy { + apply({ object, positions, i, ...args }) { + const final = new Vector3(); + EffectManager.addEffect("position", { positions, object, i, final, ...args }); + positions.set(final.toArray(), i * 3); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/resize/resize.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/resize/resize.js new file mode 100644 index 0000000..93f3b52 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/resize/resize.js @@ -0,0 +1,22 @@ +/** + * Resize handler strategy for particle system + */ + +import { HandlerStrategy } from '../../../../../logic/handlers/strategy'; +import { SetupManager } from '../../../../../logic/setups/manager'; + +export class ResizeHandler extends HandlerStrategy { + /** + * Handle window resize events + * @param {Event} event - Window resize event + * @param {Object} args - Particle system arguments + */ + handle(event, args) { + const { particles } = args.refs; + SetupManager.setupObject("camera", args); + const { particle } = args.config; + const ratio = window.innerWidth / window.innerHeight; + const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); + particles.current.material.size = size; + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Particles/index.jsx new file mode 100644 index 0000000..8a5fa42 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/index.jsx @@ -0,0 +1,16 @@ +/** + * Particles.jsx + * Atom component for the particles in the Particles model + */ + +export default function Particles({ config, refs }) { + return ( + + + + + ); +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js new file mode 100644 index 0000000..2fb3f56 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js @@ -0,0 +1,65 @@ +import { Float32BufferAttribute } from 'three'; + +import { SetupStrategy } from '../../../../logic/setups/strategy'; +import { getImageData } from '../../../../logic'; + + +export class ParticlesSetup extends SetupStrategy { + apply({ config, data: { color, unit }, objects: { image }, refs }) { + const { particle } = config; + const particles = refs.particles.current; + const { data } = getImageData({ data: { unit }, objects: { image } }); + + particles.data = { + label: "particles", + count: 0, + chaotic: [], + color, + colors: [], + positions: { ideal: [], initial: [], offsets: [] }, + }; + + const dimensions = { + x: unit, + y: (image.height / image.width) * unit, + z: unit + }; + + for (let y = 0; y < dimensions.y; y += particle.density) { + for (let x = 0; x < dimensions.x; x += particle.density) { + const alpha = data[(x + y * dimensions.x) * 4 + 3]; + if (alpha > 128) { + particles.data.chaotic.push(0); + particles.data.colors.push(color.r, color.g, color.b); + particles.data.positions.ideal.push( + x - dimensions.x / 2, + -y + dimensions.y / 2, + -dimensions.z / 2); + particles.data.positions.initial.push( + (Math.random() - 0.5) * unit * 2, + (Math.random() - 0.5) * unit * 2, + (Math.random() - 0.5) * unit * 2 + ); + particles.data.positions.offsets.push( + Math.random() * Math.PI * 2, + Math.random() * Math.PI * 2, + Math.random() * Math.PI * 2, + ); + particles.data.count++; + } + } + } + + const colorsArray = particles.data.colors; + const colorsValue = new Float32BufferAttribute(colorsArray, 3); + particles.geometry.setAttribute("color", colorsValue); + + const positionsArray = particles.data.positions.ideal; + const positionsValue = new Float32BufferAttribute(positionsArray, 3); + particles.geometry.setAttribute("position", positionsValue); + + const ratio = window.innerWidth / window.innerHeight; + const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); + particles.material.size = size; + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js new file mode 100644 index 0000000..42ddb81 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js @@ -0,0 +1,41 @@ +import { UpdateManager } from '../../../../logic/updates/manager'; +import { UpdateStrategy } from '../../../../logic/updates/strategy'; + +export class ParticlesUpdate extends UpdateStrategy { + apply({ config, objects, refs: { mouse, particles }, ...args }) { + const { clock } = objects; + const { animations: { interpolation } } = config; + + const object = particles.current; + const time = clock.current.getElapsedTime(); + const intersects = objects.raycaster.intersectObject(object); + const idxs = new Set(intersects.map(({ index }) => index)); + const positions = object.geometry.attributes.position.array; + + for (let i = 0; i < object.data.count; i++) { + if (time >= interpolation.duration) { + UpdateManager.updateAttribute("chaos", { + config, + i, + idxs, + mouse, + particles: object, + ...args + }); + } + UpdateManager.updateAttribute("position", { + config, + i, + idxs, + object, + positions, + particles, + time, + ...args + }); + } + + object.geometry.attributes.color.needsUpdate = true; + object.geometry.attributes.position.needsUpdate = true; + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx new file mode 100644 index 0000000..9fff14d --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx @@ -0,0 +1,23 @@ +//* Main +export default function Plane({ config, data, refs }) { + // Props + const { + general: { showHelpers } + } = config; + // Return + return ( + + + + + ); +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js new file mode 100644 index 0000000..68cb95d --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js @@ -0,0 +1,9 @@ +import { Plane, Vector3 } from 'three'; +import { SetupStrategy } from '../../../../logic/setups/strategy'; + +export class PlaneSetup extends SetupStrategy { + apply({ data: { unit }, refs: { plane } }) { + const normal = new Vector3(0, 0, 1); + plane.current = new Plane(normal, unit / 2); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx b/src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx new file mode 100644 index 0000000..20938db --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx @@ -0,0 +1,19 @@ +/** + * RayLine.jsx + * Atom component for the ray line in the Particles model + */ + +//* Main +export default function RayLine({ config, refs }) { + // Props + const { + general: { showHelpers } + } = config; + // Return + return ( + + + + + ); +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/RayLine/logic/line.js b/src/frontend/ui/models/Particles/components/atoms/RayLine/logic/line.js new file mode 100644 index 0000000..bf8de0a --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/RayLine/logic/line.js @@ -0,0 +1,12 @@ +import { BufferGeometry } from 'three'; +import { UpdateStrategy } from '../../../../logic/updates/strategy'; + +export class LineUpdate extends UpdateStrategy { + apply({ objects, refs, target }) { + const { origin } = objects.raycaster.ray; + const points = [origin, target]; + const geometry = new BufferGeometry().setFromPoints(points); + refs.rayLine.current.geometry.dispose(); + refs.rayLine.current.geometry = geometry; + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.jsx new file mode 100644 index 0000000..a6b2b5b --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.jsx @@ -0,0 +1,8 @@ +/** + * Raycaster.jsx + * Atom component for the raycaster in the Particles model + */ + +export default function Raycaster() { + return null; +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js new file mode 100644 index 0000000..f3729ae --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js @@ -0,0 +1,8 @@ +import { SetupStrategy } from '../../../../logic/setups/strategy'; + +export class RaycasterSetup extends SetupStrategy { + apply({ config, data: { unit }, objects: { raycaster } }) { + const { animations: { chaos: { radius } } } = config; + raycaster.params.Points.threshold = radius * unit; + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js new file mode 100644 index 0000000..092dd25 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js @@ -0,0 +1,12 @@ +import { Vector2 } from 'three'; +import { UpdateStrategy } from '../../../../logic/updates/strategy'; + +export class RaycasterUpdate extends UpdateStrategy { + apply({ objects, refs }) { + const { raycaster } = objects; + const camera = refs.camera.current; + const mouse = refs.mouse.current; + const coords = new Vector2(mouse.x, mouse.y); + raycaster.setFromCamera(coords, camera); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticleSystem.jsx b/src/frontend/ui/models/Particles/components/molecules/ParticleSystem.jsx new file mode 100644 index 0000000..25f0389 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/molecules/ParticleSystem.jsx @@ -0,0 +1,42 @@ +/** + * ParticleSystem.jsx + * Molecule component that combines Particles with its logic + */ + +//* Imports +import { useEffect } from "react"; +import { useFrame } from "@react-three/fiber"; +import Particles from "../atoms/Particles"; +import { + setupObjects, + updateObjects, +} from "@semantyk/frontend/ui/models/Particles/logic"; +import { SetupManager } from "../../logic/setups/manager"; +import { ListenerManager } from "../../logic/listeners/manager"; +import { HandlerManager } from "../../logic/handlers/manager"; + +//* Main +export default function ParticleSystem(args) { + // Logic + useEffect(() => { + setupObjects(args); + + const handleMouseMove = (event) => { + HandlerManager.handleMouseMove(event, args); + }; + + const handleResize = (event) => { + HandlerManager.handleResize(event, args); + }; + + ListenerManager.addEventListeners({ handleMouseMove, handleResize }); + return () => ListenerManager.removeEventListeners({ handleMouseMove, handleResize }); + }, [args]); + + useFrame(({ clock }) => { + args.objects.clock.current = clock; + updateObjects(args); + }); + + return ; +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/organisms/ParticlesScene.jsx b/src/frontend/ui/models/Particles/components/organisms/ParticlesScene.jsx new file mode 100644 index 0000000..0b70415 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/organisms/ParticlesScene.jsx @@ -0,0 +1,44 @@ +/** + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + * # `ParticlesScene.jsx` + * @organization: Semantyk + * @project: Client + * + * @file: This file contains the logic for the ParticlesScene component. + * + * @created: Mar 13, 2025 + * @modified: Mar 13, 2025 + * + * @author: Semantyk Team + * @maintainer: Daniel Bakas + * + * @copyright: Semantyk © 2025. All rights reserved. + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + */ + +//* Imports +import { useArgs } from "@semantyk/frontend/ui/models/Particles/hooks/useArgs"; +import { config } from "@semantyk/frontend/ui/models/Particles/config"; +import Camera from "../atoms/Camera"; +import Controls from "../atoms/Controls"; +import Box from "../atoms/Box"; +import Circle from "../atoms/Circle"; +import ParticleSystem from "../molecules/ParticleSystem"; +import Plane from "../atoms/Plane"; +import RayLine from "../atoms/RayLine"; + +//* Main +export default function ParticlesScene() { + // Hooks + const args = useArgs(); + // Return + return (<> + + + + + + + + ); +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/config/index.js b/src/frontend/ui/models/Particles/config/index.js new file mode 100644 index 0000000..51d778e --- /dev/null +++ b/src/frontend/ui/models/Particles/config/index.js @@ -0,0 +1,61 @@ +/** + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + * # `config.js` + * @organization: Semantyk + * @project: Client + * + * @file: This file contains the configuration for the Particles model. + * + * @created: Mar 7, 2025 + * @modified: Mar 7, 2025 + * + * @author: Semantyk Team + * @maintainer: Daniel Bakas + * + * @copyright: Semantyk © 2025. All rights reserved. + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + */ + +//* Main +export const config = { + // General + general: { + showHelpers: true, + scale: 1, + size: 150, + }, + // Camera + camera: { + margin: 1 / 3, + makeDefault: true + }, + // Animations + animations: { + chaos: { + magnitude: 0.25, + radius: 0.10 + }, + order: { + magnitude: 0.25 + }, + expansion: { + magnitude: 1, + }, + flotation: { + magnitude: 1, + speed: 1 + }, + interpolation: { + duration: 5 + } + }, + // Image + image: { + path: "/favicon.png" + }, + // Particles + particle: { + density: 1, + size: 0.75 + } +}; \ No newline at end of file diff --git a/src/frontend/ui/models/molecule/Particles/hooks.jsx b/src/frontend/ui/models/Particles/hooks/useArgs.jsx similarity index 88% rename from src/frontend/ui/models/molecule/Particles/hooks.jsx rename to src/frontend/ui/models/Particles/hooks/useArgs.jsx index 4d2a4a4..ca95f33 100644 --- a/src/frontend/ui/models/molecule/Particles/hooks.jsx +++ b/src/frontend/ui/models/Particles/hooks/useArgs.jsx @@ -19,13 +19,13 @@ import { useRef } from "react"; import { useLoader } from "@react-three/fiber"; import { Color, Raycaster, TextureLoader } from "three"; //* Local Imports -import { props } from "@semantyk/frontend/ui/models/molecule/Particles/logic"; +import { config } from "@semantyk/frontend/ui/models/Particles/config"; import useColorScheme from "@semantyk/frontend/hooks/useColorScheme"; //* Main export function useArgs() { // Props - const { general: { scale, size }, image: { path } } = props; + const { general: { scale, size }, image: { path } } = config; // Hooks const { colorScheme } = useColorScheme(); const { image } = useLoader(TextureLoader, path); @@ -34,7 +34,7 @@ export function useArgs() { const colorV3 = new Color(color, color, color); // Return return { - /// Data + // Data data: { color: colorV3, unit: scale * size @@ -45,12 +45,15 @@ export function useArgs() { image, raycaster: new Raycaster() }, + // Props + config, // Refs refs: { box: useRef(), camera: useRef(), circle: useRef(), mouse: useRef({ x: 0, y: 0 }), + moveMouseTimeout: useRef(), particles: useRef(), plane: useRef(), rayLine: useRef(), diff --git a/src/frontend/ui/models/Particles/index.jsx b/src/frontend/ui/models/Particles/index.jsx new file mode 100644 index 0000000..e32ae97 --- /dev/null +++ b/src/frontend/ui/models/Particles/index.jsx @@ -0,0 +1,23 @@ +/** + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + * # `index.jsx` | `ParticlesModel` + * @organization: Semantyk + * @project: Client + * + * @file: This file contains the main entry point for the Particles model. + * + * @created: Sep 12, 2024 + * @modified: Mar 12, 2025 + * + * @author: Semantyk Team + * @maintainer: Daniel Bakas + * + * @copyright: Semantyk © 2025. All rights reserved. + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + */ + +import ParticlesScene from "./components/organisms/ParticlesScene"; + +export default function ParticlesModel() { + return ; +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/effects/manager.js b/src/frontend/ui/models/Particles/logic/effects/manager.js new file mode 100644 index 0000000..47437a9 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/effects/manager.js @@ -0,0 +1,31 @@ +import { ChaosEffect } from '../../components/atoms/Particles/effects/chaos/effect'; +import { ColorEffect } from '../../components/atoms/Particles/effects/color/effect'; +import { PositionEffect } from '../../components/atoms/Particles/effects/position/effect'; + +/** + * Manager class that handles all particle effects using the Strategy pattern + */ +export class EffectManager { + static instance = new EffectManager(); + + constructor() { + this.strategies = { + chaos: new ChaosEffect(), + color: new ColorEffect(), + position: new PositionEffect() + }; + } + + addEffect(type, args) { + const strategy = this.strategies[type]; + if (strategy) { + strategy.apply(args); + } + } + + static addEffect(type, args) { + return EffectManager.instance.addEffect(type, args); + } +} + +export const { addEffect } = EffectManager; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/effects/strategy.js b/src/frontend/ui/models/Particles/logic/effects/strategy.js new file mode 100644 index 0000000..a283135 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/effects/strategy.js @@ -0,0 +1,13 @@ +/** + * Base class for particle effects following the Strategy Pattern + */ +export class EffectStrategy { + /** + * Apply the effect + * @param {Object} args - Effect arguments + * @throws {Error} Must be implemented by child classes + */ + apply(args) { + throw new Error('Effect strategy must implement apply method'); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/handlers/manager.js b/src/frontend/ui/models/Particles/logic/handlers/manager.js new file mode 100644 index 0000000..487fcf2 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/handlers/manager.js @@ -0,0 +1,59 @@ +/** + * Manager for particle system event handlers + */ + +import { MouseHandler } from '../../components/atoms/Mouse/logic/handler'; +import { ResizeHandler } from '../../components/atoms/Particles/effects/resize/resize'; + +export class HandlerManager { + static #instance = null; + #mouseHandler = new MouseHandler(); + #resizeHandler = new ResizeHandler(); + + /** + * Get the singleton instance + * @returns {HandlerManager} + */ + static getInstance() { + if (!HandlerManager.#instance) { + HandlerManager.#instance = new HandlerManager(); + } + return HandlerManager.#instance; + } + + /** + * Handle mouse/touch move event + * @param {MouseEvent|TouchEvent} event - Mouse or touch event + * @param {Object} args - Particle system arguments + */ + handleMouseMove(event, args) { + this.#mouseHandler.handle(event, args); + } + + /** + * Handle window resize event + * @param {Event} event - Window resize event + * @param {Object} args - Particle system arguments + */ + handleResize(event, args) { + this.#resizeHandler.handle(event, args); + } + + /** + * Static method to handle mouse/touch move event + * @param {MouseEvent|TouchEvent} event - Mouse or touch event + * @param {Object} args - Particle system arguments + */ + static handleMouseMove(event, args) { + HandlerManager.getInstance().handleMouseMove(event, args); + } + + /** + * Static method to handle window resize event + * @param {Event} event - Window resize event + * @param {Object} args - Particle system arguments + */ + static handleResize(event, args) { + HandlerManager.getInstance().handleResize(event, args); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/handlers/strategy.js b/src/frontend/ui/models/Particles/logic/handlers/strategy.js new file mode 100644 index 0000000..1eafcd2 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/handlers/strategy.js @@ -0,0 +1,14 @@ +/** + * Base class for particle event handlers following the Strategy Pattern + */ +export class HandlerStrategy { + /** + * Handle the event + * @param {Object} event - Event object + * @param {Object} args - Additional arguments + * @throws {Error} Must be implemented by child classes + */ + handle(event, args) { + throw new Error('Handler strategy must implement handle method'); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/index.js b/src/frontend/ui/models/Particles/logic/index.js new file mode 100644 index 0000000..bf21a74 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/index.js @@ -0,0 +1,61 @@ +/** + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + * # `logic.js` + * @organization: Semantyk + * @project: Client + * + * @created: Jul 17, 2024 + * @modified: Mar 7, 2025 + * + * @author: Semantyk Team + * @maintainer: Daniel Bakas + * + * @copyright: Semantyk © 2025. All rights reserved. + * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + */ + +//* Imports +import { Vector3 } from "three"; +//* Local Imports +import { UpdateManager } from "./updates/manager"; +import { SetupManager } from "./setups/manager"; + +//* Main +export function getImageData(args) { + // Args + const { data: { unit }, objects: { image } } = args; + // Logic + let { width, height } = image; + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + canvas.width = unit; + canvas.height = (height / width) * unit; + context.drawImage(image, 0, 0, canvas.width, canvas.height); + // Return + return context.getImageData(0, 0, canvas.width, canvas.height); +} + +export function ease(time, duration) { + const t = Math.min(time / duration, 1); + return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; +} + +export function setupObjects(args) { + SetupManager.setupObject("camera", args); + SetupManager.setupObject("plane", args); + SetupManager.setupObject("particles", args); + SetupManager.setupObject("raycaster", args); +} + +export function updateObjects(args) { + UpdateManager.updateObject("particles", args); +} + +export function updateOnMouseMove(args) { + const target = new Vector3(); + UpdateManager.updateObject("camera", args); + UpdateManager.updateObject("raycaster", args); + UpdateManager.updateObject("mouse", args); + UpdateManager.updateObject("circle", { target, ...args }); + UpdateManager.updateObject("line", { target, ...args }); +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/listeners/manager.js b/src/frontend/ui/models/Particles/logic/listeners/manager.js new file mode 100644 index 0000000..4faf509 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/listeners/manager.js @@ -0,0 +1,38 @@ +import { MouseListener } from '../../components/atoms/Mouse/logic/listener'; +import { ResizeListener } from './resize'; + +/** + * Manager class that handles all particle event listeners using the Strategy pattern + */ +export class ListenerManager { + static instance = new ListenerManager(); + + constructor() { + this.strategies = { + mouse: new MouseListener(), + resize: new ResizeListener(), + }; + } + + addEventListeners(args) { + Object.values(this.strategies).forEach(strategy => { + strategy.add(args); + }); + } + + removeEventListeners(args) { + Object.values(this.strategies).forEach(strategy => { + strategy.remove(args); + }); + } + + static addEventListeners(args) { + return ListenerManager.instance.addEventListeners(args); + } + + static removeEventListeners(args) { + return ListenerManager.instance.removeEventListeners(args); + } +} + +export const { addEventListeners, removeEventListeners } = ListenerManager; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/listeners/resize.js b/src/frontend/ui/models/Particles/logic/listeners/resize.js new file mode 100644 index 0000000..9297517 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/listeners/resize.js @@ -0,0 +1,11 @@ +import { ListenerStrategy } from './strategy'; + +export class ResizeListener extends ListenerStrategy { + add({ handleResize }) { + window.addEventListener("resize", handleResize); + } + + remove({ handleResize }) { + window.removeEventListener("resize", handleResize); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/listeners/strategy.js b/src/frontend/ui/models/Particles/logic/listeners/strategy.js new file mode 100644 index 0000000..f7b4f8c --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/listeners/strategy.js @@ -0,0 +1,22 @@ +/** + * Base class for particle event listeners following the Strategy Pattern + */ +export class ListenerStrategy { + /** + * Add the event listener + * @param {Object} args - Event arguments + * @throws {Error} Must be implemented by child classes + */ + add(args) { + throw new Error('Listener strategy must implement add method'); + } + + /** + * Remove the event listener + * @param {Object} args - Event arguments + * @throws {Error} Must be implemented by child classes + */ + remove(args) { + throw new Error('Listener strategy must implement remove method'); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/setups/manager.js b/src/frontend/ui/models/Particles/logic/setups/manager.js new file mode 100644 index 0000000..1e4c8e4 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/setups/manager.js @@ -0,0 +1,33 @@ +import { CameraSetup } from '../../components/atoms/Camera/logic/setup'; +import { ParticlesSetup } from '../../components/atoms/Particles/logic/setup'; +import { PlaneSetup } from '../../components/atoms/Plane/logic/setup'; +import { RaycasterSetup } from '../../components/atoms/Raycaster/logic/setup'; + +/** + * Manager class that handles all particle setups using the Strategy pattern + */ +export class SetupManager { + static instance = new SetupManager(); + + constructor() { + this.strategies = { + camera: new CameraSetup(), + particles: new ParticlesSetup(), + plane: new PlaneSetup(), + raycaster: new RaycasterSetup(), + }; + } + + setupObject(type, args) { + const strategy = this.strategies[type]; + if (strategy) { + strategy.apply(args); + } + } + + static setupObject(type, args) { + return SetupManager.instance.setupObject(type, args); + } +} + +export const { setupObject } = SetupManager; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/setups/strategy.js b/src/frontend/ui/models/Particles/logic/setups/strategy.js new file mode 100644 index 0000000..8a620b1 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/setups/strategy.js @@ -0,0 +1,13 @@ +/** + * Base class for particle setups following the Strategy Pattern + */ +export class SetupStrategy { + /** + * Apply the setup + * @param {Object} args - Setup arguments + * @throws {Error} Must be implemented by child classes + */ + apply(args) { + throw new Error('Setup strategy must implement apply method'); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/updates/manager.js b/src/frontend/ui/models/Particles/logic/updates/manager.js new file mode 100644 index 0000000..5a9ec9e --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/updates/manager.js @@ -0,0 +1,55 @@ +import { CircleUpdate } from '../../components/atoms/Circle/logic/update'; +import { LineUpdate } from '../../components/atoms/RayLine/logic/line'; +import { MouseUpdate } from '../../components/atoms/Mouse/logic/update'; +import { ParticlesUpdate } from '../../components/atoms/Particles/logic/update'; +import { RaycasterUpdate } from '../../components/atoms/Raycaster/logic/update'; +import { ChaosUpdate } from '../../components/atoms/Particles/effects/chaos/update'; +import { ColorUpdate } from '../../components/atoms/Particles/effects/color/update'; +import { PositionUpdate } from '../../components/atoms/Particles/effects/position/update'; + +/** + * Manager class that handles all particle updates using the Strategy pattern + */ +export class UpdateManager { + static instance = new UpdateManager(); + + constructor() { + this.objectStrategies = { + circle: new CircleUpdate(), + line: new LineUpdate(), + mouse: new MouseUpdate(), + particles: new ParticlesUpdate(), + raycaster: new RaycasterUpdate(), + }; + + this.attributeStrategies = { + chaos: new ChaosUpdate(), + color: new ColorUpdate(), + position: new PositionUpdate() + }; + } + + updateObject(type, args) { + const strategy = this.objectStrategies[type]; + if (strategy) { + strategy.apply(args); + } + } + + updateAttribute(type, args) { + const strategy = this.attributeStrategies[type]; + if (strategy) { + strategy.apply(args); + } + } + + static updateObject(type, args) { + return UpdateManager.instance.updateObject(type, args); + } + + static updateAttribute(type, args) { + return UpdateManager.instance.updateAttribute(type, args); + } +} + +export const { updateObject, updateAttribute } = UpdateManager; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/updates/strategy.js b/src/frontend/ui/models/Particles/logic/updates/strategy.js new file mode 100644 index 0000000..02695a1 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/updates/strategy.js @@ -0,0 +1,13 @@ +/** + * Base class for particle updates following the Strategy Pattern + */ +export class UpdateStrategy { + /** + * Apply the update + * @param {Object} args - Update arguments + * @throws {Error} Must be implemented by child classes + */ + apply(args) { + throw new Error('Update strategy must implement apply method'); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/molecule/Particles/effects.js b/src/frontend/ui/models/molecule/Particles/effects.js deleted file mode 100644 index 3be6ec7..0000000 --- a/src/frontend/ui/models/molecule/Particles/effects.js +++ /dev/null @@ -1,124 +0,0 @@ -/** - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - * # `effects.js` - * @organization: Semantyk - * @project: Client - * - * @created: Sep 17, 2024 - * @modified: Mar 7, 2025 - * - * @author: Semantyk Team - * @maintainer: Daniel Bakas - * - * @copyright: Semantyk © 2025. All rights reserved. - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - */ - -// - expansion -import { - ease, - props, -} from "@semantyk/frontend/ui/models/molecule/Particles/logic"; -import { Color, Vector3 } from "three"; - -//* Main -//* ---------------------------------------------------------------------------- -// Effect Builder -export function addEffect(type, args) { - // Logic - // - declare options - const options = { - // - THREE.point - color: addPointColorEffect, - position: addPointPositionEffect, - // - custom - expansion: addExpansionEffect, - flotation: addFlotationEffect, - interpolation: addInterpolationEffect, - }; - // - select option - let option = options[type]; - // Update - if (option) - option(args); -} - -//* ---------------------------------------------------------------------------- -// THREE.point Effects -// - color -export function addPointColorEffect({ particles, i, final, colors }) { - // Logic - const chaoticValue = particles.data.chaotic[i]; - const target = new Color(1, 0, 0); - // Transform - final.lerp(target, chaoticValue); - // Update - colors.set(final.toArray(), i * 3); -} - -// - position -export function addPointPositionEffect(args) { - // Props - const { animations: { interpolation } } = props; - // Effects - addEffect("interpolation", args); - addEffect("flotation", args); - if (args.time >= interpolation.duration) - addEffect("expansion", args); -} - -//* ---------------------------------------------------------------------------- -// Custom Effects -// - expansion -export function addExpansionEffect({ object, i, final }) { - // Props - const { expansion } = props.animations; - const chaosValue = object.data.chaotic[i]; - const { ideal } = object.data.positions; - const positions = object.geometry.attributes.position.array; - // Logic - const source = new Vector3().fromArray(positions, i * 3); - const target = new Vector3().fromArray(ideal, i * 3); - const effect = new Vector3().subVectors(source, target); - effect.multiplyScalar(chaosValue); - effect.multiplyScalar(expansion.magnitude); - // Add Effect - final.add(effect); -} - -// - flotation -export function addFlotationEffect({ object, i, final, time }) { - // Props - const { offsets } = object.data.positions; - const { animations: { flotation } } = props; - // Logic - const vector = new Vector3().fromArray(offsets, i * 3); - vector.addScalar(time * flotation.speed); - const effect = new Vector3( - Math.sin(vector.x), - Math.sin(vector.y) - ); - effect.multiplyScalar(flotation.magnitude); - // Prepare for Update - final.add(effect); -} - -// - interpolation -export function addInterpolationEffect(args) { - // Args - const { time, object, i, final } = args; - // Props - const { ideal, initial } = object.data.positions; - const { interpolation: { duration } } = props.animations; - // Logic - const source = new Vector3().fromArray(initial, i * 3); - const target = new Vector3().fromArray(ideal, i * 3); - // - ease timeease - const easedTime = ease(time, duration); - // - interpolate - source.multiplyScalar(1 - easedTime); - target.multiplyScalar(easedTime); - // Add Effect - final.add(source); - final.add(target); -} \ No newline at end of file diff --git a/src/frontend/ui/models/molecule/Particles/index.jsx b/src/frontend/ui/models/molecule/Particles/index.jsx deleted file mode 100644 index d3fc0f6..0000000 --- a/src/frontend/ui/models/molecule/Particles/index.jsx +++ /dev/null @@ -1,160 +0,0 @@ -/** - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - * # `index.jsx` | `ParticlesModel` - * @organization: Semantyk - * @project: Client - * - * @file: This file contains the logic for the Particles model. - * - * @created: Sep 12, 2024 - * @modified: Mar 7, 2025 - * - * @author: Semantyk Team - * @maintainer: Daniel Bakas - * - * @copyright: Semantyk © 2025. All rights reserved. - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - */ - -//* Imports -import { useEffect, useRef } from "react"; -import { CameraHelper } from "three"; -import { OrbitControls, PerspectiveCamera, useHelper } from "@react-three/drei"; -import { useFrame } from "@react-three/fiber"; -//* Local Imports -import { - addEventListeners, - props, - removeEventListeners, - setupObjects, - updateObjects, - updateOnMouseMove, -} from "@semantyk/frontend/ui/models/molecule/Particles/logic"; -import { useArgs } from "@semantyk/frontend/ui/models/molecule/Particles/hooks"; -import { - setupCamera -} from "@semantyk/frontend/ui/models/molecule/Particles/setups"; - -//* Main -export default function ParticlesModel() { - // Props - const { - general: { showHelpers }, - animations: { chaos: { radius } } - } = props; - // Hooks - // - useArgs - const args = useArgs(); - const { data, objects, refs } = args; - // Logic - const moveMouseTimeoutRef = useRef(null); - // Hooks - // - useEffect - useEffect(() => { - // Setup Objects - setupObjects({ data, objects, refs }); - // Listeners - // - mousemove/touchmove - const handleMouseMove = (event) => { - const { mouse } = refs; - clearTimeout(moveMouseTimeoutRef.current); - mouse.current.isMoving = true; - moveMouseTimeoutRef.current = setTimeout(() => mouse.current.isMoving = false, 1); - let clientX, clientY; - if (event.type === "mousemove") { - clientX = event.clientX; - clientY = event.clientY; - } else if (event.type === "touchmove") { - clientX = event.touches[0].clientX; - clientY = event.touches[0].clientY; - } - updateOnMouseMove({ - events: { mousemove: { clientX, clientY } }, - data, - objects, - refs - }); - }; - // - resize - const handleResize = () => { - const { particles } = refs; - setupCamera(args); - // Resize Particles - const { particle } = props; - const ratio = window.innerWidth / window.innerHeight; - const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); - particles.current.material.size = size; - }; - // - add - addEventListeners({ handleMouseMove, handleResize }); - // - remove - return () => removeEventListeners({ handleMouseMove, handleResize }); - }, [args, data, objects, refs]); - // - useFrame - useFrame(({ clock }) => { - objects.clock.current = clock; - updateObjects(args); - }); - // - useHelpers - useHelper(showHelpers && refs.camera, CameraHelper); - // Return - return ( - <> - {/* Camera */} - - {/* Orbit Controls */} - {showHelpers && } - {/* Box */} - - - - - {/* Circle */} - - - - - {/* Particles */} - - - - - {/* Plane */} - - - - - {/* RayLine */} - - - - - - ); -} \ No newline at end of file diff --git a/src/frontend/ui/models/molecule/Particles/logic.js b/src/frontend/ui/models/molecule/Particles/logic.js deleted file mode 100644 index d37c3de..0000000 --- a/src/frontend/ui/models/molecule/Particles/logic.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - * # `logic.js` - * @organization: Semantyk - * @project: Client - * - * @created: Jul 17, 2024 - * @modified: Mar 7, 2025 - * - * @author: Semantyk Team - * @maintainer: Daniel Bakas - * - * @copyright: Semantyk © 2025. All rights reserved. - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - */ - -//* Imports -import { Vector3 } from "three"; -//* Local Imports -import { - updateObject -} from "@semantyk/frontend/ui/models/molecule/Particles/updates"; -import { - setupObject -} from "@semantyk/frontend/ui/models/molecule/Particles/setups"; - -//* Main -// props -export const props = { - // General - general: { - showHelpers: false, - scale: 1, - size: 150, - }, - // Camera - camera: { - margin: 1 / 3, - makeDefault: true - }, - // Animations - animations: { - chaos: { - magnitude: 0.25, - radius: 0.10 - }, - order: { - magnitude: 0.25 - }, - expansion: { - magnitude: 1, - }, - flotation: { - magnitude: 1, - speed: 1 - }, - interpolation: { - duration: 5 - } - }, - // Image - image: { - path: "/favicon.png" - }, - // Particles - particle: { - density: 1, - size: 0.75 - } -}; - -export function getImageData(args) { - // Args - const { data: { unit }, objects: { image } } = args; - // Logic - let { width, height } = image; - const canvas = document.createElement("canvas"); - const context = canvas.getContext("2d"); - canvas.width = unit; - canvas.height = (height / width) * unit; - context.drawImage(image, 0, 0, canvas.width, canvas.height); - // Return - return context.getImageData(0, 0, canvas.width, canvas.height); -} - -export function ease(time, duration) { - const t = Math.min(time / duration, 1); - return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; -} - -export function addEventListeners(args) { - // Args - const { handleMouseMove, handleResize } = args; - // Listeners - window.addEventListener("mousemove", handleMouseMove); - window.addEventListener("touchmove", handleMouseMove); - window.addEventListener("resize", handleResize); -} - -export function removeEventListeners(args) { - // Args - const { handleMouseMove, handleResize } = args; - // Listeners - window.removeEventListener("mousemove", handleMouseMove); - window.removeEventListener("touchmove", handleMouseMove); - window.removeEventListener("resize", handleResize); -} - -export function setupObjects(args) { - // Setup - // - camera - setupObject("camera", args); - // - plane - setupObject("plane", args); - // - object - setupObject("particles", args); - // - raycaster - setupObject("raycaster", args); -} - -export function updateObjects(args) { - updateObject("particles", args); -} - -export function updateOnMouseMove(args) { - // Logic - const target = new Vector3(); - updateObject("camera", args); - updateObject("raycaster", args); - updateObject("mouse", args); - updateObject("circle", { target, ...args }); - updateObject("line", { target, ...args }); -} \ No newline at end of file diff --git a/src/frontend/ui/models/molecule/Particles/setups.js b/src/frontend/ui/models/molecule/Particles/setups.js deleted file mode 100644 index 162595a..0000000 --- a/src/frontend/ui/models/molecule/Particles/setups.js +++ /dev/null @@ -1,131 +0,0 @@ -/** - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - * # `setups.js` - * @organization: Semantyk - * @project: Client - * - * @created: Sep 17, 2024 - * @modified: Mar 7, 2025 - * - * @author: Semantyk Team - * @maintainer: Daniel Bakas - * - * @copyright: Semantyk © 2025. All rights reserved. - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - */ - -//* Imports -import { - getImageData, - props -} from "@semantyk/frontend/ui/models/molecule/Particles/logic"; -// - plane -import { Float32BufferAttribute, Plane, Vector3 } from "three"; - -//* Main -export function setupObject(type, args) { - switch (type) { - case "camera": - setupCamera(args); - case "particles": - setupParticles(args); - case "plane": - setupPlane(args); - case "raycaster": - setupRaycaster(args); - default: - return; - } -} - -export function setupCamera(args) { - // Args - const { data: { unit }, refs: { camera } } = args; - let { camera: { margin } } = props; - // Logic - // - camera - const aspectRatio = window.innerWidth / window.innerHeight; - let x = (1 + margin) / ((aspectRatio >= 1) ? 2 : (2 * aspectRatio)); - const fx = 2 * Math.atan(x) * (180 / Math.PI); - camera.current.fov = fx; - camera.current.aspect = aspectRatio; - camera.current.position.z = unit / 2; - camera.current.updateProjectionMatrix(); -} - -// - object -export function setupParticles(args) { - // Args - const { data: { color, unit }, objects: { image }, refs } = args; - const { particle } = props; - const particles = refs.particles.current; - const { data } = getImageData(args); - particles.data = { - label: "particles", - count: 0, - chaotic: [], - color, - colors: [], - positions: { ideal: [], initial: [], offsets: [] }, - }; - - const dimensions = { - x: unit, - y: (image.height / image.width) * unit, - z: unit - }; - for (let y = 0; y < dimensions.y; y += particle.density) { - for (let x = 0; x < dimensions.x; x += particle.density) { - const alpha = data[(x + y * dimensions.x) * 4 + 3]; - if (alpha > 128) { - particles.data.chaotic.push(0); - particles.data.colors.push(color.r, color.g, color.b); - particles.data.positions.ideal.push( - x - dimensions.x / 2, - -y + dimensions.y / 2, - -dimensions.z / 2); - particles.data.positions.initial.push( - (Math.random() - 0.5) * unit * 2, - (Math.random() - 0.5) * unit * 2, - (Math.random() - 0.5) * unit * 2 - ); - particles.data.positions.offsets.push( - Math.random() * Math.PI * 2, - Math.random() * Math.PI * 2, - Math.random() * Math.PI * 2, - ); - particles.data.count++; - } - } - } - // - color - const colorsArray = particles.data.colors; - const colorsValue = new Float32BufferAttribute(colorsArray, 3); - particles.geometry.setAttribute("color", colorsValue); - // - position - const positionsArray = particles.data.positions.ideal; - const positionsValue = new Float32BufferAttribute(positionsArray, 3); - particles.geometry.setAttribute("position", positionsValue); - // - size - const ratio = window.innerWidth / window.innerHeight; - const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); - particles.material.size = size; -} - -// - plane -export function setupPlane(args) { - // Args - const { data: { unit }, refs: { plane } } = args; - const normal = new Vector3(0, 0, 1); - plane.current = new Plane(normal, unit / 2); -} - -// - raycaster -export function setupRaycaster(args) { - // Args - const { data: { unit }, objects: { raycaster } } = args; - // Props - const { animations: { chaos: { radius } } } = props; - // Logic - raycaster.params.Points.threshold = radius * unit; -} \ No newline at end of file diff --git a/src/frontend/ui/models/molecule/Particles/updates.js b/src/frontend/ui/models/molecule/Particles/updates.js deleted file mode 100644 index 3a99803..0000000 --- a/src/frontend/ui/models/molecule/Particles/updates.js +++ /dev/null @@ -1,182 +0,0 @@ -/** - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - * # `updates.js` - * @organization: Semantyk - * @project: Client - * - * @created: Sep 17, 2024 - * @modified: Mar 7, 2025 - * - * @author: Semantyk Team - * @maintainer: Daniel Bakas - * - * @copyright: Semantyk © 2025. All rights reserved. - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - */ - -//* Imports -import { BufferGeometry, Color, Vector2, Vector3 } from "three"; -import { - addEffect -} from "@semantyk/frontend/ui/models/molecule/Particles/effects"; -import { onMouseMove } from "@semantyk/frontend/logic/services/callbacks"; -//* ---------------------------------------------------------------------------- -// Particle Updates -// - particle.chaos -import { props } from "@semantyk/frontend/ui/models/molecule/Particles/logic"; - -//* Main -//* ---------------------------------------------------------------------------- -// Update Factories -// - particles -export function updateObject(type, args) { - // Logic - // - declare options - const options = { - circle: updateCircle, - line: updateLine, - mouse: updateMouse, - particles: updateParticles, - raycaster: updateRaycaster, - }; - // - select option - let option = options[type]; - // Update - if (option) - option(args); -} - -// - attribute -export function updateAttribute(type, args) { - // Logic - // - declare options - const options = { - chaos: updateChaos, - color: updateColor, - position: updatePosition - }; - // - select option - let option = options[type]; - // Update - if (option) - option(args); -} - -//* ---------------------------------------------------------------------------- -// Object Updates -// - circle -export function updateCircle({ objects, refs, target }) { - // Logic - objects.raycaster.ray.intersectPlane(refs.plane.current, target); - refs.circle.current.position.copy(target); -} - -// - particles -export const updateLine = ({ objects, refs, target }) => { - // Logic - const { origin } = objects.raycaster.ray; - const points = [origin, target]; - const geometry = new BufferGeometry().setFromPoints(points); - refs.rayLine.current.geometry.dispose(); - // Update - refs.rayLine.current.geometry = geometry; -}; - -// - mouse -export const updateMouse = ({ refs, events }) => { - const { x, y } = onMouseMove(events.mousemove); - refs.mouse.current.x = x * 2 - 1; - refs.mouse.current.y = -y * 2 + 1; -}; - -// - particles -export const updateParticles = ({ - objects, - refs: { mouse, particles }, - ...args - }) => { - // Props - let { clock } = objects; - const { interpolation } = props.animations; - // Logic - const object = particles.current; - const time = clock.current.getElapsedTime(); - const intersects = objects.raycaster.intersectObject(object); - const idxs = new Set(intersects.map(({ index }) => index)); - const colors = object.geometry.attributes.color.array; - const positions = object.geometry.attributes.position.array; - // Update - // - each particle - for (let i = 0; i < object.data.count; i++) { - if (time >= interpolation.duration) { - updateAttribute("chaos", { - i, - idxs, - mouse, - particles: object, - ...args - }); - // updateAttribute("color", { i, colors, particles: object, ...args }); - } - updateAttribute("position", { - i, - idxs, - object, - positions, - particles, - time, - ...args - }); - } - // - all particles - object.geometry.attributes.color.needsUpdate = true; - object.geometry.attributes.position.needsUpdate = true; -}; - -// - raycaster -export function updateRaycaster({ objects, refs }) { - // Args - const { raycaster } = objects; - const camera = refs.camera.current; - const mouse = refs.mouse.current; - const coords = new Vector2(mouse.x, mouse.y); - raycaster.setFromCamera(coords, camera); -} - -const updateChaos = ({ data, i, idxs, mouse, particles }) => { - // Props - const { unit } = data; - const { animations: { chaos, order } } = props; - // Logic - let magnitude; - let currentChaos = particles.data.chaotic[i]; - if (idxs.has(i) && mouse.current.isMoving) { - currentChaos += chaos.magnitude; - magnitude = Math.min(currentChaos, 1); - } else { - // magnitude over time - currentChaos -= order.magnitude / unit; - magnitude = Math.max(currentChaos, 0); - } - particles.data.chaotic[i] = magnitude; -}; - -// - particle.color -const updateColor = ({ i, colors, particles, ...args }) => { - // Logic - let final = new Color(); - // Effects - addEffect("color", { colors, particles, i, final, ...args }); - // Update - colors.set(final.toArray(), i * 3); -}; - -// - particle.position -function updatePosition({ object, positions, i, ...args }) { - // Logic - let final = new Vector3(); - // Effects - addEffect("position", { positions, object, i, final, ...args }); - // Update - positions.set(final.toArray(), i * 3); -} \ No newline at end of file From 7a336f4696798903e9211618c5c5f0db74685a04 Mon Sep 17 00:00:00 2001 From: "Daniel B." Date: Thu, 13 Mar 2025 17:32:57 -0600 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=A7=B9=20Chore:=20Refactor=20Particle?= =?UTF-8?q?s=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Centralize strategy and manager --- public/bimi.svg | 20 +++++ .../ui/components/molecules/Model/index.jsx | 2 +- .../molecules/Model/logic/manager.js | 56 ++++++++++++ .../molecules/Model/logic/strategy.js | 31 +++++++ .../components/atoms/Camera/index.jsx | 3 + .../components/atoms/Camera/logic/setup.js | 14 ++- .../components/atoms/Circle/index.jsx | 5 ++ .../components/atoms/Circle/logic/update.js | 6 +- .../components/atoms/Controls/index.jsx | 10 --- .../Particles/components/atoms/Mouse/index.js | 3 + .../components/atoms/Mouse/logic/handler.js | 12 +-- .../components/atoms/Mouse/logic/listener.js | 4 +- .../components/atoms/Mouse/logic/update.js | 6 +- .../components/atoms/Mouse/updates/color.js | 8 +- .../atoms/Mouse/updates/position.js | 8 +- .../atoms/Particles/effects/chaos.js | 25 ++++++ .../atoms/Particles/effects/chaos/effect.js | 21 ----- .../atoms/Particles/effects/chaos/update.js | 8 -- .../atoms/Particles/effects/color.js | 12 +++ .../atoms/Particles/effects/color/effect.js | 11 --- .../atoms/Particles/effects/color/update.js | 11 --- .../atoms/Particles/effects/entropy.js | 20 +++++ .../Particles/effects/expansion/effect.js | 19 ---- .../atoms/Particles/effects/flotation.js | 20 +++++ .../Particles/effects/flotation/effect.js | 19 ---- .../atoms/Particles/effects/index.js | 4 + .../effect.js => interpolation.js} | 11 +-- .../atoms/Particles/effects/position.js | 31 +++++++ .../Particles/effects/position/effect.js | 24 ----- .../Particles/effects/position/update.js | 11 --- .../atoms/Particles/effects/resize/resize.js | 22 ----- .../atoms/Particles/handlers/index.js | 1 + .../atoms/Particles/handlers/resize.js | 21 +++++ .../components/atoms/Particles/index.js | 3 + .../components/atoms/Particles/logic/index.js | 2 + .../components/atoms/Particles/logic/setup.js | 17 ++-- .../atoms/Particles/logic/update.js | 44 +++------- .../components/atoms/Plane/index.jsx | 5 ++ .../components/atoms/Plane/logic/setup.js | 14 ++- .../components/atoms/RayLine/index.jsx | 5 ++ .../RayLine/logic/{line.js => update.js} | 6 +- .../components/atoms/Raycaster/index.js | 2 + .../components/atoms/Raycaster/logic/setup.js | 14 ++- .../atoms/Raycaster/logic/update.js | 6 +- .../Particles/components/atoms/index.js | 9 ++ .../components/molecules/Controls/index.jsx | 22 +++++ .../index.jsx} | 16 ++-- .../components/organisms/ParticlesScene.jsx | 44 ---------- .../ui/models/Particles/config/index.js | 2 +- .../ui/models/Particles/hooks/useArgs.jsx | 4 +- src/frontend/ui/models/Particles/index.jsx | 23 +++-- .../models/Particles/logic/effects/manager.js | 31 ------- .../Particles/logic/effects/strategy.js | 13 --- .../Particles/logic/handlers/manager.js | 59 ------------- .../Particles/logic/handlers/strategy.js | 14 --- .../ui/models/Particles/logic/index.js | 23 +++-- .../Particles/logic/listeners/manager.js | 38 -------- .../Particles/logic/listeners/resize.js | 4 +- .../Particles/logic/listeners/strategy.js | 22 ----- .../ui/models/Particles/logic/manager.js | 88 +++++++++++++++++++ .../models/Particles/logic/setups/manager.js | 33 ------- .../models/Particles/logic/setups/strategy.js | 13 --- .../models/Particles/logic/updates/manager.js | 55 ------------ .../Particles/logic/updates/strategy.js | 13 --- 64 files changed, 517 insertions(+), 606 deletions(-) create mode 100644 public/bimi.svg create mode 100644 src/frontend/ui/components/molecules/Model/logic/manager.js create mode 100644 src/frontend/ui/components/molecules/Model/logic/strategy.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Controls/index.jsx create mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/index.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/effect.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/update.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/effect.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/update.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/expansion/effect.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation/effect.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/index.js rename src/frontend/ui/models/Particles/components/atoms/Particles/effects/{interpolation/effect.js => interpolation.js} (52%) create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/effect.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/update.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/resize/resize.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/handlers/index.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/index.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/logic/index.js rename src/frontend/ui/models/Particles/components/atoms/RayLine/logic/{line.js => update.js} (62%) create mode 100644 src/frontend/ui/models/Particles/components/atoms/Raycaster/index.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/index.js create mode 100644 src/frontend/ui/models/Particles/components/molecules/Controls/index.jsx rename src/frontend/ui/models/Particles/components/molecules/{ParticleSystem.jsx => ParticlesSystem/index.jsx} (53%) delete mode 100644 src/frontend/ui/models/Particles/components/organisms/ParticlesScene.jsx delete mode 100644 src/frontend/ui/models/Particles/logic/effects/manager.js delete mode 100644 src/frontend/ui/models/Particles/logic/effects/strategy.js delete mode 100644 src/frontend/ui/models/Particles/logic/handlers/manager.js delete mode 100644 src/frontend/ui/models/Particles/logic/handlers/strategy.js delete mode 100644 src/frontend/ui/models/Particles/logic/listeners/manager.js delete mode 100644 src/frontend/ui/models/Particles/logic/listeners/strategy.js create mode 100644 src/frontend/ui/models/Particles/logic/manager.js delete mode 100644 src/frontend/ui/models/Particles/logic/setups/manager.js delete mode 100644 src/frontend/ui/models/Particles/logic/setups/strategy.js delete mode 100644 src/frontend/ui/models/Particles/logic/updates/manager.js delete mode 100644 src/frontend/ui/models/Particles/logic/updates/strategy.js diff --git a/public/bimi.svg b/public/bimi.svg new file mode 100644 index 0000000..af7dc2c --- /dev/null +++ b/public/bimi.svg @@ -0,0 +1,20 @@ + + + bimi-svg-tiny-12-ps + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/frontend/ui/components/molecules/Model/index.jsx b/src/frontend/ui/components/molecules/Model/index.jsx index 80020bc..e85efc4 100644 --- a/src/frontend/ui/components/molecules/Model/index.jsx +++ b/src/frontend/ui/components/molecules/Model/index.jsx @@ -21,9 +21,9 @@ //* Imports import React from "react"; import Canvas from "@semantyk/frontend/ui/components/molecules/Canvas"; -import ParticlesModel from "@semantyk/frontend/ui/models/Particles"; import GraphModel from "@semantyk/frontend/ui/models/Graph"; import { usePathname } from "next/navigation"; +import ParticlesModel from "@semantyk/frontend/ui/models/Particles"; //* Main export default function Model() { diff --git a/src/frontend/ui/components/molecules/Model/logic/manager.js b/src/frontend/ui/components/molecules/Model/logic/manager.js new file mode 100644 index 0000000..aa03b56 --- /dev/null +++ b/src/frontend/ui/components/molecules/Model/logic/manager.js @@ -0,0 +1,56 @@ +/** + * Base manager class for models using the Strategy pattern + */ +export class ModelManager { + constructor() { + if (this.constructor === ModelManager) { + throw new Error('Abstract class ModelManager cannot be instantiated directly'); + } + } + + // Generic method to handle operations on collections + execute(collection, type, args) { + const item = collection[type]; + if (item) { + return item.execute(args); + } + } + + // Event listener methods + addEventListeners(args) { + Object.values(this.listeners).forEach(listener => { + listener.add(args); + }); + } + + removeEventListeners(args) { + Object.values(this.listeners).forEach(listener => { + listener.remove(args); + }); + } + + // Static helper method that automatically uses the correct instance + static execute(methodName, type, ...args) { + const instance = this.instance; + if (!instance) { + throw new Error(`No instance found for ${this.name}. Make sure to initialize the static instance property.`); + } + + switch (methodName) { + case 'addEffect': + return instance.addEffect(type, args[0]); + case 'handleEvent': + return instance.handleEvent(type, args[0], args[1]); + case 'setupObject': + return instance.setupObject(type, args[0]); + case 'updateObject': + return instance.updateObject(type, args[0]); + case 'addEventListeners': + return instance.addEventListeners(type); + case 'removeEventListeners': + return instance.removeEventListeners(type); + default: + throw new Error(`Unknown method ${methodName}`); + } + } +} \ No newline at end of file diff --git a/src/frontend/ui/components/molecules/Model/logic/strategy.js b/src/frontend/ui/components/molecules/Model/logic/strategy.js new file mode 100644 index 0000000..1c1fd7c --- /dev/null +++ b/src/frontend/ui/components/molecules/Model/logic/strategy.js @@ -0,0 +1,31 @@ +/** + * Base class for model strategies following the Strategy Pattern + */ +export class ModelStrategy { + /** + * Add the event listener + * @param {Object} args - Event arguments + * @throws {Error} Must be implemented by child classes + */ + add(args) { + throw new Error('Strategy must implement add method'); + } + + /** + * Execute the strategy + * @param {Object} args - Strategy arguments + * @throws {Error} Must be implemented by child classes + */ + execute(args) { + throw new Error('Strategy must implement execute method'); + } + + /** + * Remove the event listener + * @param {Object} args - Event arguments + * @throws {Error} Must be implemented by child classes + */ + remove(args) { + throw new Error('Strategy must implement remove method'); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx index 636282d..2e5c7f0 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx @@ -6,6 +6,9 @@ import { PerspectiveCamera } from "@react-three/drei"; import { CameraHelper } from "three"; import { useHelper } from "@react-three/drei"; +import { CameraSetup } from './logic/setup'; + +export { CameraSetup }; export default function Camera({ config, refs }) { // Props diff --git a/src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js index 93ada07..1d0726f 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js +++ b/src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js @@ -1,7 +1,15 @@ -import { SetupStrategy } from '../../../../logic/setups/strategy'; +/** + * Camera setup strategy for particle system + */ -export class CameraSetup extends SetupStrategy { - apply({ config, data: { unit }, refs: { camera } }) { +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; + +export class CameraSetup extends ModelStrategy { + /** + * Execute camera setup + * @param {Object} args - Arguments containing config, data, and refs + */ + execute({ config, data: { unit }, refs: { camera } }) { const { camera: { margin } } = config; const aspectRatio = window.innerWidth / window.innerHeight; diff --git a/src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx index a253cd2..646bfa7 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx @@ -3,6 +3,11 @@ * Atom component for the circle in the Particles model */ +//* Imports +import { CircleUpdate } from './logic/update'; + +export { CircleUpdate }; + //* Main export default function Circle({ config, data, refs }) { // Props diff --git a/src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js index c00e734..7845f40 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js +++ b/src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js @@ -1,7 +1,7 @@ -import { UpdateStrategy } from '../../../../logic/updates/strategy'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class CircleUpdate extends UpdateStrategy { - apply({ objects, refs, target }) { +export class CircleUpdate extends ModelStrategy { + execute({ objects, refs, target }) { objects.raycaster.ray.intersectPlane(refs.plane.current, target); refs.circle.current.position.copy(target); } diff --git a/src/frontend/ui/models/Particles/components/atoms/Controls/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Controls/index.jsx deleted file mode 100644 index bac1f01..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Controls/index.jsx +++ /dev/null @@ -1,10 +0,0 @@ -//* Imports -import { OrbitControls } from "@react-three/drei"; - -//* Main -export default function Controls({ config }) { - // Props - const { general: { showHelpers } } = config; - // Return - return showHelpers && ; -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/index.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/index.js new file mode 100644 index 0000000..d1ba805 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/index.js @@ -0,0 +1,3 @@ +export { MouseHandler } from './logic/handler'; +export { MouseListener } from './logic/listener'; +export { MouseUpdate } from './logic/update'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js index ec9c24d..3ef64a9 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js @@ -2,16 +2,16 @@ * Mouse handler strategy for particle system */ -import { HandlerStrategy } from '../../../../logic/handlers/strategy'; +import { Vector2 } from 'three'; +import { ModelStrategy } from '@semantyk/frontend/ui/components/molecules/Model/logic/strategy'; import { updateOnMouseMove } from '../../../../logic/index'; -export class MouseHandler extends HandlerStrategy { +export class MouseHandler extends ModelStrategy { /** - * Handle mouse/touch move events - * @param {MouseEvent|TouchEvent} event - Mouse or touch event - * @param {Object} args - Particle system arguments + * Execute mouse/touch move events + * @param {Object} args - Arguments containing event and particle system data */ - handle(event, args) { + execute({ event, ...args }) { const { mouse, moveMouseTimeout } = args.refs; clearTimeout(moveMouseTimeout.current); mouse.current.isMoving = true; diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js index f5bdaa9..d9f1bfe 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js @@ -1,6 +1,6 @@ -import { ListenerStrategy } from '../../../../logic/listeners/strategy'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class MouseListener extends ListenerStrategy { +export class MouseListener extends ModelStrategy { add({ handleMouseMove }) { window.addEventListener("mousemove", handleMouseMove); window.addEventListener("touchmove", handleMouseMove); diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js index 564efd0..b81889e 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js @@ -1,8 +1,8 @@ import { onMouseMove } from "@semantyk/frontend/logic/services/callbacks"; -import { UpdateStrategy } from '../../../../logic/updates/strategy'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class MouseUpdate extends UpdateStrategy { - apply({ refs, events }) { +export class MouseUpdate extends ModelStrategy { + execute({ refs, events }) { const { x, y } = onMouseMove(events.mousemove); refs.mouse.current.x = x * 2 - 1; refs.mouse.current.y = -y * 2 + 1; diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js index 57bd581..6623815 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js @@ -1,11 +1,11 @@ import { Color } from 'three'; -import { EffectManager } from '../../../../../logic/effects/manager'; -import { UpdateStrategy } from '../../../../../logic/updates/strategy'; +import { ParticleManager } from '../../../../../logic/manager'; +import { ParticleStrategy } from '../../../../../logic/strategy'; -export class ColorUpdate extends UpdateStrategy { +export class ColorUpdate extends ParticleStrategy { apply({ i, colors, particles, ...args }) { const final = new Color(); - EffectManager.addEffect("color", { colors, particles, i, final, ...args }); + ParticleManager.addEffect("color", { colors, particles, i, final, ...args }); colors.set(final.toArray(), i * 3); } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js index 18e09e8..b643b60 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js @@ -1,11 +1,11 @@ import { Vector3 } from 'three'; -import { EffectManager } from '../../../../../logic/effects/manager'; -import { UpdateStrategy } from '../../../../../logic/updates/strategy'; +import { ParticleManager } from '../../../../../logic/manager'; +import { ParticleStrategy } from '../../../../../logic/strategy'; -export class PositionUpdate extends UpdateStrategy { +export class PositionUpdate extends ParticleStrategy { apply({ object, positions, i, ...args }) { const final = new Vector3(); - EffectManager.addEffect("position", { positions, object, i, final, ...args }); + ParticleManager.addEffect("position", { positions, object, i, final, ...args }); positions.set(final.toArray(), i * 3); } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js new file mode 100644 index 0000000..56149f0 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js @@ -0,0 +1,25 @@ +import { ModelStrategy } from '@semantyk/frontend/ui/components/molecules/Model/logic/strategy'; + +export class ChaosEffect extends ModelStrategy { + execute({ config, data, i, idxs, final, objects: { clock }, refs: { mouse, particles } }) { + const { unit } = data; + const { animations: { chaos, order, interpolation } } = config; + const elapsedTime = clock.current.getElapsedTime(); + + if (elapsedTime < interpolation.duration) return; + + let magnitude; + let currentChaos = particles.current.data.chaotic[i]; + + if (idxs.has(i) && mouse.current.isMoving) { + currentChaos += chaos.magnitude; + magnitude = Math.min(currentChaos, 1); + } else { + currentChaos -= order.magnitude / unit; + magnitude = Math.max(currentChaos, 0); + } + + particles.current.data.chaotic[i] = magnitude; + final.multiplyScalar(magnitude); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/effect.js deleted file mode 100644 index 59bacc9..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/effect.js +++ /dev/null @@ -1,21 +0,0 @@ -import { EffectStrategy } from '../../../../../logic/effects/strategy'; - -export class ChaosEffect extends EffectStrategy { - apply({ config, data, i, idxs, mouse, particles }) { - const { unit } = data; - const { animations: { chaos, order } } = config; - - let magnitude; - let currentChaos = particles.data.chaotic[i]; - - if (idxs.has(i) && mouse.current.isMoving) { - currentChaos += chaos.magnitude; - magnitude = Math.min(currentChaos, 1); - } else { - currentChaos -= order.magnitude / unit; - magnitude = Math.max(currentChaos, 0); - } - - particles.data.chaotic[i] = magnitude; - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/update.js deleted file mode 100644 index 1771b66..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos/update.js +++ /dev/null @@ -1,8 +0,0 @@ -import { UpdateStrategy } from '../../../../../logic/updates/strategy'; -import { EffectManager } from '../../../../../logic/effects/manager'; - -export class ChaosUpdate extends UpdateStrategy { - apply(params) { - EffectManager.addEffect("chaos", params); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js new file mode 100644 index 0000000..bc596fc --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js @@ -0,0 +1,12 @@ +import { Color } from 'three'; +import { ModelStrategy } from '@semantyk/frontend/ui/components/molecules/Model/logic/strategy'; + +export class ColorEffect extends ModelStrategy { + execute({ data: { color }, i, ...args }) { + const chaoticValue = args.refs.particles.current.data.chaotic[i]; + const final = color.clone(); + const target = new Color(1, 0, 0); + final.lerp(target, chaoticValue); + args.refs.particles.current.geometry.attributes.color.set(final.toArray(), i * 3); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/effect.js deleted file mode 100644 index 5db63fe..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/effect.js +++ /dev/null @@ -1,11 +0,0 @@ -import { Color } from 'three'; -import { EffectStrategy } from '../../../../../logic/effects/strategy'; - -export class ColorEffect extends EffectStrategy { - apply({ particles, i, final, colors }) { - const chaoticValue = particles.data.chaotic[i]; - const target = new Color(1, 0, 0); - final.lerp(target, chaoticValue); - colors.set(final.toArray(), i * 3); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/update.js deleted file mode 100644 index 57bd581..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color/update.js +++ /dev/null @@ -1,11 +0,0 @@ -import { Color } from 'three'; -import { EffectManager } from '../../../../../logic/effects/manager'; -import { UpdateStrategy } from '../../../../../logic/updates/strategy'; - -export class ColorUpdate extends UpdateStrategy { - apply({ i, colors, particles, ...args }) { - const final = new Color(); - EffectManager.addEffect("color", { colors, particles, i, final, ...args }); - colors.set(final.toArray(), i * 3); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js new file mode 100644 index 0000000..741e222 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js @@ -0,0 +1,20 @@ +import { Vector3 } from 'three'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; + +export class EntropyEffect extends ModelStrategy { + execute({ config, i, idxs, final, ...args }) { + const { animations: { expansion, interpolation } } = config; + const elapsedTime = args.objects.clock.current.getElapsedTime(); + + if (elapsedTime < interpolation.duration) return; + const { ideal } = args.refs.particles.current.data.positions; + const positions = args.refs.particles.current.geometry.attributes.position.array; + + const source = new Vector3().fromArray(positions, i * 3); + const target = new Vector3().fromArray(ideal, i * 3); + const effect = new Vector3().subVectors(source, target); + effect.multiplyScalar(expansion.magnitude); + + final.add(effect); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/expansion/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/expansion/effect.js deleted file mode 100644 index ca14ba3..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/expansion/effect.js +++ /dev/null @@ -1,19 +0,0 @@ -import { Vector3 } from 'three'; -import { EffectStrategy } from '../../../../../logic/effects/strategy'; - -export class ExpansionEffect extends EffectStrategy { - apply({ config, object, i, final }) { - const { animations: { expansion } } = config; - const chaosValue = object.data.chaotic[i]; - const { ideal } = object.data.positions; - const positions = object.geometry.attributes.position.array; - - const source = new Vector3().fromArray(positions, i * 3); - const target = new Vector3().fromArray(ideal, i * 3); - const effect = new Vector3().subVectors(source, target); - effect.multiplyScalar(chaosValue); - effect.multiplyScalar(expansion.magnitude); - - final.add(effect); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js new file mode 100644 index 0000000..73a6bae --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js @@ -0,0 +1,20 @@ +import { Vector3 } from 'three'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; + +export class FlotationEffect extends ModelStrategy { + execute({ config, i, final, objects: { clock }, refs: { particles } }) { + const { offsets } = particles.current.data.positions; + const { animations: { flotation } } = config; + const elapsedTime = clock.current.getElapsedTime(); + + const vector = new Vector3().fromArray(offsets, i * 3); + vector.addScalar(elapsedTime * flotation.speed); + const effect = new Vector3( + Math.sin(vector.x), + Math.sin(vector.y) + ); + effect.multiplyScalar(flotation.magnitude); + + final.add(effect); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation/effect.js deleted file mode 100644 index a3d280e..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation/effect.js +++ /dev/null @@ -1,19 +0,0 @@ -import { Vector3 } from 'three'; -import { EffectStrategy } from '../../../../../logic/effects/strategy'; - -export class FlotationEffect extends EffectStrategy { - apply({ config, object, i, final, time }) { - const { offsets } = object.data.positions; - const { animations: { flotation } } = config; - - const vector = new Vector3().fromArray(offsets, i * 3); - vector.addScalar(time * flotation.speed); - const effect = new Vector3( - Math.sin(vector.x), - Math.sin(vector.y) - ); - effect.multiplyScalar(flotation.magnitude); - - final.add(effect); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/index.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/index.js new file mode 100644 index 0000000..f033d6b --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/index.js @@ -0,0 +1,4 @@ +export { ChaosEffect } from './chaos'; +export { ColorEffect } from './color'; +export { EntropyEffect } from './entropy'; +export { PositionEffect } from './position'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js similarity index 52% rename from src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation/effect.js rename to src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js index 3c138c3..882a536 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation/effect.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js @@ -1,16 +1,17 @@ import { Vector3 } from 'three'; import { ease } from "@semantyk/frontend/ui/models/Particles/logic"; -import { EffectStrategy } from '../../../../../logic/effects/strategy'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class InterpolationEffect extends EffectStrategy { - apply({ config, time, object, i, final }) { - const { ideal, initial } = object.data.positions; +export class InterpolationEffect extends ModelStrategy { + execute({ config, i, final, objects: { clock }, refs: { particles } }) { + const { ideal, initial } = particles.current.data.positions; const { animations: { interpolation: { duration } } } = config; const source = new Vector3().fromArray(initial, i * 3); const target = new Vector3().fromArray(ideal, i * 3); - const easedTime = ease(time, duration); + const elapsedTime = clock.current.getElapsedTime(); + const easedTime = ease(elapsedTime, duration); source.multiplyScalar(1 - easedTime); target.multiplyScalar(easedTime); diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js new file mode 100644 index 0000000..06c4db6 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js @@ -0,0 +1,31 @@ +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; +import { InterpolationEffect } from './interpolation'; +import { FlotationEffect } from './flotation'; +import { EntropyEffect } from './entropy'; +import { Vector3 } from 'three'; +import { ChaosEffect } from "./chaos"; + +export class PositionEffect extends ModelStrategy { + constructor() { + super(); + this.chaosEffect = new ChaosEffect(); + this.entropyEffect = new EntropyEffect(); + this.interpolationEffect = new InterpolationEffect(); + this.flotationEffect = new FlotationEffect(); + } + + execute({ i, idxs, ...args }) { + const { particles } = args.refs; + const final = new Vector3() + + //* Effects + //! Order Matters + this.entropyEffect.execute({ i, idxs, final, ...args }); + this.chaosEffect.execute({ i, idxs, final, ...args }); + this.interpolationEffect.execute({ i, final, ...args }); + this.flotationEffect.execute({ i, final, ...args }); + + const positions = particles.current.geometry.attributes.position.array; + positions.set(final.toArray(), i * 3); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/effect.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/effect.js deleted file mode 100644 index 8962ab4..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/effect.js +++ /dev/null @@ -1,24 +0,0 @@ -import { EffectStrategy } from '../../../../../logic/effects/strategy'; -import { InterpolationEffect } from '../interpolation/effect'; -import { FlotationEffect } from '../flotation/effect'; -import { ExpansionEffect } from '../expansion/effect'; - -export class PositionEffect extends EffectStrategy { - constructor() { - super(); - this.interpolationEffect = new InterpolationEffect(); - this.flotationEffect = new FlotationEffect(); - this.expansionEffect = new ExpansionEffect(); - } - - apply(args) { - const { animations: { interpolation } } = args.config; - - this.interpolationEffect.apply(args); - this.flotationEffect.apply(args); - - if (args.time >= interpolation.duration) { - this.expansionEffect.apply(args); - } - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/update.js deleted file mode 100644 index 18e09e8..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position/update.js +++ /dev/null @@ -1,11 +0,0 @@ -import { Vector3 } from 'three'; -import { EffectManager } from '../../../../../logic/effects/manager'; -import { UpdateStrategy } from '../../../../../logic/updates/strategy'; - -export class PositionUpdate extends UpdateStrategy { - apply({ object, positions, i, ...args }) { - const final = new Vector3(); - EffectManager.addEffect("position", { positions, object, i, final, ...args }); - positions.set(final.toArray(), i * 3); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/resize/resize.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/resize/resize.js deleted file mode 100644 index 93f3b52..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/resize/resize.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Resize handler strategy for particle system - */ - -import { HandlerStrategy } from '../../../../../logic/handlers/strategy'; -import { SetupManager } from '../../../../../logic/setups/manager'; - -export class ResizeHandler extends HandlerStrategy { - /** - * Handle window resize events - * @param {Event} event - Window resize event - * @param {Object} args - Particle system arguments - */ - handle(event, args) { - const { particles } = args.refs; - SetupManager.setupObject("camera", args); - const { particle } = args.config; - const ratio = window.innerWidth / window.innerHeight; - const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); - particles.current.material.size = size; - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/index.js b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/index.js new file mode 100644 index 0000000..c514824 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/index.js @@ -0,0 +1 @@ +export { ResizeHandler } from './resize'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js new file mode 100644 index 0000000..eda995f --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js @@ -0,0 +1,21 @@ +/** + * Resize handler strategy for particle system + */ + +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; +import { ParticlesModelManager } from '../../../../logic/manager'; + +export class ResizeHandler extends ModelStrategy { + /** + * Execute window resize events + * @param {Object} args - Arguments containing event and particle system data + */ + execute({ event, ...args }) { + const { particles } = args.refs; + ParticlesModelManager.execute('setupObject', "camera", args); + const { particle } = args.config; + const ratio = window.innerWidth / window.innerHeight; + const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); + particles.current.material.size = size; + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/index.js b/src/frontend/ui/models/Particles/components/atoms/Particles/index.js new file mode 100644 index 0000000..eae883e --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/index.js @@ -0,0 +1,3 @@ +export * from './effects'; +export * from './handlers'; +export * from './logic'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/index.js b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/index.js new file mode 100644 index 0000000..b9ee04a5 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/index.js @@ -0,0 +1,2 @@ +export { ParticlesSetup } from './setup'; +export { ParticlesUpdate } from './update'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js index 2fb3f56..afbe6e3 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js @@ -1,20 +1,25 @@ +/** + * Particles setup strategy for particle system + */ + import { Float32BufferAttribute } from 'three'; -import { SetupStrategy } from '../../../../logic/setups/strategy'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; import { getImageData } from '../../../../logic'; - -export class ParticlesSetup extends SetupStrategy { - apply({ config, data: { color, unit }, objects: { image }, refs }) { +export class ParticlesSetup extends ModelStrategy { + /** + * Execute particles setup + * @param {Object} args - Arguments containing config, data, objects, and refs + */ + execute({ config, data: { color, unit }, objects: { image }, refs }) { const { particle } = config; const particles = refs.particles.current; const { data } = getImageData({ data: { unit }, objects: { image } }); particles.data = { - label: "particles", count: 0, chaotic: [], - color, colors: [], positions: { ideal: [], initial: [], offsets: [] }, }; diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js index 42ddb81..4464974 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js @@ -1,41 +1,17 @@ -import { UpdateManager } from '../../../../logic/updates/manager'; -import { UpdateStrategy } from '../../../../logic/updates/strategy'; +import { ParticlesModelManager } from '../../../../logic/manager'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class ParticlesUpdate extends UpdateStrategy { - apply({ config, objects, refs: { mouse, particles }, ...args }) { - const { clock } = objects; - const { animations: { interpolation } } = config; - - const object = particles.current; - const time = clock.current.getElapsedTime(); - const intersects = objects.raycaster.intersectObject(object); +export class ParticlesUpdate extends ModelStrategy { + execute(args) { + const intersects = args.objects.raycaster.intersectObject(args.refs.particles.current); const idxs = new Set(intersects.map(({ index }) => index)); - const positions = object.geometry.attributes.position.array; - for (let i = 0; i < object.data.count; i++) { - if (time >= interpolation.duration) { - UpdateManager.updateAttribute("chaos", { - config, - i, - idxs, - mouse, - particles: object, - ...args - }); - } - UpdateManager.updateAttribute("position", { - config, - i, - idxs, - object, - positions, - particles, - time, - ...args - }); + for (let i = 0; i < args.refs.particles.current.data.count; i++) { + ParticlesModelManager.execute('updateAttribute', "color", { i, ...args }); + ParticlesModelManager.execute('updateAttribute', "position", { i, idxs, ...args }); } - object.geometry.attributes.color.needsUpdate = true; - object.geometry.attributes.position.needsUpdate = true; + args.refs.particles.current.geometry.attributes.color.needsUpdate = true; + args.refs.particles.current.geometry.attributes.position.needsUpdate = true; } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx index 9fff14d..a5b358c 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx @@ -1,3 +1,8 @@ +//* Imports +import { PlaneSetup } from './logic/setup'; + +export { PlaneSetup }; + //* Main export default function Plane({ config, data, refs }) { // Props diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js index 68cb95d..d57c525 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js +++ b/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js @@ -1,8 +1,16 @@ +/** + * Plane setup strategy for particle system + */ + import { Plane, Vector3 } from 'three'; -import { SetupStrategy } from '../../../../logic/setups/strategy'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class PlaneSetup extends SetupStrategy { - apply({ data: { unit }, refs: { plane } }) { +export class PlaneSetup extends ModelStrategy { + /** + * Execute plane setup + * @param {Object} args - Arguments containing data and refs + */ + execute({ data: { unit }, refs: { plane } }) { const normal = new Vector3(0, 0, 1); plane.current = new Plane(normal, unit / 2); } diff --git a/src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx b/src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx index 20938db..361a4bf 100644 --- a/src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx @@ -3,6 +3,11 @@ * Atom component for the ray line in the Particles model */ +//* Imports +import { LineUpdate } from './logic/update'; + +export { LineUpdate }; + //* Main export default function RayLine({ config, refs }) { // Props diff --git a/src/frontend/ui/models/Particles/components/atoms/RayLine/logic/line.js b/src/frontend/ui/models/Particles/components/atoms/RayLine/logic/update.js similarity index 62% rename from src/frontend/ui/models/Particles/components/atoms/RayLine/logic/line.js rename to src/frontend/ui/models/Particles/components/atoms/RayLine/logic/update.js index bf8de0a..a6dbc36 100644 --- a/src/frontend/ui/models/Particles/components/atoms/RayLine/logic/line.js +++ b/src/frontend/ui/models/Particles/components/atoms/RayLine/logic/update.js @@ -1,8 +1,8 @@ import { BufferGeometry } from 'three'; -import { UpdateStrategy } from '../../../../logic/updates/strategy'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class LineUpdate extends UpdateStrategy { - apply({ objects, refs, target }) { +export class LineUpdate extends ModelStrategy { + execute({ objects, refs, target }) { const { origin } = objects.raycaster.ray; const points = [origin, target]; const geometry = new BufferGeometry().setFromPoints(points); diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.js new file mode 100644 index 0000000..d726742 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.js @@ -0,0 +1,2 @@ +export { RaycasterSetup } from './logic/setup'; +export { RaycasterUpdate } from './logic/update'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js index f3729ae..012ef54 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js @@ -1,7 +1,15 @@ -import { SetupStrategy } from '../../../../logic/setups/strategy'; +/** + * Raycaster setup strategy for particle system + */ -export class RaycasterSetup extends SetupStrategy { - apply({ config, data: { unit }, objects: { raycaster } }) { +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; + +export class RaycasterSetup extends ModelStrategy { + /** + * Execute raycaster setup + * @param {Object} args - Arguments containing config, data, and objects + */ + execute({ config, data: { unit }, objects: { raycaster } }) { const { animations: { chaos: { radius } } } = config; raycaster.params.Points.threshold = radius * unit; } diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js index 092dd25..237c8a6 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js @@ -1,8 +1,8 @@ import { Vector2 } from 'three'; -import { UpdateStrategy } from '../../../../logic/updates/strategy'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class RaycasterUpdate extends UpdateStrategy { - apply({ objects, refs }) { +export class RaycasterUpdate extends ModelStrategy { + execute({ objects, refs }) { const { raycaster } = objects; const camera = refs.camera.current; const mouse = refs.mouse.current; diff --git a/src/frontend/ui/models/Particles/components/atoms/index.js b/src/frontend/ui/models/Particles/components/atoms/index.js new file mode 100644 index 0000000..7227091 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/index.js @@ -0,0 +1,9 @@ +export * from './Camera'; +export * from './Circle'; +export * from './Mouse'; +export * from './Plane'; +export * from './Particles/effects'; +export * from './Particles/handlers'; +export * from './Particles/logic'; +export * from './Raycaster'; +export * from './RayLine'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/Controls/index.jsx b/src/frontend/ui/models/Particles/components/molecules/Controls/index.jsx new file mode 100644 index 0000000..a0e2e3f --- /dev/null +++ b/src/frontend/ui/models/Particles/components/molecules/Controls/index.jsx @@ -0,0 +1,22 @@ +//* Imports +import { OrbitControls } from "@react-three/drei"; +import Box from "../../atoms/Box"; +import Circle from "../../atoms/Circle"; +import Plane from "../../atoms/Plane"; +import RayLine from "../../atoms/RayLine"; +import Camera from "../../atoms/Camera"; + +//* Main +export default function Controls(args) { + // Props + const { general: { showHelpers } } = args.config; + // Return + return (<> + + + + + + {showHelpers && } + ); +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticleSystem.jsx b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx similarity index 53% rename from src/frontend/ui/models/Particles/components/molecules/ParticleSystem.jsx rename to src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx index 25f0389..7b0f9c6 100644 --- a/src/frontend/ui/models/Particles/components/molecules/ParticleSystem.jsx +++ b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx @@ -6,31 +6,29 @@ //* Imports import { useEffect } from "react"; import { useFrame } from "@react-three/fiber"; -import Particles from "../atoms/Particles"; +import Particles from "../../atoms/Particles/index.jsx"; import { setupObjects, updateObjects, } from "@semantyk/frontend/ui/models/Particles/logic"; -import { SetupManager } from "../../logic/setups/manager"; -import { ListenerManager } from "../../logic/listeners/manager"; -import { HandlerManager } from "../../logic/handlers/manager"; +import { ParticlesModelManager } from "../../../logic/manager.js"; //* Main -export default function ParticleSystem(args) { +export default function ParticlesSystem(args) { // Logic useEffect(() => { setupObjects(args); const handleMouseMove = (event) => { - HandlerManager.handleMouseMove(event, args); + ParticlesModelManager.execute('handleEvent', 'mouse', event, args); }; const handleResize = (event) => { - HandlerManager.handleResize(event, args); + ParticlesModelManager.execute('handleEvent', 'resize', event, args); }; - ListenerManager.addEventListeners({ handleMouseMove, handleResize }); - return () => ListenerManager.removeEventListeners({ handleMouseMove, handleResize }); + ParticlesModelManager.execute('addEventListeners', { handleMouseMove, handleResize }); + return () => ParticlesModelManager.execute('removeEventListeners', { handleMouseMove, handleResize }); }, [args]); useFrame(({ clock }) => { diff --git a/src/frontend/ui/models/Particles/components/organisms/ParticlesScene.jsx b/src/frontend/ui/models/Particles/components/organisms/ParticlesScene.jsx deleted file mode 100644 index 0b70415..0000000 --- a/src/frontend/ui/models/Particles/components/organisms/ParticlesScene.jsx +++ /dev/null @@ -1,44 +0,0 @@ -/** - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - * # `ParticlesScene.jsx` - * @organization: Semantyk - * @project: Client - * - * @file: This file contains the logic for the ParticlesScene component. - * - * @created: Mar 13, 2025 - * @modified: Mar 13, 2025 - * - * @author: Semantyk Team - * @maintainer: Daniel Bakas - * - * @copyright: Semantyk © 2025. All rights reserved. - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - */ - -//* Imports -import { useArgs } from "@semantyk/frontend/ui/models/Particles/hooks/useArgs"; -import { config } from "@semantyk/frontend/ui/models/Particles/config"; -import Camera from "../atoms/Camera"; -import Controls from "../atoms/Controls"; -import Box from "../atoms/Box"; -import Circle from "../atoms/Circle"; -import ParticleSystem from "../molecules/ParticleSystem"; -import Plane from "../atoms/Plane"; -import RayLine from "../atoms/RayLine"; - -//* Main -export default function ParticlesScene() { - // Hooks - const args = useArgs(); - // Return - return (<> - - - - - - - - ); -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/config/index.js b/src/frontend/ui/models/Particles/config/index.js index 51d778e..3842e7d 100644 --- a/src/frontend/ui/models/Particles/config/index.js +++ b/src/frontend/ui/models/Particles/config/index.js @@ -20,7 +20,7 @@ export const config = { // General general: { - showHelpers: true, + showHelpers: false, scale: 1, size: 150, }, diff --git a/src/frontend/ui/models/Particles/hooks/useArgs.jsx b/src/frontend/ui/models/Particles/hooks/useArgs.jsx index ca95f33..5f1b224 100644 --- a/src/frontend/ui/models/Particles/hooks/useArgs.jsx +++ b/src/frontend/ui/models/Particles/hooks/useArgs.jsx @@ -52,8 +52,8 @@ export function useArgs() { box: useRef(), camera: useRef(), circle: useRef(), - mouse: useRef({ x: 0, y: 0 }), - moveMouseTimeout: useRef(), + mouse: useRef({ current: { x: 0, y: 0, isMoving: false } }), + moveMouseTimeout: useRef(null), particles: useRef(), plane: useRef(), rayLine: useRef(), diff --git a/src/frontend/ui/models/Particles/index.jsx b/src/frontend/ui/models/Particles/index.jsx index e32ae97..b1427fe 100644 --- a/src/frontend/ui/models/Particles/index.jsx +++ b/src/frontend/ui/models/Particles/index.jsx @@ -1,13 +1,13 @@ /** * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - * # `index.jsx` | `ParticlesModel` + * # `ParticlesScene.jsx` * @organization: Semantyk * @project: Client * - * @file: This file contains the main entry point for the Particles model. + * @file: This file contains the logic for the ParticlesScene component. * - * @created: Sep 12, 2024 - * @modified: Mar 12, 2025 + * @created: Mar 13, 2025 + * @modified: Mar 13, 2025 * * @author: Semantyk Team * @maintainer: Daniel Bakas @@ -16,8 +16,19 @@ * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ -import ParticlesScene from "./components/organisms/ParticlesScene"; +//* Imports +import { useArgs } from "@semantyk/frontend/ui/models/Particles/hooks/useArgs"; +import Controls from "./components/molecules/Controls"; +import ParticlesSystem from "./components/molecules/ParticlesSystem"; +import Camera from "./components/atoms/Camera"; +//* Main export default function ParticlesModel() { - return ; + // Hooks + const args = useArgs(); + // Return + return (<> + + + ); } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/effects/manager.js b/src/frontend/ui/models/Particles/logic/effects/manager.js deleted file mode 100644 index 47437a9..0000000 --- a/src/frontend/ui/models/Particles/logic/effects/manager.js +++ /dev/null @@ -1,31 +0,0 @@ -import { ChaosEffect } from '../../components/atoms/Particles/effects/chaos/effect'; -import { ColorEffect } from '../../components/atoms/Particles/effects/color/effect'; -import { PositionEffect } from '../../components/atoms/Particles/effects/position/effect'; - -/** - * Manager class that handles all particle effects using the Strategy pattern - */ -export class EffectManager { - static instance = new EffectManager(); - - constructor() { - this.strategies = { - chaos: new ChaosEffect(), - color: new ColorEffect(), - position: new PositionEffect() - }; - } - - addEffect(type, args) { - const strategy = this.strategies[type]; - if (strategy) { - strategy.apply(args); - } - } - - static addEffect(type, args) { - return EffectManager.instance.addEffect(type, args); - } -} - -export const { addEffect } = EffectManager; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/effects/strategy.js b/src/frontend/ui/models/Particles/logic/effects/strategy.js deleted file mode 100644 index a283135..0000000 --- a/src/frontend/ui/models/Particles/logic/effects/strategy.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Base class for particle effects following the Strategy Pattern - */ -export class EffectStrategy { - /** - * Apply the effect - * @param {Object} args - Effect arguments - * @throws {Error} Must be implemented by child classes - */ - apply(args) { - throw new Error('Effect strategy must implement apply method'); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/handlers/manager.js b/src/frontend/ui/models/Particles/logic/handlers/manager.js deleted file mode 100644 index 487fcf2..0000000 --- a/src/frontend/ui/models/Particles/logic/handlers/manager.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Manager for particle system event handlers - */ - -import { MouseHandler } from '../../components/atoms/Mouse/logic/handler'; -import { ResizeHandler } from '../../components/atoms/Particles/effects/resize/resize'; - -export class HandlerManager { - static #instance = null; - #mouseHandler = new MouseHandler(); - #resizeHandler = new ResizeHandler(); - - /** - * Get the singleton instance - * @returns {HandlerManager} - */ - static getInstance() { - if (!HandlerManager.#instance) { - HandlerManager.#instance = new HandlerManager(); - } - return HandlerManager.#instance; - } - - /** - * Handle mouse/touch move event - * @param {MouseEvent|TouchEvent} event - Mouse or touch event - * @param {Object} args - Particle system arguments - */ - handleMouseMove(event, args) { - this.#mouseHandler.handle(event, args); - } - - /** - * Handle window resize event - * @param {Event} event - Window resize event - * @param {Object} args - Particle system arguments - */ - handleResize(event, args) { - this.#resizeHandler.handle(event, args); - } - - /** - * Static method to handle mouse/touch move event - * @param {MouseEvent|TouchEvent} event - Mouse or touch event - * @param {Object} args - Particle system arguments - */ - static handleMouseMove(event, args) { - HandlerManager.getInstance().handleMouseMove(event, args); - } - - /** - * Static method to handle window resize event - * @param {Event} event - Window resize event - * @param {Object} args - Particle system arguments - */ - static handleResize(event, args) { - HandlerManager.getInstance().handleResize(event, args); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/handlers/strategy.js b/src/frontend/ui/models/Particles/logic/handlers/strategy.js deleted file mode 100644 index 1eafcd2..0000000 --- a/src/frontend/ui/models/Particles/logic/handlers/strategy.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Base class for particle event handlers following the Strategy Pattern - */ -export class HandlerStrategy { - /** - * Handle the event - * @param {Object} event - Event object - * @param {Object} args - Additional arguments - * @throws {Error} Must be implemented by child classes - */ - handle(event, args) { - throw new Error('Handler strategy must implement handle method'); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/index.js b/src/frontend/ui/models/Particles/logic/index.js index bf21a74..ed52cba 100644 --- a/src/frontend/ui/models/Particles/logic/index.js +++ b/src/frontend/ui/models/Particles/logic/index.js @@ -17,8 +17,7 @@ //* Imports import { Vector3 } from "three"; //* Local Imports -import { UpdateManager } from "./updates/manager"; -import { SetupManager } from "./setups/manager"; +import { ParticlesModelManager } from "./manager"; //* Main export function getImageData(args) { @@ -41,21 +40,21 @@ export function ease(time, duration) { } export function setupObjects(args) { - SetupManager.setupObject("camera", args); - SetupManager.setupObject("plane", args); - SetupManager.setupObject("particles", args); - SetupManager.setupObject("raycaster", args); + ParticlesModelManager.execute('setupObject', "camera", args); + ParticlesModelManager.execute('setupObject', "plane", args); + ParticlesModelManager.execute('setupObject', "particles", args); + ParticlesModelManager.execute('setupObject', "raycaster", args); } export function updateObjects(args) { - UpdateManager.updateObject("particles", args); + ParticlesModelManager.execute('updateObject', "particles", args); } export function updateOnMouseMove(args) { const target = new Vector3(); - UpdateManager.updateObject("camera", args); - UpdateManager.updateObject("raycaster", args); - UpdateManager.updateObject("mouse", args); - UpdateManager.updateObject("circle", { target, ...args }); - UpdateManager.updateObject("line", { target, ...args }); + ParticlesModelManager.execute('updateObject', "camera", args); + ParticlesModelManager.execute('updateObject', "raycaster", args); + ParticlesModelManager.execute('updateObject', "mouse", args); + ParticlesModelManager.execute('updateObject', "circle", { target, ...args }); + ParticlesModelManager.execute('updateObject', "line", { target, ...args }); } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/listeners/manager.js b/src/frontend/ui/models/Particles/logic/listeners/manager.js deleted file mode 100644 index 4faf509..0000000 --- a/src/frontend/ui/models/Particles/logic/listeners/manager.js +++ /dev/null @@ -1,38 +0,0 @@ -import { MouseListener } from '../../components/atoms/Mouse/logic/listener'; -import { ResizeListener } from './resize'; - -/** - * Manager class that handles all particle event listeners using the Strategy pattern - */ -export class ListenerManager { - static instance = new ListenerManager(); - - constructor() { - this.strategies = { - mouse: new MouseListener(), - resize: new ResizeListener(), - }; - } - - addEventListeners(args) { - Object.values(this.strategies).forEach(strategy => { - strategy.add(args); - }); - } - - removeEventListeners(args) { - Object.values(this.strategies).forEach(strategy => { - strategy.remove(args); - }); - } - - static addEventListeners(args) { - return ListenerManager.instance.addEventListeners(args); - } - - static removeEventListeners(args) { - return ListenerManager.instance.removeEventListeners(args); - } -} - -export const { addEventListeners, removeEventListeners } = ListenerManager; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/listeners/resize.js b/src/frontend/ui/models/Particles/logic/listeners/resize.js index 9297517..2825069 100644 --- a/src/frontend/ui/models/Particles/logic/listeners/resize.js +++ b/src/frontend/ui/models/Particles/logic/listeners/resize.js @@ -1,6 +1,6 @@ -import { ListenerStrategy } from './strategy'; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class ResizeListener extends ListenerStrategy { +export class ResizeListener extends ModelStrategy { add({ handleResize }) { window.addEventListener("resize", handleResize); } diff --git a/src/frontend/ui/models/Particles/logic/listeners/strategy.js b/src/frontend/ui/models/Particles/logic/listeners/strategy.js deleted file mode 100644 index f7b4f8c..0000000 --- a/src/frontend/ui/models/Particles/logic/listeners/strategy.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Base class for particle event listeners following the Strategy Pattern - */ -export class ListenerStrategy { - /** - * Add the event listener - * @param {Object} args - Event arguments - * @throws {Error} Must be implemented by child classes - */ - add(args) { - throw new Error('Listener strategy must implement add method'); - } - - /** - * Remove the event listener - * @param {Object} args - Event arguments - * @throws {Error} Must be implemented by child classes - */ - remove(args) { - throw new Error('Listener strategy must implement remove method'); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/manager.js b/src/frontend/ui/models/Particles/logic/manager.js new file mode 100644 index 0000000..0995610 --- /dev/null +++ b/src/frontend/ui/models/Particles/logic/manager.js @@ -0,0 +1,88 @@ +import { ModelManager } from '@semantyk/frontend/ui/components/molecules/Model/logic/manager'; +import { ResizeListener } from './listeners/resize'; +import { CameraSetup } from '../components/atoms/Camera/logic/setup'; +import { CircleUpdate } from '../components/atoms/Circle/logic/update'; +import { MouseHandler, MouseListener, MouseUpdate } from '../components/atoms/Mouse'; +import { PlaneSetup } from '../components/atoms/Plane/logic/setup'; +import { RaycasterSetup, RaycasterUpdate } from '../components/atoms/Raycaster'; +import { LineUpdate } from '../components/atoms/RayLine/logic/update'; +import { ChaosEffect } from '../components/atoms/Particles/effects/chaos'; +import { ColorEffect } from '../components/atoms/Particles/effects/color'; +import { EntropyEffect } from '../components/atoms/Particles/effects/entropy'; +import { PositionEffect } from '../components/atoms/Particles/effects/position'; +import { ResizeHandler } from '../components/atoms/Particles/handlers/resize'; +import { ParticlesSetup } from '../components/atoms/Particles/logic/setup'; +import { ParticlesUpdate } from '../components/atoms/Particles/logic/update'; + +/** + * Manager class for particle strategies using the Strategy pattern + */ +export class ParticlesModelManager extends ModelManager { + static instance = new ParticlesModelManager(); + + static execute(type, method, ...args) { + return this.instance[type](method, ...args); + } + + constructor() { + super(); + this.effects = { + chaos: new ChaosEffect(), + color: new ColorEffect(), + entropy: new EntropyEffect(), + position: new PositionEffect() + }; + + this.handlers = { + mouse: new MouseHandler(), + resize: new ResizeHandler() + }; + + this.setups = { + camera: new CameraSetup(), + particles: new ParticlesSetup(), + plane: new PlaneSetup(), + raycaster: new RaycasterSetup() + }; + + this.listeners = { + mouse: new MouseListener(), + resize: new ResizeListener() + }; + + this.updates = { + objects: { + circle: new CircleUpdate(), + line: new LineUpdate(), + mouse: new MouseUpdate(), + particles: new ParticlesUpdate(), + raycaster: new RaycasterUpdate(), + }, + attributes: { + color: this.effects.color, + position: this.effects.position + } + }; + } + + // Instance methods + addEffect(type, args) { + return this.execute(this.effects, type, args); + } + + handleEvent(type, event, args) { + return this.execute(this.handlers, type, { event, ...args }); + } + + setupObject(type, args) { + return this.execute(this.setups, type, args); + } + + updateObject(type, args) { + return this.execute(this.updates.objects, type, args); + } + + updateAttribute(type, args) { + return this.execute(this.updates.attributes, type, args); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/setups/manager.js b/src/frontend/ui/models/Particles/logic/setups/manager.js deleted file mode 100644 index 1e4c8e4..0000000 --- a/src/frontend/ui/models/Particles/logic/setups/manager.js +++ /dev/null @@ -1,33 +0,0 @@ -import { CameraSetup } from '../../components/atoms/Camera/logic/setup'; -import { ParticlesSetup } from '../../components/atoms/Particles/logic/setup'; -import { PlaneSetup } from '../../components/atoms/Plane/logic/setup'; -import { RaycasterSetup } from '../../components/atoms/Raycaster/logic/setup'; - -/** - * Manager class that handles all particle setups using the Strategy pattern - */ -export class SetupManager { - static instance = new SetupManager(); - - constructor() { - this.strategies = { - camera: new CameraSetup(), - particles: new ParticlesSetup(), - plane: new PlaneSetup(), - raycaster: new RaycasterSetup(), - }; - } - - setupObject(type, args) { - const strategy = this.strategies[type]; - if (strategy) { - strategy.apply(args); - } - } - - static setupObject(type, args) { - return SetupManager.instance.setupObject(type, args); - } -} - -export const { setupObject } = SetupManager; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/setups/strategy.js b/src/frontend/ui/models/Particles/logic/setups/strategy.js deleted file mode 100644 index 8a620b1..0000000 --- a/src/frontend/ui/models/Particles/logic/setups/strategy.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Base class for particle setups following the Strategy Pattern - */ -export class SetupStrategy { - /** - * Apply the setup - * @param {Object} args - Setup arguments - * @throws {Error} Must be implemented by child classes - */ - apply(args) { - throw new Error('Setup strategy must implement apply method'); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/updates/manager.js b/src/frontend/ui/models/Particles/logic/updates/manager.js deleted file mode 100644 index 5a9ec9e..0000000 --- a/src/frontend/ui/models/Particles/logic/updates/manager.js +++ /dev/null @@ -1,55 +0,0 @@ -import { CircleUpdate } from '../../components/atoms/Circle/logic/update'; -import { LineUpdate } from '../../components/atoms/RayLine/logic/line'; -import { MouseUpdate } from '../../components/atoms/Mouse/logic/update'; -import { ParticlesUpdate } from '../../components/atoms/Particles/logic/update'; -import { RaycasterUpdate } from '../../components/atoms/Raycaster/logic/update'; -import { ChaosUpdate } from '../../components/atoms/Particles/effects/chaos/update'; -import { ColorUpdate } from '../../components/atoms/Particles/effects/color/update'; -import { PositionUpdate } from '../../components/atoms/Particles/effects/position/update'; - -/** - * Manager class that handles all particle updates using the Strategy pattern - */ -export class UpdateManager { - static instance = new UpdateManager(); - - constructor() { - this.objectStrategies = { - circle: new CircleUpdate(), - line: new LineUpdate(), - mouse: new MouseUpdate(), - particles: new ParticlesUpdate(), - raycaster: new RaycasterUpdate(), - }; - - this.attributeStrategies = { - chaos: new ChaosUpdate(), - color: new ColorUpdate(), - position: new PositionUpdate() - }; - } - - updateObject(type, args) { - const strategy = this.objectStrategies[type]; - if (strategy) { - strategy.apply(args); - } - } - - updateAttribute(type, args) { - const strategy = this.attributeStrategies[type]; - if (strategy) { - strategy.apply(args); - } - } - - static updateObject(type, args) { - return UpdateManager.instance.updateObject(type, args); - } - - static updateAttribute(type, args) { - return UpdateManager.instance.updateAttribute(type, args); - } -} - -export const { updateObject, updateAttribute } = UpdateManager; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/updates/strategy.js b/src/frontend/ui/models/Particles/logic/updates/strategy.js deleted file mode 100644 index 02695a1..0000000 --- a/src/frontend/ui/models/Particles/logic/updates/strategy.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Base class for particle updates following the Strategy Pattern - */ -export class UpdateStrategy { - /** - * Apply the update - * @param {Object} args - Update arguments - * @throws {Error} Must be implemented by child classes - */ - apply(args) { - throw new Error('Update strategy must implement apply method'); - } -} \ No newline at end of file From c20645c273e46490bbe29b073cfd94326820e706 Mon Sep 17 00:00:00 2001 From: "Daniel B." Date: Thu, 13 Mar 2025 19:13:43 -0600 Subject: [PATCH 3/7] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Refactor:=20Update=20P?= =?UTF-8?q?articles=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Simplify method calling --- .../molecules/Model/logic/manager.js | 45 ++++-------- .../components/atoms/Mouse/updates/color.js | 4 +- .../atoms/Mouse/updates/position.js | 2 +- .../atoms/Particles/handlers/resize.js | 4 +- .../atoms/Particles/logic/update.js | 6 +- .../molecules/ParticlesSystem/index.jsx | 18 ++--- .../ui/models/Particles/logic/index.js | 25 ++++--- .../ui/models/Particles/logic/manager.js | 69 +++++++++---------- 8 files changed, 72 insertions(+), 101 deletions(-) diff --git a/src/frontend/ui/components/molecules/Model/logic/manager.js b/src/frontend/ui/components/molecules/Model/logic/manager.js index aa03b56..55de217 100644 --- a/src/frontend/ui/components/molecules/Model/logic/manager.js +++ b/src/frontend/ui/components/molecules/Model/logic/manager.js @@ -9,48 +9,27 @@ export class ModelManager { } // Generic method to handle operations on collections - execute(collection, type, args) { - const item = collection[type]; + execute(collection, key, args) { + const item = collection[key]; if (item) { return item.execute(args); } } + // TODO: Add a method for `add` and `remove` + // Event listener methods - addEventListeners(args) { - Object.values(this.listeners).forEach(listener => { - listener.add(args); + addAll(collectionName, args) { + const collection = this[collectionName]; + Object.values(collection).forEach(item => { + item.add(args); }); } - removeEventListeners(args) { - Object.values(this.listeners).forEach(listener => { - listener.remove(args); + removeAll(collectionName, args) { + const collection = this[collectionName]; + Object.values(collection).forEach(item => { + item.remove(args); }); } - - // Static helper method that automatically uses the correct instance - static execute(methodName, type, ...args) { - const instance = this.instance; - if (!instance) { - throw new Error(`No instance found for ${this.name}. Make sure to initialize the static instance property.`); - } - - switch (methodName) { - case 'addEffect': - return instance.addEffect(type, args[0]); - case 'handleEvent': - return instance.handleEvent(type, args[0], args[1]); - case 'setupObject': - return instance.setupObject(type, args[0]); - case 'updateObject': - return instance.updateObject(type, args[0]); - case 'addEventListeners': - return instance.addEventListeners(type); - case 'removeEventListeners': - return instance.removeEventListeners(type); - default: - throw new Error(`Unknown method ${methodName}`); - } - } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js index 6623815..5528fc4 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js @@ -1,11 +1,11 @@ import { Color } from 'three'; -import { ParticleManager } from '../../../../../logic/manager'; import { ParticleStrategy } from '../../../../../logic/strategy'; +import { ParticlesManager } from '../../../../logic/manager'; export class ColorUpdate extends ParticleStrategy { apply({ i, colors, particles, ...args }) { const final = new Color(); - ParticleManager.addEffect("color", { colors, particles, i, final, ...args }); + ParticlesManager.add("effects", "color", { colors, particles, i, final, ...args }); colors.set(final.toArray(), i * 3); } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js index b643b60..073b937 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js @@ -5,7 +5,7 @@ import { ParticleStrategy } from '../../../../../logic/strategy'; export class PositionUpdate extends ParticleStrategy { apply({ object, positions, i, ...args }) { const final = new Vector3(); - ParticleManager.addEffect("position", { positions, object, i, final, ...args }); + ParticleManager.add("effects", "position", { positions, object, i, final, ...args }); positions.set(final.toArray(), i * 3); } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js index eda995f..988cb52 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js @@ -3,7 +3,7 @@ */ import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -import { ParticlesModelManager } from '../../../../logic/manager'; +import { ParticlesManager } from '../../../../logic/manager'; export class ResizeHandler extends ModelStrategy { /** @@ -12,7 +12,7 @@ export class ResizeHandler extends ModelStrategy { */ execute({ event, ...args }) { const { particles } = args.refs; - ParticlesModelManager.execute('setupObject', "camera", args); + ParticlesManager.execute('setupObject', "camera", args); const { particle } = args.config; const ratio = window.innerWidth / window.innerHeight; const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js index 4464974..7c011e2 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js @@ -1,4 +1,4 @@ -import { ParticlesModelManager } from '../../../../logic/manager'; +import { ParticlesManager } from '../../../../logic/manager'; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export class ParticlesUpdate extends ModelStrategy { @@ -7,8 +7,8 @@ export class ParticlesUpdate extends ModelStrategy { const idxs = new Set(intersects.map(({ index }) => index)); for (let i = 0; i < args.refs.particles.current.data.count; i++) { - ParticlesModelManager.execute('updateAttribute', "color", { i, ...args }); - ParticlesModelManager.execute('updateAttribute', "position", { i, idxs, ...args }); + ParticlesManager.add('effects', "color", { i, ...args }); + ParticlesManager.add('effects', "position", { i, idxs, ...args }); } args.refs.particles.current.geometry.attributes.color.needsUpdate = true; diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx index 7b0f9c6..0990e00 100644 --- a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx +++ b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx @@ -8,32 +8,32 @@ import { useEffect } from "react"; import { useFrame } from "@react-three/fiber"; import Particles from "../../atoms/Particles/index.jsx"; import { - setupObjects, - updateObjects, + setup, + update, } from "@semantyk/frontend/ui/models/Particles/logic"; -import { ParticlesModelManager } from "../../../logic/manager.js"; +import { ParticlesManager } from "../../../logic/manager.js"; //* Main export default function ParticlesSystem(args) { // Logic useEffect(() => { - setupObjects(args); + setup(args); const handleMouseMove = (event) => { - ParticlesModelManager.execute('handleEvent', 'mouse', event, args); + ParticlesManager.handle('mouseMove', { event, ...args }); }; const handleResize = (event) => { - ParticlesModelManager.execute('handleEvent', 'resize', event, args); + ParticlesManager.handle('resize', { event, ...args }); }; - ParticlesModelManager.execute('addEventListeners', { handleMouseMove, handleResize }); - return () => ParticlesModelManager.execute('removeEventListeners', { handleMouseMove, handleResize }); + ParticlesManager.execute('addAll', 'listener', { handleMouseMove, handleResize }); + return () => ParticlesManager.execute('removeAll', 'listener', { handleMouseMove, handleResize }); }, [args]); useFrame(({ clock }) => { args.objects.clock.current = clock; - updateObjects(args); + update(args); }); return ; diff --git a/src/frontend/ui/models/Particles/logic/index.js b/src/frontend/ui/models/Particles/logic/index.js index ed52cba..905e258 100644 --- a/src/frontend/ui/models/Particles/logic/index.js +++ b/src/frontend/ui/models/Particles/logic/index.js @@ -17,7 +17,7 @@ //* Imports import { Vector3 } from "three"; //* Local Imports -import { ParticlesModelManager } from "./manager"; +import { ParticlesManager } from "./manager"; //* Main export function getImageData(args) { @@ -39,22 +39,21 @@ export function ease(time, duration) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; } -export function setupObjects(args) { - ParticlesModelManager.execute('setupObject', "camera", args); - ParticlesModelManager.execute('setupObject', "plane", args); - ParticlesModelManager.execute('setupObject', "particles", args); - ParticlesModelManager.execute('setupObject', "raycaster", args); +export function setup(args) { + ParticlesManager.setup('camera', args); + ParticlesManager.setup('particles', args); + ParticlesManager.setup('plane', args); + ParticlesManager.setup('raycaster', args); } -export function updateObjects(args) { - ParticlesModelManager.execute('updateObject', "particles", args); +export function update(args) { + ParticlesManager.update("particles", args); } export function updateOnMouseMove(args) { const target = new Vector3(); - ParticlesModelManager.execute('updateObject', "camera", args); - ParticlesModelManager.execute('updateObject', "raycaster", args); - ParticlesModelManager.execute('updateObject', "mouse", args); - ParticlesModelManager.execute('updateObject', "circle", { target, ...args }); - ParticlesModelManager.execute('updateObject', "line", { target, ...args }); + ParticlesManager.update("circle", { target, ...args }); + ParticlesManager.update("line", { target, ...args }); + ParticlesManager.update("mouse", args); + ParticlesManager.update("raycaster", args); } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/manager.js b/src/frontend/ui/models/Particles/logic/manager.js index 0995610..fa44f2b 100644 --- a/src/frontend/ui/models/Particles/logic/manager.js +++ b/src/frontend/ui/models/Particles/logic/manager.js @@ -17,11 +17,31 @@ import { ParticlesUpdate } from '../components/atoms/Particles/logic/update'; /** * Manager class for particle strategies using the Strategy pattern */ -export class ParticlesModelManager extends ModelManager { - static instance = new ParticlesModelManager(); +export class ParticlesManager extends ModelManager { + static instance = new ParticlesManager(); - static execute(type, method, ...args) { - return this.instance[type](method, ...args); + static execute(method, item, ...args) { + return this.instance[method](item, ...args); + } + + static add(collectionName, item, args) { + const collection = this.instance[collectionName]; + return this.instance.execute(collection, item, args); + } + + static handle(item, args) { + const collection = this.instance.handlers; + return this.instance.execute(collection, item, args); + } + + static setup(item, args) { + const collection = this.instance.setups; + return this.instance.execute(collection, item, args); + } + + static update(item, args) { + const collection = this.instance.updates; + return this.instance.execute(collection, item, args); } constructor() { @@ -34,7 +54,7 @@ export class ParticlesModelManager extends ModelManager { }; this.handlers = { - mouse: new MouseHandler(), + mouseMove: new MouseHandler(), resize: new ResizeHandler() }; @@ -45,44 +65,17 @@ export class ParticlesModelManager extends ModelManager { raycaster: new RaycasterSetup() }; - this.listeners = { + this.listener = { mouse: new MouseListener(), resize: new ResizeListener() }; this.updates = { - objects: { - circle: new CircleUpdate(), - line: new LineUpdate(), - mouse: new MouseUpdate(), - particles: new ParticlesUpdate(), - raycaster: new RaycasterUpdate(), - }, - attributes: { - color: this.effects.color, - position: this.effects.position - } + circle: new CircleUpdate(), + line: new LineUpdate(), + mouse: new MouseUpdate(), + particles: new ParticlesUpdate(), + raycaster: new RaycasterUpdate(), }; } - - // Instance methods - addEffect(type, args) { - return this.execute(this.effects, type, args); - } - - handleEvent(type, event, args) { - return this.execute(this.handlers, type, { event, ...args }); - } - - setupObject(type, args) { - return this.execute(this.setups, type, args); - } - - updateObject(type, args) { - return this.execute(this.updates.objects, type, args); - } - - updateAttribute(type, args) { - return this.execute(this.updates.attributes, type, args); - } } \ No newline at end of file From 0f5d40c83328aca6b484b91742298684df6c4f92 Mon Sep 17 00:00:00 2001 From: "Daniel B." Date: Fri, 14 Mar 2025 00:21:08 -0600 Subject: [PATCH 4/7] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Refactor:=20Update=20P?= =?UTF-8?q?articles=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Simplify model atomically --- .../molecules/Model/logic/manager.js | 28 ++---- .../components/atoms/Mouse/updates/color.js | 2 +- .../atoms/Particles/handlers/resize.js | 2 +- .../atoms/Particles/logic/update.js | 4 +- .../molecules/ParticlesSystem/index.jsx | 4 +- .../ui/models/Particles/logic/index.js | 2 +- .../ui/models/Particles/logic/manager.js | 94 +++++++++---------- 7 files changed, 63 insertions(+), 73 deletions(-) diff --git a/src/frontend/ui/components/molecules/Model/logic/manager.js b/src/frontend/ui/components/molecules/Model/logic/manager.js index 55de217..07dcdb0 100644 --- a/src/frontend/ui/components/molecules/Model/logic/manager.js +++ b/src/frontend/ui/components/molecules/Model/logic/manager.js @@ -8,28 +8,20 @@ export class ModelManager { } } - // Generic method to handle operations on collections - execute(collection, key, args) { - const item = collection[key]; - if (item) { - return item.execute(args); - } + static call(object, member, ...args) { + if (!object) return; + else object[member](...args); } - // TODO: Add a method for `add` and `remove` - - // Event listener methods - addAll(collectionName, args) { - const collection = this[collectionName]; - Object.values(collection).forEach(item => { - item.add(args); - }); + static execute(collection, item, member, ...args) { + const object = collection[item]; + if (!object) return; + else this.call(object, member, ...args); } - removeAll(collectionName, args) { - const collection = this[collectionName]; - Object.values(collection).forEach(item => { - item.remove(args); + static executeAll(collection, member, ...args) { + Object.values(collection).forEach(object => { + this.call(object, member, ...args); }); } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js index 5528fc4..f343d91 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js @@ -5,7 +5,7 @@ import { ParticlesManager } from '../../../../logic/manager'; export class ColorUpdate extends ParticleStrategy { apply({ i, colors, particles, ...args }) { const final = new Color(); - ParticlesManager.add("effects", "color", { colors, particles, i, final, ...args }); + ParticlesManager.affect("color", { colors, particles, i, final, ...args }); colors.set(final.toArray(), i * 3); } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js index 988cb52..d1b750f 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js @@ -12,7 +12,7 @@ export class ResizeHandler extends ModelStrategy { */ execute({ event, ...args }) { const { particles } = args.refs; - ParticlesManager.execute('setupObject', "camera", args); + ParticlesManager.setup('camera', args); const { particle } = args.config; const ratio = window.innerWidth / window.innerHeight; const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js index 7c011e2..d33249f 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js @@ -7,8 +7,8 @@ export class ParticlesUpdate extends ModelStrategy { const idxs = new Set(intersects.map(({ index }) => index)); for (let i = 0; i < args.refs.particles.current.data.count; i++) { - ParticlesManager.add('effects', "color", { i, ...args }); - ParticlesManager.add('effects', "position", { i, idxs, ...args }); + ParticlesManager.affect('color', { i, ...args }); + ParticlesManager.affect('position', { i, idxs, ...args }); } args.refs.particles.current.geometry.attributes.color.needsUpdate = true; diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx index 0990e00..2a23113 100644 --- a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx +++ b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx @@ -27,8 +27,8 @@ export default function ParticlesSystem(args) { ParticlesManager.handle('resize', { event, ...args }); }; - ParticlesManager.execute('addAll', 'listener', { handleMouseMove, handleResize }); - return () => ParticlesManager.execute('removeAll', 'listener', { handleMouseMove, handleResize }); + ParticlesManager.executeAll('add', { handleMouseMove, handleResize }); + return () => ParticlesManager.executeAll('remove', { handleMouseMove, handleResize }); }, [args]); useFrame(({ clock }) => { diff --git a/src/frontend/ui/models/Particles/logic/index.js b/src/frontend/ui/models/Particles/logic/index.js index 905e258..a7ce015 100644 --- a/src/frontend/ui/models/Particles/logic/index.js +++ b/src/frontend/ui/models/Particles/logic/index.js @@ -51,7 +51,7 @@ export function update(args) { } export function updateOnMouseMove(args) { - const target = new Vector3(); + const target = new Vector3() ParticlesManager.update("circle", { target, ...args }); ParticlesManager.update("line", { target, ...args }); ParticlesManager.update("mouse", args); diff --git a/src/frontend/ui/models/Particles/logic/manager.js b/src/frontend/ui/models/Particles/logic/manager.js index fa44f2b..f2bccf9 100644 --- a/src/frontend/ui/models/Particles/logic/manager.js +++ b/src/frontend/ui/models/Particles/logic/manager.js @@ -20,62 +20,60 @@ import { ParticlesUpdate } from '../components/atoms/Particles/logic/update'; export class ParticlesManager extends ModelManager { static instance = new ParticlesManager(); - static execute(method, item, ...args) { - return this.instance[method](item, ...args); - } + static effects = { + chaos: new ChaosEffect(), + color: new ColorEffect(), + entropy: new EntropyEffect(), + position: new PositionEffect() + }; - static add(collectionName, item, args) { - const collection = this.instance[collectionName]; - return this.instance.execute(collection, item, args); - } + static handlers = { + mouseMove: new MouseHandler(), + resize: new ResizeHandler() + }; - static handle(item, args) { - const collection = this.instance.handlers; - return this.instance.execute(collection, item, args); - } + static setups = { + camera: new CameraSetup(), + particles: new ParticlesSetup(), + plane: new PlaneSetup(), + raycaster: new RaycasterSetup() + }; - static setup(item, args) { - const collection = this.instance.setups; - return this.instance.execute(collection, item, args); - } + static listeners = { + mouse: new MouseListener(), + resize: new ResizeListener() + }; - static update(item, args) { - const collection = this.instance.updates; - return this.instance.execute(collection, item, args); - } + static updates = { + circle: new CircleUpdate(), + line: new LineUpdate(), + mouse: new MouseUpdate(), + particles: new ParticlesUpdate(), + raycaster: new RaycasterUpdate(), + }; - constructor() { - super(); - this.effects = { - chaos: new ChaosEffect(), - color: new ColorEffect(), - entropy: new EntropyEffect(), - position: new PositionEffect() - }; + static affect(item, ...args) { + const collection = this.effects; + return super.execute(collection, item, 'execute', ...args); + } - this.handlers = { - mouseMove: new MouseHandler(), - resize: new ResizeHandler() - }; + static handle(item, ...args) { + const collection = this.handlers; + return super.execute(collection, item, 'execute', ...args); + } - this.setups = { - camera: new CameraSetup(), - particles: new ParticlesSetup(), - plane: new PlaneSetup(), - raycaster: new RaycasterSetup() - }; + static setup(item, ...args) { + const collection = this.setups; + return super.execute(collection, item, 'execute', ...args); + } - this.listener = { - mouse: new MouseListener(), - resize: new ResizeListener() - }; + static update(item, ...args) { + const collection = this.updates; + return super.execute(collection, item, 'execute', ...args); + } - this.updates = { - circle: new CircleUpdate(), - line: new LineUpdate(), - mouse: new MouseUpdate(), - particles: new ParticlesUpdate(), - raycaster: new RaycasterUpdate(), - }; + static executeAll(member, ...args) { + const collection = this.listeners; + return super.executeAll(collection, member, ...args); } } \ No newline at end of file From a2d481550f9398afeaa70b9d034b6f17a12c78c3 Mon Sep 17 00:00:00 2001 From: "Daniel B." Date: Fri, 14 Mar 2025 21:47:19 -0600 Subject: [PATCH 5/7] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Refactor:=20Perform=20?= =?UTF-8?q?major=20refactor=20on=20Particles=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement atomic components architecture --- .../atoms/Box/{index.jsx => Box.jsx} | 4 +- .../Particles/components/atoms/Box/index.js | 2 + .../atoms/Camera/{index.jsx => Camera.jsx} | 13 ++-- .../{logic/setup.js => Camera.logic.js} | 12 +--- .../components/atoms/Camera/index.js | 2 + .../atoms/Circle/{index.jsx => Circle.jsx} | 13 ++-- .../{logic/update.js => Circle.logic.js} | 4 +- .../components/atoms/Circle/index.js | 2 + .../Particles/components/atoms/Mouse/Mouse.js | 7 ++ .../components/atoms/Mouse/Mouse.logic.js | 42 ++++++++++++ .../Particles/components/atoms/Mouse/index.js | 5 +- .../components/atoms/Mouse/logic/handler.js | 34 ---------- .../components/atoms/Mouse/logic/listener.js | 13 ---- .../components/atoms/Mouse/logic/update.js | 10 --- .../Particles/{index.jsx => Particles.jsx} | 7 +- .../{logic/setup.js => Particles.logic.js} | 40 +++++++---- .../atoms/Particles/effects/interpolation.js | 2 +- .../atoms/Particles/handlers/index.js | 1 - .../atoms/Particles/handlers/resize.js | 21 ------ .../components/atoms/Particles/index.js | 4 +- .../components/atoms/Particles/logic/index.js | 2 - .../atoms/Particles/logic/update.js | 17 ----- .../atoms/Plane/{index.jsx => Plane.jsx} | 12 ++-- .../components/atoms/Plane/Plane.logic.js | 9 +++ .../Particles/components/atoms/Plane/index.js | 2 + .../components/atoms/Plane/logic/setup.js | 17 ----- .../atoms/RayLine/{index.jsx => RayLine.jsx} | 12 ++-- .../{logic/update.js => RayLine.logic.js} | 6 +- .../components/atoms/RayLine/index.js | 2 + .../components/atoms/Raycaster/Raycaster.js | 12 ++++ .../{logic/update.js => Raycaster.logic.js} | 11 ++- .../components/atoms/Raycaster/index.js | 4 +- .../components/atoms/Raycaster/index.jsx | 8 --- .../components/atoms/Raycaster/logic/setup.js | 16 ----- .../Particles/components/atoms/index.js | 5 +- .../Controls/{index.jsx => Controls.jsx} | 8 +-- .../components/molecules/Controls/index.js | 2 + .../{index.jsx => ParticlesSystem.jsx} | 14 ++-- .../ParticlesSystem/ParticlesSystem.logic.js | 23 +++++++ .../molecules/ParticlesSystem/index.js | 2 + .../Particles/components/molecules/index.js | 2 + .../ui/models/Particles/config/index.js | 2 +- src/frontend/ui/models/Particles/index.jsx | 4 +- .../ui/models/Particles/logic/index.js | 21 +----- .../Particles/logic/listeners/resize.js | 11 --- .../ui/models/Particles/logic/manager.js | 67 ++++++++----------- .../ui/models/Particles/utils/ease.js | 4 ++ .../ui/models/Particles/utils/image.js | 17 +++++ 48 files changed, 259 insertions(+), 291 deletions(-) rename src/frontend/ui/models/Particles/components/atoms/Box/{index.jsx => Box.jsx} (88%) create mode 100644 src/frontend/ui/models/Particles/components/atoms/Box/index.js rename src/frontend/ui/models/Particles/components/atoms/Camera/{index.jsx => Camera.jsx} (78%) rename src/frontend/ui/models/Particles/components/atoms/Camera/{logic/setup.js => Camera.logic.js} (65%) create mode 100644 src/frontend/ui/models/Particles/components/atoms/Camera/index.js rename src/frontend/ui/models/Particles/components/atoms/Circle/{index.jsx => Circle.jsx} (81%) rename src/frontend/ui/models/Particles/components/atoms/Circle/{logic/update.js => Circle.logic.js} (70%) create mode 100644 src/frontend/ui/models/Particles/components/atoms/Circle/index.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.logic.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js rename src/frontend/ui/models/Particles/components/atoms/Particles/{index.jsx => Particles.jsx} (79%) rename src/frontend/ui/models/Particles/components/atoms/Particles/{logic/setup.js => Particles.logic.js} (65%) delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/handlers/index.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/logic/index.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js rename src/frontend/ui/models/Particles/components/atoms/Plane/{index.jsx => Plane.jsx} (78%) create mode 100644 src/frontend/ui/models/Particles/components/atoms/Plane/Plane.logic.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Plane/index.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js rename src/frontend/ui/models/Particles/components/atoms/RayLine/{index.jsx => RayLine.jsx} (71%) rename src/frontend/ui/models/Particles/components/atoms/RayLine/{logic/update.js => RayLine.logic.js} (72%) create mode 100644 src/frontend/ui/models/Particles/components/atoms/RayLine/index.js create mode 100644 src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.js rename src/frontend/ui/models/Particles/components/atoms/Raycaster/{logic/update.js => Raycaster.logic.js} (51%) delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Raycaster/index.jsx delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js rename src/frontend/ui/models/Particles/components/molecules/Controls/{index.jsx => Controls.jsx} (69%) create mode 100644 src/frontend/ui/models/Particles/components/molecules/Controls/index.js rename src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/{index.jsx => ParticlesSystem.jsx} (70%) create mode 100644 src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js create mode 100644 src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.js create mode 100644 src/frontend/ui/models/Particles/components/molecules/index.js delete mode 100644 src/frontend/ui/models/Particles/logic/listeners/resize.js create mode 100644 src/frontend/ui/models/Particles/utils/ease.js create mode 100644 src/frontend/ui/models/Particles/utils/image.js diff --git a/src/frontend/ui/models/Particles/components/atoms/Box/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Box/Box.jsx similarity index 88% rename from src/frontend/ui/models/Particles/components/atoms/Box/index.jsx rename to src/frontend/ui/models/Particles/components/atoms/Box/Box.jsx index a29a3bd..162fb2c 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Box/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Box/Box.jsx @@ -6,9 +6,7 @@ //* Main export default function Box({ config, data, refs }) { // Props - const { - general: { showHelpers } - } = config; + const { general: { showHelpers } } = config; // Return return ( diff --git a/src/frontend/ui/models/Particles/components/atoms/Box/index.js b/src/frontend/ui/models/Particles/components/atoms/Box/index.js new file mode 100644 index 0000000..6f296f0 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Box/index.js @@ -0,0 +1,2 @@ +export { default } from './Box'; +export { default as Box } from './Box'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.jsx similarity index 78% rename from src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx rename to src/frontend/ui/models/Particles/components/atoms/Camera/Camera.jsx index 2e5c7f0..a9f1681 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Camera/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.jsx @@ -6,11 +6,8 @@ import { PerspectiveCamera } from "@react-three/drei"; import { CameraHelper } from "three"; import { useHelper } from "@react-three/drei"; -import { CameraSetup } from './logic/setup'; - -export { CameraSetup }; - -export default function Camera({ config, refs }) { +import CameraLogic from "./Camera.logic"; +function Camera({ config, refs }) { // Props const { general: { showHelpers } @@ -24,4 +21,8 @@ export default function Camera({ config, refs }) { {...config.camera} /> ); -} \ No newline at end of file +} + +Camera.logic = new CameraLogic(); + +export default Camera; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.logic.js similarity index 65% rename from src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js rename to src/frontend/ui/models/Particles/components/atoms/Camera/Camera.logic.js index 1d0726f..79811c0 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Camera/logic/setup.js +++ b/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.logic.js @@ -1,15 +1,7 @@ -/** - * Camera setup strategy for particle system - */ - import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class CameraSetup extends ModelStrategy { - /** - * Execute camera setup - * @param {Object} args - Arguments containing config, data, and refs - */ - execute({ config, data: { unit }, refs: { camera } }) { +export default class CameraLogic extends ModelStrategy { + setup({ config, data: { unit }, refs: { camera } }) { const { camera: { margin } } = config; const aspectRatio = window.innerWidth / window.innerHeight; diff --git a/src/frontend/ui/models/Particles/components/atoms/Camera/index.js b/src/frontend/ui/models/Particles/components/atoms/Camera/index.js new file mode 100644 index 0000000..7247c9a --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Camera/index.js @@ -0,0 +1,2 @@ +export { default } from './Camera'; +export { default as Camera } from './Camera'; diff --git a/src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.jsx similarity index 81% rename from src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx rename to src/frontend/ui/models/Particles/components/atoms/Circle/Circle.jsx index 646bfa7..96d0f66 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Circle/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.jsx @@ -4,12 +4,9 @@ */ //* Imports -import { CircleUpdate } from './logic/update'; - -export { CircleUpdate }; - +import CircleLogic from "./Circle.logic"; //* Main -export default function Circle({ config, data, refs }) { +function Circle({ config, data, refs }) { // Props const { general: { showHelpers }, @@ -31,4 +28,8 @@ export default function Circle({ config, data, refs }) { /> ); -} \ No newline at end of file +} + +Circle.logic = new CircleLogic(); + +export default Circle; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.logic.js similarity index 70% rename from src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js rename to src/frontend/ui/models/Particles/components/atoms/Circle/Circle.logic.js index 7845f40..c12521e 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Circle/logic/update.js +++ b/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.logic.js @@ -1,7 +1,7 @@ import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -export class CircleUpdate extends ModelStrategy { - execute({ objects, refs, target }) { +export default class CircleLogic extends ModelStrategy { + update({ objects, refs, target }) { objects.raycaster.ray.intersectPlane(refs.plane.current, target); refs.circle.current.position.copy(target); } diff --git a/src/frontend/ui/models/Particles/components/atoms/Circle/index.js b/src/frontend/ui/models/Particles/components/atoms/Circle/index.js new file mode 100644 index 0000000..fefad1e --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Circle/index.js @@ -0,0 +1,2 @@ +export { default } from './Circle'; +export { default as Circle } from './Circle'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.js new file mode 100644 index 0000000..7915baa --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.js @@ -0,0 +1,7 @@ +import MouseLogic from './Mouse.logic'; + +const Mouse = { + logic: new MouseLogic() +}; + +export default Mouse; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.logic.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.logic.js new file mode 100644 index 0000000..f9722c5 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.logic.js @@ -0,0 +1,42 @@ +import { onMouseMove } from "@semantyk/frontend/logic/services/callbacks"; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; +import { updateOnMouseMove } from "../../../logic"; + +export default class MouseLogic extends ModelStrategy { + add({ handleMouseMove }) { + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("touchmove", handleMouseMove); + } + + remove({ handleMouseMove }) { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("touchmove", handleMouseMove); + } + + handle({ event, ...args }) { + const { mouse, moveMouseTimeout } = args.refs; + clearTimeout(moveMouseTimeout.current); + mouse.current.isMoving = true; + moveMouseTimeout.current = setTimeout(() => mouse.current.isMoving = false, 1); + + let clientX, clientY; + if (event.type === "mousemove") { + clientX = event.clientX; + clientY = event.clientY; + } else if (event.type === "touchmove") { + clientX = event.touches[0].clientX; + clientY = event.touches[0].clientY; + } + + updateOnMouseMove({ + events: { mousemove: { clientX, clientY } }, + ...args + }); + } + + update({ refs, events }) { + const { x, y } = onMouseMove(events.mousemove); + refs.mouse.current.x = x * 2 - 1; + refs.mouse.current.y = -y * 2 + 1; + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/index.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/index.js index d1ba805..a2301c1 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/index.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/index.js @@ -1,3 +1,2 @@ -export { MouseHandler } from './logic/handler'; -export { MouseListener } from './logic/listener'; -export { MouseUpdate } from './logic/update'; \ No newline at end of file +export { default } from './Mouse'; +export { default as Mouse } from './Mouse'; diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js deleted file mode 100644 index 3ef64a9..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/handler.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Mouse handler strategy for particle system - */ - -import { Vector2 } from 'three'; -import { ModelStrategy } from '@semantyk/frontend/ui/components/molecules/Model/logic/strategy'; -import { updateOnMouseMove } from '../../../../logic/index'; - -export class MouseHandler extends ModelStrategy { - /** - * Execute mouse/touch move events - * @param {Object} args - Arguments containing event and particle system data - */ - execute({ event, ...args }) { - const { mouse, moveMouseTimeout } = args.refs; - clearTimeout(moveMouseTimeout.current); - mouse.current.isMoving = true; - moveMouseTimeout.current = setTimeout(() => mouse.current.isMoving = false, 1); - - let clientX, clientY; - if (event.type === "mousemove") { - clientX = event.clientX; - clientY = event.clientY; - } else if (event.type === "touchmove") { - clientX = event.touches[0].clientX; - clientY = event.touches[0].clientY; - } - - updateOnMouseMove({ - events: { mousemove: { clientX, clientY } }, - ...args - }); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js deleted file mode 100644 index d9f1bfe..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/listener.js +++ /dev/null @@ -1,13 +0,0 @@ -import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; - -export class MouseListener extends ModelStrategy { - add({ handleMouseMove }) { - window.addEventListener("mousemove", handleMouseMove); - window.addEventListener("touchmove", handleMouseMove); - } - - remove({ handleMouseMove }) { - window.removeEventListener("mousemove", handleMouseMove); - window.removeEventListener("touchmove", handleMouseMove); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js deleted file mode 100644 index b81889e..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/logic/update.js +++ /dev/null @@ -1,10 +0,0 @@ -import { onMouseMove } from "@semantyk/frontend/logic/services/callbacks"; -import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; - -export class MouseUpdate extends ModelStrategy { - execute({ refs, events }) { - const { x, y } = onMouseMove(events.mousemove); - refs.mouse.current.x = x * 2 - 1; - refs.mouse.current.y = -y * 2 + 1; - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.jsx similarity index 79% rename from src/frontend/ui/models/Particles/components/atoms/Particles/index.jsx rename to src/frontend/ui/models/Particles/components/atoms/Particles/Particles.jsx index 8a5fa42..b2406fa 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.jsx @@ -3,7 +3,8 @@ * Atom component for the particles in the Particles model */ -export default function Particles({ config, refs }) { +//* Main +function Particles({ config, refs }) { return ( @@ -13,4 +14,6 @@ export default function Particles({ config, refs }) { /> ); -} \ No newline at end of file +} + +export default Particles; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js similarity index 65% rename from src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js rename to src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js index afbe6e3..a4a6f8e 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/setup.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js @@ -1,18 +1,19 @@ -/** - * Particles setup strategy for particle system - */ - -import { Float32BufferAttribute } from 'three'; - import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -import { getImageData } from '../../../../logic'; +import { getImageData } from "../../../utils/image"; +import { Float32BufferAttribute } from "three"; +import { ParticlesManager } from "../../../logic/manager"; + +export default class ParticlesLogic extends ModelStrategy { + handle({ event, ...args }) { + const { particles } = args.refs; + ParticlesManager.setup('camera', args); + const { particle } = args.config; + const ratio = window.innerWidth / window.innerHeight; + const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); + particles.current.material.size = size; + } -export class ParticlesSetup extends ModelStrategy { - /** - * Execute particles setup - * @param {Object} args - Arguments containing config, data, objects, and refs - */ - execute({ config, data: { color, unit }, objects: { image }, refs }) { + setup({ config, data: { color, unit }, objects: { image }, refs }) { const { particle } = config; const particles = refs.particles.current; const { data } = getImageData({ data: { unit }, objects: { image } }); @@ -67,4 +68,17 @@ export class ParticlesSetup extends ModelStrategy { const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); particles.material.size = size; } + + update(args) { + const intersects = args.objects.raycaster.intersectObject(args.refs.particles.current); + const idxs = new Set(intersects.map(({ index }) => index)); + + for (let i = 0; i < args.refs.particles.current.data.count; i++) { + ParticlesManager.affect('color', { i, ...args }); + ParticlesManager.affect('position', { i, idxs, ...args }); + } + + args.refs.particles.current.geometry.attributes.color.needsUpdate = true; + args.refs.particles.current.geometry.attributes.position.needsUpdate = true; + } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js index 882a536..47b59e0 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js @@ -1,5 +1,5 @@ import { Vector3 } from 'three'; -import { ease } from "@semantyk/frontend/ui/models/Particles/logic"; +import { ease } from "@semantyk/frontend/ui/models/Particles/utils/ease"; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export class InterpolationEffect extends ModelStrategy { diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/index.js b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/index.js deleted file mode 100644 index c514824..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ResizeHandler } from './resize'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js b/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js deleted file mode 100644 index d1b750f..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/handlers/resize.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Resize handler strategy for particle system - */ - -import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; -import { ParticlesManager } from '../../../../logic/manager'; - -export class ResizeHandler extends ModelStrategy { - /** - * Execute window resize events - * @param {Object} args - Arguments containing event and particle system data - */ - execute({ event, ...args }) { - const { particles } = args.refs; - ParticlesManager.setup('camera', args); - const { particle } = args.config; - const ratio = window.innerWidth / window.innerHeight; - const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); - particles.current.material.size = size; - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/index.js b/src/frontend/ui/models/Particles/components/atoms/Particles/index.js index eae883e..0cb9b8f 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/index.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/index.js @@ -1,3 +1,3 @@ export * from './effects'; -export * from './handlers'; -export * from './logic'; \ No newline at end of file +export { default } from './Particles' +export { default as Particles } from './Particles'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/index.js b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/index.js deleted file mode 100644 index b9ee04a5..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { ParticlesSetup } from './setup'; -export { ParticlesUpdate } from './update'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js deleted file mode 100644 index d33249f..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/logic/update.js +++ /dev/null @@ -1,17 +0,0 @@ -import { ParticlesManager } from '../../../../logic/manager'; -import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; - -export class ParticlesUpdate extends ModelStrategy { - execute(args) { - const intersects = args.objects.raycaster.intersectObject(args.refs.particles.current); - const idxs = new Set(intersects.map(({ index }) => index)); - - for (let i = 0; i < args.refs.particles.current.data.count; i++) { - ParticlesManager.affect('color', { i, ...args }); - ParticlesManager.affect('position', { i, idxs, ...args }); - } - - args.refs.particles.current.geometry.attributes.color.needsUpdate = true; - args.refs.particles.current.geometry.attributes.position.needsUpdate = true; - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.jsx similarity index 78% rename from src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx rename to src/frontend/ui/models/Particles/components/atoms/Plane/Plane.jsx index a5b358c..d212590 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Plane/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.jsx @@ -1,10 +1,8 @@ //* Imports -import { PlaneSetup } from './logic/setup'; - -export { PlaneSetup }; +import PlaneLogic from "./Plane.logic"; //* Main -export default function Plane({ config, data, refs }) { +function Plane({ config, data, refs }) { // Props const { general: { showHelpers } @@ -25,4 +23,8 @@ export default function Plane({ config, data, refs }) { /> ); -} \ No newline at end of file +} + +Plane.logic = new PlaneLogic(); + +export default Plane; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.logic.js b/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.logic.js new file mode 100644 index 0000000..3737ef0 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.logic.js @@ -0,0 +1,9 @@ +import { Plane, Vector3 } from "three"; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; + +export default class PlaneLogic extends ModelStrategy { + setup({ data: { unit }, refs: { plane } }) { + const normal = new Vector3(0, 0, 1); + plane.current = new Plane(normal, unit / 2); + } +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/index.js b/src/frontend/ui/models/Particles/components/atoms/Plane/index.js new file mode 100644 index 0000000..c83936e --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Plane/index.js @@ -0,0 +1,2 @@ +export { default } from './Plane'; +export { default as Plane } from './Plane'; diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js deleted file mode 100644 index d57c525..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Plane/logic/setup.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Plane setup strategy for particle system - */ - -import { Plane, Vector3 } from 'three'; -import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; - -export class PlaneSetup extends ModelStrategy { - /** - * Execute plane setup - * @param {Object} args - Arguments containing data and refs - */ - execute({ data: { unit }, refs: { plane } }) { - const normal = new Vector3(0, 0, 1); - plane.current = new Plane(normal, unit / 2); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx b/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.jsx similarity index 71% rename from src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx rename to src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.jsx index 361a4bf..edd1a6a 100644 --- a/src/frontend/ui/models/Particles/components/atoms/RayLine/index.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.jsx @@ -4,12 +4,10 @@ */ //* Imports -import { LineUpdate } from './logic/update'; - -export { LineUpdate }; +import RayLineLogic from './RayLine.logic'; //* Main -export default function RayLine({ config, refs }) { +function RayLine({ config, refs }) { // Props const { general: { showHelpers } @@ -21,4 +19,8 @@ export default function RayLine({ config, refs }) { ); -} \ No newline at end of file +} + +RayLine.logic = new RayLineLogic(); + +export default RayLine; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/RayLine/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.logic.js similarity index 72% rename from src/frontend/ui/models/Particles/components/atoms/RayLine/logic/update.js rename to src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.logic.js index a6dbc36..989854e 100644 --- a/src/frontend/ui/models/Particles/components/atoms/RayLine/logic/update.js +++ b/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.logic.js @@ -1,8 +1,8 @@ -import { BufferGeometry } from 'three'; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; +import { BufferGeometry } from "three"; -export class LineUpdate extends ModelStrategy { - execute({ objects, refs, target }) { +export default class RayLineLogic extends ModelStrategy { + update({ objects, refs, target }) { const { origin } = objects.raycaster.ray; const points = [origin, target]; const geometry = new BufferGeometry().setFromPoints(points); diff --git a/src/frontend/ui/models/Particles/components/atoms/RayLine/index.js b/src/frontend/ui/models/Particles/components/atoms/RayLine/index.js new file mode 100644 index 0000000..d13cae3 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/RayLine/index.js @@ -0,0 +1,2 @@ +export { default } from './RayLine' +export { default as RayLine } from './RayLine'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.js new file mode 100644 index 0000000..ebac016 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.js @@ -0,0 +1,12 @@ +/** + * Raycaster.jsx + * Atom component for the raycaster in the Particles model + */ + +import RaycasterLogic from "./Raycaster.logic"; + +const Raycaster = { + logic: new RaycasterLogic() +}; + +export default Raycaster; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.logic.js similarity index 51% rename from src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js rename to src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.logic.js index 237c8a6..ffdad7e 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/update.js +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.logic.js @@ -1,8 +1,13 @@ -import { Vector2 } from 'three'; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; +import { Vector2 } from "three"; -export class RaycasterUpdate extends ModelStrategy { - execute({ objects, refs }) { +export default class RaycasterLogic extends ModelStrategy { + setup({ config, data: { unit }, objects: { raycaster } }) { + const { animations: { chaos: { radius } } } = config; + raycaster.params.Points.threshold = radius * unit; + } + + update({ objects, refs }) { const { raycaster } = objects; const camera = refs.camera.current; const mouse = refs.mouse.current; diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.js index d726742..e803f87 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.js +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.js @@ -1,2 +1,2 @@ -export { RaycasterSetup } from './logic/setup'; -export { RaycasterUpdate } from './logic/update'; \ No newline at end of file +export { default as Raycaster } from './Raycaster'; +export { default as RaycasterLogic } from './Raycaster.logic'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.jsx b/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.jsx deleted file mode 100644 index a6b2b5b..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Raycaster/index.jsx +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Raycaster.jsx - * Atom component for the raycaster in the Particles model - */ - -export default function Raycaster() { - return null; -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js deleted file mode 100644 index 012ef54..0000000 --- a/src/frontend/ui/models/Particles/components/atoms/Raycaster/logic/setup.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Raycaster setup strategy for particle system - */ - -import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; - -export class RaycasterSetup extends ModelStrategy { - /** - * Execute raycaster setup - * @param {Object} args - Arguments containing config, data, and objects - */ - execute({ config, data: { unit }, objects: { raycaster } }) { - const { animations: { chaos: { radius } } } = config; - raycaster.params.Points.threshold = radius * unit; - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/index.js b/src/frontend/ui/models/Particles/components/atoms/index.js index 7227091..49d0f0f 100644 --- a/src/frontend/ui/models/Particles/components/atoms/index.js +++ b/src/frontend/ui/models/Particles/components/atoms/index.js @@ -1,9 +1,8 @@ +export * from './Box'; export * from './Camera'; export * from './Circle'; export * from './Mouse'; export * from './Plane'; -export * from './Particles/effects'; -export * from './Particles/handlers'; -export * from './Particles/logic'; +export * from './Particles'; export * from './Raycaster'; export * from './RayLine'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/Controls/index.jsx b/src/frontend/ui/models/Particles/components/molecules/Controls/Controls.jsx similarity index 69% rename from src/frontend/ui/models/Particles/components/molecules/Controls/index.jsx rename to src/frontend/ui/models/Particles/components/molecules/Controls/Controls.jsx index a0e2e3f..2500829 100644 --- a/src/frontend/ui/models/Particles/components/molecules/Controls/index.jsx +++ b/src/frontend/ui/models/Particles/components/molecules/Controls/Controls.jsx @@ -1,10 +1,10 @@ //* Imports import { OrbitControls } from "@react-three/drei"; import Box from "../../atoms/Box"; -import Circle from "../../atoms/Circle"; -import Plane from "../../atoms/Plane"; -import RayLine from "../../atoms/RayLine"; -import Camera from "../../atoms/Camera"; +import Circle from "../../atoms/Circle/Circle"; +import Plane from "../../atoms/Plane/Plane"; +import RayLine from "../../atoms/RayLine/RayLine"; +import Camera from "../../atoms/Camera/Camera"; //* Main export default function Controls(args) { diff --git a/src/frontend/ui/models/Particles/components/molecules/Controls/index.js b/src/frontend/ui/models/Particles/components/molecules/Controls/index.js new file mode 100644 index 0000000..71b5b47 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/molecules/Controls/index.js @@ -0,0 +1,2 @@ +export { default } from './Controls'; +export { default as Controls } from './Controls'; diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx similarity index 70% rename from src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx rename to src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx index 2a23113..9708797 100644 --- a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.jsx +++ b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx @@ -6,7 +6,7 @@ //* Imports import { useEffect } from "react"; import { useFrame } from "@react-three/fiber"; -import Particles from "../../atoms/Particles/index.jsx"; +import Particles from "../../atoms/Particles/Particles.jsx"; import { setup, update, @@ -14,10 +14,10 @@ import { import { ParticlesManager } from "../../../logic/manager.js"; //* Main -export default function ParticlesSystem(args) { +function ParticlesSystem(args) { // Logic useEffect(() => { - setup(args); + ParticlesManager.setup('particlesSystem', args); const handleMouseMove = (event) => { ParticlesManager.handle('mouseMove', { event, ...args }); @@ -27,8 +27,8 @@ export default function ParticlesSystem(args) { ParticlesManager.handle('resize', { event, ...args }); }; - ParticlesManager.executeAll('add', { handleMouseMove, handleResize }); - return () => ParticlesManager.executeAll('remove', { handleMouseMove, handleResize }); + ParticlesManager.addAll({ handleMouseMove, handleResize }); + return () => ParticlesManager.removeAll({ handleMouseMove, handleResize }); }, [args]); useFrame(({ clock }) => { @@ -37,4 +37,6 @@ export default function ParticlesSystem(args) { }); return ; -} \ No newline at end of file +} + +export default ParticlesSystem; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js new file mode 100644 index 0000000..9b84c08 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js @@ -0,0 +1,23 @@ +import { ParticlesManager } from "../../../logic/manager"; +import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; + +export default class ParticlesSystemLogic extends ModelStrategy { + add({ handleResize }) { + window.addEventListener("resize", handleResize); + } + + remove({ handleResize }) { + window.removeEventListener("resize", handleResize); + } + + setup(args) { + ParticlesManager.setup('camera', args); + ParticlesManager.setup('particles', args); + ParticlesManager.setup('plane', args); + ParticlesManager.setup('raycaster', args); + } + + update(args) { + ParticlesManager.update("particles", args); + } +} diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.js b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.js new file mode 100644 index 0000000..cc9fa22 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.js @@ -0,0 +1,2 @@ +export { default } from './ParticlesSystem'; +export { default as ParticlesSystem } from './ParticlesSystem'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/index.js b/src/frontend/ui/models/Particles/components/molecules/index.js new file mode 100644 index 0000000..2365e40 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/molecules/index.js @@ -0,0 +1,2 @@ +export * from './Controls'; +export * from './ParticlesSystem'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/config/index.js b/src/frontend/ui/models/Particles/config/index.js index 3842e7d..51d778e 100644 --- a/src/frontend/ui/models/Particles/config/index.js +++ b/src/frontend/ui/models/Particles/config/index.js @@ -20,7 +20,7 @@ export const config = { // General general: { - showHelpers: false, + showHelpers: true, scale: 1, size: 150, }, diff --git a/src/frontend/ui/models/Particles/index.jsx b/src/frontend/ui/models/Particles/index.jsx index b1427fe..f130bbd 100644 --- a/src/frontend/ui/models/Particles/index.jsx +++ b/src/frontend/ui/models/Particles/index.jsx @@ -18,9 +18,7 @@ //* Imports import { useArgs } from "@semantyk/frontend/ui/models/Particles/hooks/useArgs"; -import Controls from "./components/molecules/Controls"; -import ParticlesSystem from "./components/molecules/ParticlesSystem"; -import Camera from "./components/atoms/Camera"; +import { Controls, ParticlesSystem } from "./components/molecules"; //* Main export default function ParticlesModel() { diff --git a/src/frontend/ui/models/Particles/logic/index.js b/src/frontend/ui/models/Particles/logic/index.js index a7ce015..1ba1147 100644 --- a/src/frontend/ui/models/Particles/logic/index.js +++ b/src/frontend/ui/models/Particles/logic/index.js @@ -20,25 +20,6 @@ import { Vector3 } from "three"; import { ParticlesManager } from "./manager"; //* Main -export function getImageData(args) { - // Args - const { data: { unit }, objects: { image } } = args; - // Logic - let { width, height } = image; - const canvas = document.createElement("canvas"); - const context = canvas.getContext("2d"); - canvas.width = unit; - canvas.height = (height / width) * unit; - context.drawImage(image, 0, 0, canvas.width, canvas.height); - // Return - return context.getImageData(0, 0, canvas.width, canvas.height); -} - -export function ease(time, duration) { - const t = Math.min(time / duration, 1); - return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; -} - export function setup(args) { ParticlesManager.setup('camera', args); ParticlesManager.setup('particles', args); @@ -53,7 +34,7 @@ export function update(args) { export function updateOnMouseMove(args) { const target = new Vector3() ParticlesManager.update("circle", { target, ...args }); - ParticlesManager.update("line", { target, ...args }); + ParticlesManager.update("rayLine", { target, ...args }); ParticlesManager.update("mouse", args); ParticlesManager.update("raycaster", args); } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/listeners/resize.js b/src/frontend/ui/models/Particles/logic/listeners/resize.js deleted file mode 100644 index 2825069..0000000 --- a/src/frontend/ui/models/Particles/logic/listeners/resize.js +++ /dev/null @@ -1,11 +0,0 @@ -import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; - -export class ResizeListener extends ModelStrategy { - add({ handleResize }) { - window.addEventListener("resize", handleResize); - } - - remove({ handleResize }) { - window.removeEventListener("resize", handleResize); - } -} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/manager.js b/src/frontend/ui/models/Particles/logic/manager.js index f2bccf9..4a9b033 100644 --- a/src/frontend/ui/models/Particles/logic/manager.js +++ b/src/frontend/ui/models/Particles/logic/manager.js @@ -1,24 +1,25 @@ import { ModelManager } from '@semantyk/frontend/ui/components/molecules/Model/logic/manager'; -import { ResizeListener } from './listeners/resize'; -import { CameraSetup } from '../components/atoms/Camera/logic/setup'; -import { CircleUpdate } from '../components/atoms/Circle/logic/update'; -import { MouseHandler, MouseListener, MouseUpdate } from '../components/atoms/Mouse'; -import { PlaneSetup } from '../components/atoms/Plane/logic/setup'; -import { RaycasterSetup, RaycasterUpdate } from '../components/atoms/Raycaster'; -import { LineUpdate } from '../components/atoms/RayLine/logic/update'; import { ChaosEffect } from '../components/atoms/Particles/effects/chaos'; import { ColorEffect } from '../components/atoms/Particles/effects/color'; import { EntropyEffect } from '../components/atoms/Particles/effects/entropy'; import { PositionEffect } from '../components/atoms/Particles/effects/position'; -import { ResizeHandler } from '../components/atoms/Particles/handlers/resize'; -import { ParticlesSetup } from '../components/atoms/Particles/logic/setup'; -import { ParticlesUpdate } from '../components/atoms/Particles/logic/update'; - +import { Camera, Circle, Mouse, Particles, Plane, Raycaster, RayLine } from '../components/atoms'; +import ParticlesSystemLogic from '../components/molecules/ParticlesSystem/ParticlesSystem.logic'; +import ParticlesLogic from '../components/atoms/Particles/Particles.logic'; /** * Manager class for particle strategies using the Strategy pattern */ export class ParticlesManager extends ModelManager { - static instance = new ParticlesManager(); + static logic = { + camera: Camera.logic, + circle: Circle.logic, + mouse: Mouse.logic, + plane: Plane.logic, + particles: new ParticlesLogic(), // TODO: Remove hack to get logic to work + particlesSystem: new ParticlesSystemLogic(), // TODO: Remove hack to get logic to work + raycaster: Raycaster.logic, + rayLine: RayLine.logic + } static effects = { chaos: new ChaosEffect(), @@ -28,28 +29,13 @@ export class ParticlesManager extends ModelManager { }; static handlers = { - mouseMove: new MouseHandler(), - resize: new ResizeHandler() - }; - - static setups = { - camera: new CameraSetup(), - particles: new ParticlesSetup(), - plane: new PlaneSetup(), - raycaster: new RaycasterSetup() + mouseMove: Mouse.logic, + resize: Particles.logic }; static listeners = { - mouse: new MouseListener(), - resize: new ResizeListener() - }; - - static updates = { - circle: new CircleUpdate(), - line: new LineUpdate(), - mouse: new MouseUpdate(), - particles: new ParticlesUpdate(), - raycaster: new RaycasterUpdate(), + mouse: Mouse.logic, + resize: new ParticlesSystemLogic() }; static affect(item, ...args) { @@ -59,21 +45,26 @@ export class ParticlesManager extends ModelManager { static handle(item, ...args) { const collection = this.handlers; - return super.execute(collection, item, 'execute', ...args); + return super.execute(collection, item, 'handle', ...args); } static setup(item, ...args) { - const collection = this.setups; - return super.execute(collection, item, 'execute', ...args); + const collection = this.logic; + return super.execute(collection, item, 'setup', ...args); } static update(item, ...args) { - const collection = this.updates; - return super.execute(collection, item, 'execute', ...args); + const collection = this.logic; + return super.execute(collection, item, 'update', ...args); + } + + static addAll(...args) { + const collection = this.listeners; + return super.executeAll(collection, "add", ...args); } - static executeAll(member, ...args) { + static removeAll(...args) { const collection = this.listeners; - return super.executeAll(collection, member, ...args); + return super.executeAll(collection, "remove", ...args); } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/utils/ease.js b/src/frontend/ui/models/Particles/utils/ease.js new file mode 100644 index 0000000..64a4da0 --- /dev/null +++ b/src/frontend/ui/models/Particles/utils/ease.js @@ -0,0 +1,4 @@ +export function ease(time, duration) { + const t = Math.min(time / duration, 1); + return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/utils/image.js b/src/frontend/ui/models/Particles/utils/image.js new file mode 100644 index 0000000..7b6567f --- /dev/null +++ b/src/frontend/ui/models/Particles/utils/image.js @@ -0,0 +1,17 @@ +/** + * Image utilities for Particles + */ + +export function getImageData(args) { + // Args + const { data: { unit }, objects: { image } } = args; + // Logic + let { width, height } = image; + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + canvas.width = unit; + canvas.height = (height / width) * unit; + context.drawImage(image, 0, 0, canvas.width, canvas.height); + // Return + return context.getImageData(0, 0, canvas.width, canvas.height); +} \ No newline at end of file From 9ca176afbb245b7254a9fe70e7b9834a01839d5a Mon Sep 17 00:00:00 2001 From: "Daniel B." Date: Fri, 14 Mar 2025 22:31:49 -0600 Subject: [PATCH 6/7] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Chore:=20Perfect=20ref?= =?UTF-8?q?actor=20on=20Particles=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../molecules/Model/logic/strategy.js | 15 ++++++++++++--- .../components/atoms/Camera/Camera.jsx | 2 +- .../components/atoms/Camera/Camera.logic.js | 2 +- .../components/atoms/Circle/Circle.jsx | 2 +- .../components/atoms/Circle/Circle.logic.js | 2 +- .../Particles/components/atoms/Mouse/Mouse.js | 4 +--- .../components/atoms/Mouse/Mouse.logic.js | 8 ++++---- .../atoms/Particles/Particles.logic.js | 11 ++++++----- .../atoms/Particles/effects/chaos.js | 2 +- .../atoms/Particles/effects/color.js | 2 +- .../atoms/Particles/effects/entropy.js | 2 +- .../atoms/Particles/effects/flotation.js | 2 +- .../atoms/Particles/effects/interpolation.js | 2 +- .../atoms/Particles/effects/position.js | 18 +++++------------- .../components/atoms/Particles/index.js | 3 ++- .../Particles/components/atoms/Plane/Plane.jsx | 2 +- .../components/atoms/Plane/Plane.logic.js | 2 +- .../components/atoms/RayLine/RayLine.jsx | 2 +- .../components/atoms/RayLine/RayLine.logic.js | 2 +- .../components/atoms/Raycaster/Raycaster.js | 2 +- .../atoms/Raycaster/Raycaster.logic.js | 4 ++-- .../ParticlesSystem/ParticlesSystem.jsx | 9 ++------- .../ParticlesSystem/ParticlesSystem.logic.js | 8 ++++---- .../ui/models/Particles/logic/manager.js | 17 ++++++++--------- 24 files changed, 60 insertions(+), 65 deletions(-) diff --git a/src/frontend/ui/components/molecules/Model/logic/strategy.js b/src/frontend/ui/components/molecules/Model/logic/strategy.js index 1c1fd7c..4c8e189 100644 --- a/src/frontend/ui/components/molecules/Model/logic/strategy.js +++ b/src/frontend/ui/components/molecules/Model/logic/strategy.js @@ -7,16 +7,25 @@ export class ModelStrategy { * @param {Object} args - Event arguments * @throws {Error} Must be implemented by child classes */ - add(args) { + static add(args) { throw new Error('Strategy must implement add method'); } + /** + * Handle the event + * @param {Object} args - Event arguments + * @throws {Error} Must be implemented by child classes + */ + static handle(args) { + throw new Error('Strategy must implement handle method'); + } + /** * Execute the strategy * @param {Object} args - Strategy arguments * @throws {Error} Must be implemented by child classes */ - execute(args) { + static execute(args) { throw new Error('Strategy must implement execute method'); } @@ -25,7 +34,7 @@ export class ModelStrategy { * @param {Object} args - Event arguments * @throws {Error} Must be implemented by child classes */ - remove(args) { + static remove(args) { throw new Error('Strategy must implement remove method'); } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.jsx b/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.jsx index a9f1681..c78e394 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.jsx @@ -23,6 +23,6 @@ function Camera({ config, refs }) { ); } -Camera.logic = new CameraLogic(); +Camera.logic = CameraLogic; export default Camera; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.logic.js b/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.logic.js index 79811c0..e92dc39 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.logic.js +++ b/src/frontend/ui/models/Particles/components/atoms/Camera/Camera.logic.js @@ -1,7 +1,7 @@ import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export default class CameraLogic extends ModelStrategy { - setup({ config, data: { unit }, refs: { camera } }) { + static setup({ config, data: { unit }, refs: { camera } }) { const { camera: { margin } } = config; const aspectRatio = window.innerWidth / window.innerHeight; diff --git a/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.jsx b/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.jsx index 96d0f66..5ad601e 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.jsx @@ -30,6 +30,6 @@ function Circle({ config, data, refs }) { ); } -Circle.logic = new CircleLogic(); +Circle.logic = CircleLogic; export default Circle; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.logic.js b/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.logic.js index c12521e..ba5073c 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.logic.js +++ b/src/frontend/ui/models/Particles/components/atoms/Circle/Circle.logic.js @@ -1,7 +1,7 @@ import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export default class CircleLogic extends ModelStrategy { - update({ objects, refs, target }) { + static update({ objects, refs, target }) { objects.raycaster.ray.intersectPlane(refs.plane.current, target); refs.circle.current.position.copy(target); } diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.js index 7915baa..5a5e3d6 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.js @@ -1,7 +1,5 @@ import MouseLogic from './Mouse.logic'; -const Mouse = { - logic: new MouseLogic() -}; +const Mouse = { logic: MouseLogic }; export default Mouse; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.logic.js b/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.logic.js index f9722c5..f42fa60 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.logic.js +++ b/src/frontend/ui/models/Particles/components/atoms/Mouse/Mouse.logic.js @@ -3,17 +3,17 @@ import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/ import { updateOnMouseMove } from "../../../logic"; export default class MouseLogic extends ModelStrategy { - add({ handleMouseMove }) { + static add({ handleMouseMove }) { window.addEventListener("mousemove", handleMouseMove); window.addEventListener("touchmove", handleMouseMove); } - remove({ handleMouseMove }) { + static remove({ handleMouseMove }) { window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("touchmove", handleMouseMove); } - handle({ event, ...args }) { + static handle({ event, ...args }) { const { mouse, moveMouseTimeout } = args.refs; clearTimeout(moveMouseTimeout.current); mouse.current.isMoving = true; @@ -34,7 +34,7 @@ export default class MouseLogic extends ModelStrategy { }); } - update({ refs, events }) { + static update({ refs, events }) { const { x, y } = onMouseMove(events.mousemove); refs.mouse.current.x = x * 2 - 1; refs.mouse.current.y = -y * 2 + 1; diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js b/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js index a4a6f8e..4d562d3 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js @@ -2,9 +2,10 @@ import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/ import { getImageData } from "../../../utils/image"; import { Float32BufferAttribute } from "three"; import { ParticlesManager } from "../../../logic/manager"; +import { ColorEffect, PositionEffect } from "./effects"; export default class ParticlesLogic extends ModelStrategy { - handle({ event, ...args }) { + static handle({ event, ...args }) { const { particles } = args.refs; ParticlesManager.setup('camera', args); const { particle } = args.config; @@ -13,7 +14,7 @@ export default class ParticlesLogic extends ModelStrategy { particles.current.material.size = size; } - setup({ config, data: { color, unit }, objects: { image }, refs }) { + static setup({ config, data: { color, unit }, objects: { image }, refs }) { const { particle } = config; const particles = refs.particles.current; const { data } = getImageData({ data: { unit }, objects: { image } }); @@ -69,13 +70,13 @@ export default class ParticlesLogic extends ModelStrategy { particles.material.size = size; } - update(args) { + static update(args) { const intersects = args.objects.raycaster.intersectObject(args.refs.particles.current); const idxs = new Set(intersects.map(({ index }) => index)); for (let i = 0; i < args.refs.particles.current.data.count; i++) { - ParticlesManager.affect('color', { i, ...args }); - ParticlesManager.affect('position', { i, idxs, ...args }); + ColorEffect.execute({ i, ...args }); + PositionEffect.execute({ i, idxs, ...args }); } args.refs.particles.current.geometry.attributes.color.needsUpdate = true; diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js index 56149f0..82d941c 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js @@ -1,7 +1,7 @@ import { ModelStrategy } from '@semantyk/frontend/ui/components/molecules/Model/logic/strategy'; export class ChaosEffect extends ModelStrategy { - execute({ config, data, i, idxs, final, objects: { clock }, refs: { mouse, particles } }) { + static execute({ config, data, i, idxs, final, objects: { clock }, refs: { mouse, particles } }) { const { unit } = data; const { animations: { chaos, order, interpolation } } = config; const elapsedTime = clock.current.getElapsedTime(); diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js index bc596fc..e48df30 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js @@ -2,7 +2,7 @@ import { Color } from 'three'; import { ModelStrategy } from '@semantyk/frontend/ui/components/molecules/Model/logic/strategy'; export class ColorEffect extends ModelStrategy { - execute({ data: { color }, i, ...args }) { + static execute({ data: { color }, i, ...args }) { const chaoticValue = args.refs.particles.current.data.chaotic[i]; const final = color.clone(); const target = new Color(1, 0, 0); diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js index 741e222..b63e6b8 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js @@ -2,7 +2,7 @@ import { Vector3 } from 'three'; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export class EntropyEffect extends ModelStrategy { - execute({ config, i, idxs, final, ...args }) { + static execute({ config, i, idxs, final, ...args }) { const { animations: { expansion, interpolation } } = config; const elapsedTime = args.objects.clock.current.getElapsedTime(); diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js index 73a6bae..e5a8307 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js @@ -2,7 +2,7 @@ import { Vector3 } from 'three'; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export class FlotationEffect extends ModelStrategy { - execute({ config, i, final, objects: { clock }, refs: { particles } }) { + static execute({ config, i, final, objects: { clock }, refs: { particles } }) { const { offsets } = particles.current.data.positions; const { animations: { flotation } } = config; const elapsedTime = clock.current.getElapsedTime(); diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js index 47b59e0..675baa9 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js @@ -3,7 +3,7 @@ import { ease } from "@semantyk/frontend/ui/models/Particles/utils/ease"; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export class InterpolationEffect extends ModelStrategy { - execute({ config, i, final, objects: { clock }, refs: { particles } }) { + static execute({ config, i, final, objects: { clock }, refs: { particles } }) { const { ideal, initial } = particles.current.data.positions; const { animations: { interpolation: { duration } } } = config; diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js index 06c4db6..0cea872 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js @@ -6,24 +6,16 @@ import { Vector3 } from 'three'; import { ChaosEffect } from "./chaos"; export class PositionEffect extends ModelStrategy { - constructor() { - super(); - this.chaosEffect = new ChaosEffect(); - this.entropyEffect = new EntropyEffect(); - this.interpolationEffect = new InterpolationEffect(); - this.flotationEffect = new FlotationEffect(); - } - - execute({ i, idxs, ...args }) { + static execute({ i, idxs, ...args }) { const { particles } = args.refs; const final = new Vector3() //* Effects //! Order Matters - this.entropyEffect.execute({ i, idxs, final, ...args }); - this.chaosEffect.execute({ i, idxs, final, ...args }); - this.interpolationEffect.execute({ i, final, ...args }); - this.flotationEffect.execute({ i, final, ...args }); + EntropyEffect.execute({ i, idxs, final, ...args }); + ChaosEffect.execute({ i, idxs, final, ...args }); + InterpolationEffect.execute({ i, final, ...args }); + FlotationEffect.execute({ i, final, ...args }); const positions = particles.current.geometry.attributes.position.array; positions.set(final.toArray(), i * 3); diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/index.js b/src/frontend/ui/models/Particles/components/atoms/Particles/index.js index 0cb9b8f..64fa614 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/index.js +++ b/src/frontend/ui/models/Particles/components/atoms/Particles/index.js @@ -1,3 +1,4 @@ export * from './effects'; -export { default } from './Particles' + +export { default } from './Particles'; export { default as Particles } from './Particles'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.jsx b/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.jsx index d212590..8137aa1 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.jsx @@ -25,6 +25,6 @@ function Plane({ config, data, refs }) { ); } -Plane.logic = new PlaneLogic(); +Plane.logic = PlaneLogic; export default Plane; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.logic.js b/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.logic.js index 3737ef0..b98e6b8 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.logic.js +++ b/src/frontend/ui/models/Particles/components/atoms/Plane/Plane.logic.js @@ -2,7 +2,7 @@ import { Plane, Vector3 } from "three"; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export default class PlaneLogic extends ModelStrategy { - setup({ data: { unit }, refs: { plane } }) { + static setup({ data: { unit }, refs: { plane } }) { const normal = new Vector3(0, 0, 1); plane.current = new Plane(normal, unit / 2); } diff --git a/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.jsx b/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.jsx index edd1a6a..894c096 100644 --- a/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.jsx @@ -21,6 +21,6 @@ function RayLine({ config, refs }) { ); } -RayLine.logic = new RayLineLogic(); +RayLine.logic = RayLineLogic; export default RayLine; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.logic.js b/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.logic.js index 989854e..53dfb44 100644 --- a/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.logic.js +++ b/src/frontend/ui/models/Particles/components/atoms/RayLine/RayLine.logic.js @@ -2,7 +2,7 @@ import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/ import { BufferGeometry } from "three"; export default class RayLineLogic extends ModelStrategy { - update({ objects, refs, target }) { + static update({ objects, refs, target }) { const { origin } = objects.raycaster.ray; const points = [origin, target]; const geometry = new BufferGeometry().setFromPoints(points); diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.js index ebac016..0307992 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.js +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.js @@ -6,7 +6,7 @@ import RaycasterLogic from "./Raycaster.logic"; const Raycaster = { - logic: new RaycasterLogic() + logic: RaycasterLogic }; export default Raycaster; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.logic.js b/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.logic.js index ffdad7e..49d8765 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.logic.js +++ b/src/frontend/ui/models/Particles/components/atoms/Raycaster/Raycaster.logic.js @@ -2,12 +2,12 @@ import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/ import { Vector2 } from "three"; export default class RaycasterLogic extends ModelStrategy { - setup({ config, data: { unit }, objects: { raycaster } }) { + static setup({ config, data: { unit }, objects: { raycaster } }) { const { animations: { chaos: { radius } } } = config; raycaster.params.Points.threshold = radius * unit; } - update({ objects, refs }) { + static update({ objects, refs }) { const { raycaster } = objects; const camera = refs.camera.current; const mouse = refs.mouse.current; diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx index 9708797..8fc5714 100644 --- a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx +++ b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx @@ -8,7 +8,6 @@ import { useEffect } from "react"; import { useFrame } from "@react-three/fiber"; import Particles from "../../atoms/Particles/Particles.jsx"; import { - setup, update, } from "@semantyk/frontend/ui/models/Particles/logic"; import { ParticlesManager } from "../../../logic/manager.js"; @@ -23,12 +22,8 @@ function ParticlesSystem(args) { ParticlesManager.handle('mouseMove', { event, ...args }); }; - const handleResize = (event) => { - ParticlesManager.handle('resize', { event, ...args }); - }; - - ParticlesManager.addAll({ handleMouseMove, handleResize }); - return () => ParticlesManager.removeAll({ handleMouseMove, handleResize }); + ParticlesManager.addAll({ handleMouseMove }); + return () => ParticlesManager.removeAll({ handleMouseMove }); }, [args]); useFrame(({ clock }) => { diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js index 9b84c08..6ddf60a 100644 --- a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js +++ b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js @@ -2,15 +2,15 @@ import { ParticlesManager } from "../../../logic/manager"; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export default class ParticlesSystemLogic extends ModelStrategy { - add({ handleResize }) { + static add({ handleResize }) { window.addEventListener("resize", handleResize); } - remove({ handleResize }) { + static remove({ handleResize }) { window.removeEventListener("resize", handleResize); } - setup(args) { + static setup(args) { ParticlesManager.setup('camera', args); ParticlesManager.setup('particles', args); ParticlesManager.setup('plane', args); @@ -20,4 +20,4 @@ export default class ParticlesSystemLogic extends ModelStrategy { update(args) { ParticlesManager.update("particles", args); } -} +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/manager.js b/src/frontend/ui/models/Particles/logic/manager.js index 4a9b033..522e326 100644 --- a/src/frontend/ui/models/Particles/logic/manager.js +++ b/src/frontend/ui/models/Particles/logic/manager.js @@ -15,27 +15,26 @@ export class ParticlesManager extends ModelManager { circle: Circle.logic, mouse: Mouse.logic, plane: Plane.logic, - particles: new ParticlesLogic(), // TODO: Remove hack to get logic to work - particlesSystem: new ParticlesSystemLogic(), // TODO: Remove hack to get logic to work + particles: ParticlesLogic, // TODO: Improve this fix + particlesSystem: ParticlesSystemLogic, // TODO: Improve this fix raycaster: Raycaster.logic, rayLine: RayLine.logic } static effects = { - chaos: new ChaosEffect(), - color: new ColorEffect(), - entropy: new EntropyEffect(), - position: new PositionEffect() + chaos: ChaosEffect, + color: ColorEffect, + entropy: EntropyEffect, + position: PositionEffect }; static handlers = { - mouseMove: Mouse.logic, - resize: Particles.logic + mouseMove: Mouse.logic }; static listeners = { mouse: Mouse.logic, - resize: new ParticlesSystemLogic() + resize: ParticlesSystemLogic }; static affect(item, ...args) { From dc1bc7f33d2588f41638f76a5f664fb7692f3c1b Mon Sep 17 00:00:00 2001 From: "Daniel B." Date: Fri, 14 Mar 2025 23:58:08 -0600 Subject: [PATCH 7/7] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Refactor:=20Finish=20r?= =?UTF-8?q?efactor=20on=20Particles=20Model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/404.svg | 9 +++++ src/app/layout.jsx | 2 +- .../molecules/Model/{index.jsx => Model.jsx} | 22 +++++++--- .../Particles/{index.jsx => Particles.jsx} | 10 +++-- .../{logic/manager.js => Particles.logic.js} | 25 ++---------- .../Particles/components/atoms/Box/Box.jsx | 4 +- .../components/atoms/Camera/Camera.jsx | 4 +- .../components/atoms/Circle/Circle.jsx | 4 +- .../components/atoms/Circle/Circle.logic.js | 6 +-- .../components/atoms/Mouse/Mouse.logic.js | 13 +++--- .../components/atoms/Mouse/updates/color.js | 11 ----- .../atoms/Mouse/updates/position.js | 11 ----- .../atoms/Particles/effects/index.js | 4 -- .../components/atoms/Plane/Plane.jsx | 4 +- .../components/atoms/RayLine/RayLine.jsx | 4 +- .../Particles/components/atoms/index.js | 2 +- .../molecules/Controls/Controls.jsx | 4 +- .../Particles/Particles.jsx | 0 .../Particles/Particles.logic.js | 15 ++----- .../Particles/effects/chaos.js | 0 .../Particles/effects/color.js | 6 +-- .../Particles/effects/entropy.js | 0 .../Particles/effects/flotation.js | 0 .../molecules/Particles/effects/index.js | 4 ++ .../Particles/effects/interpolation.js | 0 .../Particles/effects/position.js | 2 +- .../{atoms => molecules}/Particles/index.js | 1 - .../molecules/ParticlesSystem/index.js | 2 - .../Particles/components/molecules/index.js | 3 +- .../System/System.jsx} | 16 ++++---- .../System/System.logic.js} | 14 +------ .../components/organisms/System/index.js | 2 + .../Particles/components/organisms/index.js | 1 + .../ui/models/Particles/config/index.js | 6 +-- .../ui/models/Particles/hooks/useArgs.jsx | 6 +-- .../ui/models/Particles/logic/index.js | 40 ------------------- 36 files changed, 86 insertions(+), 171 deletions(-) create mode 100644 public/404.svg rename src/frontend/ui/components/molecules/Model/{index.jsx => Model.jsx} (71%) rename src/frontend/ui/models/Particles/{index.jsx => Particles.jsx} (82%) rename src/frontend/ui/models/Particles/{logic/manager.js => Particles.logic.js} (56%) delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/updates/color.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Mouse/updates/position.js delete mode 100644 src/frontend/ui/models/Particles/components/atoms/Particles/effects/index.js rename src/frontend/ui/models/Particles/components/{atoms => molecules}/Particles/Particles.jsx (100%) rename src/frontend/ui/models/Particles/components/{atoms => molecules}/Particles/Particles.logic.js (84%) rename src/frontend/ui/models/Particles/components/{atoms => molecules}/Particles/effects/chaos.js (100%) rename src/frontend/ui/models/Particles/components/{atoms => molecules}/Particles/effects/color.js (58%) rename src/frontend/ui/models/Particles/components/{atoms => molecules}/Particles/effects/entropy.js (100%) rename src/frontend/ui/models/Particles/components/{atoms => molecules}/Particles/effects/flotation.js (100%) create mode 100644 src/frontend/ui/models/Particles/components/molecules/Particles/effects/index.js rename src/frontend/ui/models/Particles/components/{atoms => molecules}/Particles/effects/interpolation.js (100%) rename src/frontend/ui/models/Particles/components/{atoms => molecules}/Particles/effects/position.js (94%) rename src/frontend/ui/models/Particles/components/{atoms => molecules}/Particles/index.js (99%) delete mode 100644 src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.js rename src/frontend/ui/models/Particles/components/{molecules/ParticlesSystem/ParticlesSystem.jsx => organisms/System/System.jsx} (64%) rename src/frontend/ui/models/Particles/components/{molecules/ParticlesSystem/ParticlesSystem.logic.js => organisms/System/System.logic.js} (53%) create mode 100644 src/frontend/ui/models/Particles/components/organisms/System/index.js create mode 100644 src/frontend/ui/models/Particles/components/organisms/index.js delete mode 100644 src/frontend/ui/models/Particles/logic/index.js diff --git a/public/404.svg b/public/404.svg new file mode 100644 index 0000000..d4bd90e --- /dev/null +++ b/public/404.svg @@ -0,0 +1,9 @@ + + + Media/Text Copia + + + 404 + + + \ No newline at end of file diff --git a/src/app/layout.jsx b/src/app/layout.jsx index 5251ab1..460c292 100644 --- a/src/app/layout.jsx +++ b/src/app/layout.jsx @@ -26,7 +26,7 @@ import Head from "@semantyk/frontend/ui/components/atoms/Head"; import Body from "@semantyk/frontend/ui/components/molecules/Body"; import { getLang } from "@semantyk/frontend/logic/services/getLang"; import Content from "@semantyk/frontend/ui/components/molecules/Content"; -import Model from "@semantyk/frontend/ui/components/molecules/Model"; +import Model from "@semantyk/frontend/ui/components/molecules/Model/Model"; //* Main diff --git a/src/frontend/ui/components/molecules/Model/index.jsx b/src/frontend/ui/components/molecules/Model/Model.jsx similarity index 71% rename from src/frontend/ui/components/molecules/Model/index.jsx rename to src/frontend/ui/components/molecules/Model/Model.jsx index e85efc4..aacf6d3 100644 --- a/src/frontend/ui/components/molecules/Model/index.jsx +++ b/src/frontend/ui/components/molecules/Model/Model.jsx @@ -22,18 +22,28 @@ import React from "react"; import Canvas from "@semantyk/frontend/ui/components/molecules/Canvas"; import GraphModel from "@semantyk/frontend/ui/models/Graph"; -import { usePathname } from "next/navigation"; -import ParticlesModel from "@semantyk/frontend/ui/models/Particles"; +import { usePathname, useRouter } from "next/navigation"; +import Particles from "@semantyk/frontend/ui/models/Particles/Particles"; //* Main export default function Model() { - // Logic + // Hooks const pathname = usePathname(); + // Logic + const model = () => { + switch (pathname) { + case "/": + return ; + case "/knowledge": + return ; + default: + return ; + } + }; // Return return ( - {pathname === "/" && } - {pathname === "/knowledge" && } + {model()} ); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/index.jsx b/src/frontend/ui/models/Particles/Particles.jsx similarity index 82% rename from src/frontend/ui/models/Particles/index.jsx rename to src/frontend/ui/models/Particles/Particles.jsx index f130bbd..ee68122 100644 --- a/src/frontend/ui/models/Particles/index.jsx +++ b/src/frontend/ui/models/Particles/Particles.jsx @@ -18,15 +18,17 @@ //* Imports import { useArgs } from "@semantyk/frontend/ui/models/Particles/hooks/useArgs"; -import { Controls, ParticlesSystem } from "./components/molecules"; +import { Controls } from "./components/molecules"; +import { System } from "./components/organisms"; + //* Main -export default function ParticlesModel() { +export default function Particles({ path }) { // Hooks - const args = useArgs(); + const args = useArgs({ path }); // Return return (<> - + ); } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/logic/manager.js b/src/frontend/ui/models/Particles/Particles.logic.js similarity index 56% rename from src/frontend/ui/models/Particles/logic/manager.js rename to src/frontend/ui/models/Particles/Particles.logic.js index 522e326..23edd40 100644 --- a/src/frontend/ui/models/Particles/logic/manager.js +++ b/src/frontend/ui/models/Particles/Particles.logic.js @@ -1,11 +1,6 @@ import { ModelManager } from '@semantyk/frontend/ui/components/molecules/Model/logic/manager'; -import { ChaosEffect } from '../components/atoms/Particles/effects/chaos'; -import { ColorEffect } from '../components/atoms/Particles/effects/color'; -import { EntropyEffect } from '../components/atoms/Particles/effects/entropy'; -import { PositionEffect } from '../components/atoms/Particles/effects/position'; -import { Camera, Circle, Mouse, Particles, Plane, Raycaster, RayLine } from '../components/atoms'; -import ParticlesSystemLogic from '../components/molecules/ParticlesSystem/ParticlesSystem.logic'; -import ParticlesLogic from '../components/atoms/Particles/Particles.logic'; +import { Camera, Circle, Mouse, Plane, Raycaster, RayLine } from './components/atoms'; +import ParticlesLogic from './components/molecules/Particles/Particles.logic'; /** * Manager class for particle strategies using the Strategy pattern */ @@ -16,32 +11,18 @@ export class ParticlesManager extends ModelManager { mouse: Mouse.logic, plane: Plane.logic, particles: ParticlesLogic, // TODO: Improve this fix - particlesSystem: ParticlesSystemLogic, // TODO: Improve this fix raycaster: Raycaster.logic, rayLine: RayLine.logic } - static effects = { - chaos: ChaosEffect, - color: ColorEffect, - entropy: EntropyEffect, - position: PositionEffect - }; - static handlers = { mouseMove: Mouse.logic }; static listeners = { - mouse: Mouse.logic, - resize: ParticlesSystemLogic + mouse: Mouse.logic }; - static affect(item, ...args) { - const collection = this.effects; - return super.execute(collection, item, 'execute', ...args); - } - static handle(item, ...args) { const collection = this.handlers; return super.execute(collection, item, 'handle', ...args); diff --git a/src/frontend/ui/models/Particles/components/atoms/Box/Box.jsx b/src/frontend/ui/models/Particles/components/atoms/Box/Box.jsx index 162fb2c..b8210d0 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Box/Box.jsx +++ b/src/frontend/ui/models/Particles/components/atoms/Box/Box.jsx @@ -6,10 +6,10 @@ //* Main export default function Box({ config, data, refs }) { // Props - const { general: { showHelpers } } = config; + const { general: { showControls } } = config; // Return return ( - + + diff --git a/src/frontend/ui/models/Particles/components/atoms/index.js b/src/frontend/ui/models/Particles/components/atoms/index.js index 49d0f0f..052689e 100644 --- a/src/frontend/ui/models/Particles/components/atoms/index.js +++ b/src/frontend/ui/models/Particles/components/atoms/index.js @@ -3,6 +3,6 @@ export * from './Camera'; export * from './Circle'; export * from './Mouse'; export * from './Plane'; -export * from './Particles'; +export * from '../molecules/Particles'; export * from './Raycaster'; export * from './RayLine'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/Controls/Controls.jsx b/src/frontend/ui/models/Particles/components/molecules/Controls/Controls.jsx index 2500829..bff466a 100644 --- a/src/frontend/ui/models/Particles/components/molecules/Controls/Controls.jsx +++ b/src/frontend/ui/models/Particles/components/molecules/Controls/Controls.jsx @@ -9,7 +9,7 @@ import Camera from "../../atoms/Camera/Camera"; //* Main export default function Controls(args) { // Props - const { general: { showHelpers } } = args.config; + const { general: { showControls } } = args.config; // Return return (<> @@ -17,6 +17,6 @@ export default function Controls(args) { - {showHelpers && } + {showControls && } ); } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.jsx b/src/frontend/ui/models/Particles/components/molecules/Particles/Particles.jsx similarity index 100% rename from src/frontend/ui/models/Particles/components/atoms/Particles/Particles.jsx rename to src/frontend/ui/models/Particles/components/molecules/Particles/Particles.jsx diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js b/src/frontend/ui/models/Particles/components/molecules/Particles/Particles.logic.js similarity index 84% rename from src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js rename to src/frontend/ui/models/Particles/components/molecules/Particles/Particles.logic.js index 4d562d3..fca5c5d 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/Particles.logic.js +++ b/src/frontend/ui/models/Particles/components/molecules/Particles/Particles.logic.js @@ -1,19 +1,10 @@ import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; import { getImageData } from "../../../utils/image"; import { Float32BufferAttribute } from "three"; -import { ParticlesManager } from "../../../logic/manager"; +import { ParticlesManager } from "../../../Particles.logic"; import { ColorEffect, PositionEffect } from "./effects"; export default class ParticlesLogic extends ModelStrategy { - static handle({ event, ...args }) { - const { particles } = args.refs; - ParticlesManager.setup('camera', args); - const { particle } = args.config; - const ratio = window.innerWidth / window.innerHeight; - const size = Math.min(Math.max(particle.size * ratio, 0), particle.size); - particles.current.material.size = size; - } - static setup({ config, data: { color, unit }, objects: { image }, refs }) { const { particle } = config; const particles = refs.particles.current; @@ -75,11 +66,11 @@ export default class ParticlesLogic extends ModelStrategy { const idxs = new Set(intersects.map(({ index }) => index)); for (let i = 0; i < args.refs.particles.current.data.count; i++) { - ColorEffect.execute({ i, ...args }); + // ColorEffect.execute({ i, ...args }); PositionEffect.execute({ i, idxs, ...args }); } - args.refs.particles.current.geometry.attributes.color.needsUpdate = true; + // args.refs.particles.current.geometry.attributes.color.needsUpdate = true; args.refs.particles.current.geometry.attributes.position.needsUpdate = true; } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/chaos.js similarity index 100% rename from src/frontend/ui/models/Particles/components/atoms/Particles/effects/chaos.js rename to src/frontend/ui/models/Particles/components/molecules/Particles/effects/chaos.js diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/color.js similarity index 58% rename from src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js rename to src/frontend/ui/models/Particles/components/molecules/Particles/effects/color.js index e48df30..a81d467 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/color.js +++ b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/color.js @@ -2,11 +2,11 @@ import { Color } from 'three'; import { ModelStrategy } from '@semantyk/frontend/ui/components/molecules/Model/logic/strategy'; export class ColorEffect extends ModelStrategy { - static execute({ data: { color }, i, ...args }) { - const chaoticValue = args.refs.particles.current.data.chaotic[i]; + static execute({ data: { color }, i, refs: { particles } }) { + const chaoticValue = particles.current.data.chaotic[i]; const final = color.clone(); const target = new Color(1, 0, 0); final.lerp(target, chaoticValue); - args.refs.particles.current.geometry.attributes.color.set(final.toArray(), i * 3); + particles.current.geometry.attributes.color.set(final.toArray(), i * 3); } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/entropy.js similarity index 100% rename from src/frontend/ui/models/Particles/components/atoms/Particles/effects/entropy.js rename to src/frontend/ui/models/Particles/components/molecules/Particles/effects/entropy.js diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/flotation.js similarity index 100% rename from src/frontend/ui/models/Particles/components/atoms/Particles/effects/flotation.js rename to src/frontend/ui/models/Particles/components/molecules/Particles/effects/flotation.js diff --git a/src/frontend/ui/models/Particles/components/molecules/Particles/effects/index.js b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/index.js new file mode 100644 index 0000000..3ad4338 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/index.js @@ -0,0 +1,4 @@ +export { ChaosEffect } from './chaos'; +export { EntropyEffect } from './entropy'; +export { PositionEffect } from './position'; +export { ColorEffect } from './color'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/interpolation.js similarity index 100% rename from src/frontend/ui/models/Particles/components/atoms/Particles/effects/interpolation.js rename to src/frontend/ui/models/Particles/components/molecules/Particles/effects/interpolation.js diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/position.js similarity index 94% rename from src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js rename to src/frontend/ui/models/Particles/components/molecules/Particles/effects/position.js index 0cea872..273ab23 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/effects/position.js +++ b/src/frontend/ui/models/Particles/components/molecules/Particles/effects/position.js @@ -6,7 +6,7 @@ import { Vector3 } from 'three'; import { ChaosEffect } from "./chaos"; export class PositionEffect extends ModelStrategy { - static execute({ i, idxs, ...args }) { + static execute({ object, i, idxs, ...args }) { const { particles } = args.refs; const final = new Vector3() diff --git a/src/frontend/ui/models/Particles/components/atoms/Particles/index.js b/src/frontend/ui/models/Particles/components/molecules/Particles/index.js similarity index 99% rename from src/frontend/ui/models/Particles/components/atoms/Particles/index.js rename to src/frontend/ui/models/Particles/components/molecules/Particles/index.js index 64fa614..0a7f788 100644 --- a/src/frontend/ui/models/Particles/components/atoms/Particles/index.js +++ b/src/frontend/ui/models/Particles/components/molecules/Particles/index.js @@ -1,4 +1,3 @@ export * from './effects'; - export { default } from './Particles'; export { default as Particles } from './Particles'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.js b/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.js deleted file mode 100644 index cc9fa22..0000000 --- a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './ParticlesSystem'; -export { default as ParticlesSystem } from './ParticlesSystem'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/index.js b/src/frontend/ui/models/Particles/components/molecules/index.js index 2365e40..4d69a19 100644 --- a/src/frontend/ui/models/Particles/components/molecules/index.js +++ b/src/frontend/ui/models/Particles/components/molecules/index.js @@ -1,2 +1 @@ -export * from './Controls'; -export * from './ParticlesSystem'; \ No newline at end of file +export * from './Controls'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx b/src/frontend/ui/models/Particles/components/organisms/System/System.jsx similarity index 64% rename from src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx rename to src/frontend/ui/models/Particles/components/organisms/System/System.jsx index 8fc5714..35916cb 100644 --- a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.jsx +++ b/src/frontend/ui/models/Particles/components/organisms/System/System.jsx @@ -6,17 +6,15 @@ //* Imports import { useEffect } from "react"; import { useFrame } from "@react-three/fiber"; -import Particles from "../../atoms/Particles/Particles.jsx"; -import { - update, -} from "@semantyk/frontend/ui/models/Particles/logic"; -import { ParticlesManager } from "../../../logic/manager.js"; +import Particles from "../../molecules/Particles/Particles.jsx"; +import { ParticlesManager } from "../../../Particles.logic.js"; +import ParticlesSystemLogic from "./System.logic.js"; //* Main -function ParticlesSystem(args) { +function System(args) { // Logic useEffect(() => { - ParticlesManager.setup('particlesSystem', args); + ParticlesSystemLogic.setup(args); const handleMouseMove = (event) => { ParticlesManager.handle('mouseMove', { event, ...args }); @@ -28,10 +26,10 @@ function ParticlesSystem(args) { useFrame(({ clock }) => { args.objects.clock.current = clock; - update(args); + ParticlesManager.update("particles", args); }); return ; } -export default ParticlesSystem; \ No newline at end of file +export default System; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js b/src/frontend/ui/models/Particles/components/organisms/System/System.logic.js similarity index 53% rename from src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js rename to src/frontend/ui/models/Particles/components/organisms/System/System.logic.js index 6ddf60a..5b15dc8 100644 --- a/src/frontend/ui/models/Particles/components/molecules/ParticlesSystem/ParticlesSystem.logic.js +++ b/src/frontend/ui/models/Particles/components/organisms/System/System.logic.js @@ -1,23 +1,11 @@ -import { ParticlesManager } from "../../../logic/manager"; +import { ParticlesManager } from "../../../Particles.logic"; import { ModelStrategy } from "@semantyk/frontend/ui/components/molecules/Model/logic/strategy"; export default class ParticlesSystemLogic extends ModelStrategy { - static add({ handleResize }) { - window.addEventListener("resize", handleResize); - } - - static remove({ handleResize }) { - window.removeEventListener("resize", handleResize); - } - static setup(args) { ParticlesManager.setup('camera', args); ParticlesManager.setup('particles', args); ParticlesManager.setup('plane', args); ParticlesManager.setup('raycaster', args); } - - update(args) { - ParticlesManager.update("particles", args); - } } \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/organisms/System/index.js b/src/frontend/ui/models/Particles/components/organisms/System/index.js new file mode 100644 index 0000000..5c3ae48 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/organisms/System/index.js @@ -0,0 +1,2 @@ +export { default } from './System'; +export { default as System } from './System'; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/components/organisms/index.js b/src/frontend/ui/models/Particles/components/organisms/index.js new file mode 100644 index 0000000..4cc07e7 --- /dev/null +++ b/src/frontend/ui/models/Particles/components/organisms/index.js @@ -0,0 +1 @@ +export * from "./System"; \ No newline at end of file diff --git a/src/frontend/ui/models/Particles/config/index.js b/src/frontend/ui/models/Particles/config/index.js index 51d778e..6b4e4cd 100644 --- a/src/frontend/ui/models/Particles/config/index.js +++ b/src/frontend/ui/models/Particles/config/index.js @@ -20,7 +20,7 @@ export const config = { // General general: { - showHelpers: true, + showControls: false, scale: 1, size: 150, }, @@ -49,10 +49,6 @@ export const config = { duration: 5 } }, - // Image - image: { - path: "/favicon.png" - }, // Particles particle: { density: 1, diff --git a/src/frontend/ui/models/Particles/hooks/useArgs.jsx b/src/frontend/ui/models/Particles/hooks/useArgs.jsx index 5f1b224..f52fc11 100644 --- a/src/frontend/ui/models/Particles/hooks/useArgs.jsx +++ b/src/frontend/ui/models/Particles/hooks/useArgs.jsx @@ -23,9 +23,9 @@ import { config } from "@semantyk/frontend/ui/models/Particles/config"; import useColorScheme from "@semantyk/frontend/hooks/useColorScheme"; //* Main -export function useArgs() { +export function useArgs({ path }) { // Props - const { general: { scale, size }, image: { path } } = config; + const { general: { scale, size } } = config; // Hooks const { colorScheme } = useColorScheme(); const { image } = useLoader(TextureLoader, path); @@ -53,7 +53,7 @@ export function useArgs() { camera: useRef(), circle: useRef(), mouse: useRef({ current: { x: 0, y: 0, isMoving: false } }), - moveMouseTimeout: useRef(null), + moveMouseTimeout: useRef(), particles: useRef(), plane: useRef(), rayLine: useRef(), diff --git a/src/frontend/ui/models/Particles/logic/index.js b/src/frontend/ui/models/Particles/logic/index.js deleted file mode 100644 index 1ba1147..0000000 --- a/src/frontend/ui/models/Particles/logic/index.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - * # `logic.js` - * @organization: Semantyk - * @project: Client - * - * @created: Jul 17, 2024 - * @modified: Mar 7, 2025 - * - * @author: Semantyk Team - * @maintainer: Daniel Bakas - * - * @copyright: Semantyk © 2025. All rights reserved. - * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– - */ - -//* Imports -import { Vector3 } from "three"; -//* Local Imports -import { ParticlesManager } from "./manager"; - -//* Main -export function setup(args) { - ParticlesManager.setup('camera', args); - ParticlesManager.setup('particles', args); - ParticlesManager.setup('plane', args); - ParticlesManager.setup('raycaster', args); -} - -export function update(args) { - ParticlesManager.update("particles", args); -} - -export function updateOnMouseMove(args) { - const target = new Vector3() - ParticlesManager.update("circle", { target, ...args }); - ParticlesManager.update("rayLine", { target, ...args }); - ParticlesManager.update("mouse", args); - ParticlesManager.update("raycaster", args); -} \ No newline at end of file