Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/firebird-ng/src/app/animation/default-animations.ts is written in an unsupported language. File is not indexed.

0001 import { Easing, Tween } from '@tweenjs/tween.js';
0002 import {AnimationTask} from "./animation-manager";
0003 import {
0004   BufferAttribute,
0005   BufferGeometry,
0006   Camera,
0007   Color,
0008   Mesh,
0009   MeshBasicMaterial, Plane,
0010   Scene,
0011   Sphere,
0012   SphereGeometry,
0013   TubeGeometry,
0014   Vector3, WebGLRenderer
0015 } from "three";
0016 
0017 export class CameraMoveTask implements AnimationTask {
0018   name = 'CameraMove';
0019   duration: number;
0020   private currentTween: Tween<Vector3> | null = null;
0021 
0022   constructor(
0023     private camera: Camera,
0024     private targetPosition: Vector3,
0025     duration: number,
0026     private easing?: (amount: number) => number
0027   ) {
0028     this.duration = duration;
0029   }
0030 
0031   async start(): Promise<void> {
0032     return new Promise((resolve) => {
0033       this.currentTween = new Tween(this.camera.position)
0034         .to(this.targetPosition, this.duration)
0035         .onComplete(() => {
0036           this.currentTween = null;
0037           resolve();
0038         })
0039         .start();
0040 
0041       if (this.easing) {
0042         this.currentTween.easing(this.easing);
0043       }
0044     });
0045   }
0046 
0047   stop?(): void {
0048     if (this.currentTween) {
0049       this.currentTween.stop();
0050       this.currentTween = null;
0051     }
0052   }
0053 
0054   pause?(): void {
0055     if (this.currentTween) {
0056       this.currentTween.pause();
0057     }
0058   }
0059 
0060   resume?(): void {
0061     if (this.currentTween) {
0062       this.currentTween.resume();
0063     }
0064   }
0065 }
0066 
0067 // Concrete Animation Task: ParticleCollisionTask
0068 export class ParticleCollisionTask implements AnimationTask {
0069   name = 'ParticleCollision';
0070   duration: number;
0071 
0072   constructor(
0073     private scene: Scene,
0074     duration: number,
0075     private particleSize: number = 10,
0076     private distanceFromOrigin: number = 5000,
0077     private particleColor: Color = new Color(0xffffff)
0078   ) {
0079     this.duration = duration;
0080   }
0081 
0082   async start(): Promise<void> {
0083     return new Promise((resolve) => {
0084       const electronGeometry = new SphereGeometry(
0085         0.5 * this.particleSize,
0086         32,
0087         32
0088       );
0089       const electronMaterial = new MeshBasicMaterial({
0090         color: 0x0000ff,
0091         transparent: true,
0092         opacity: 0,
0093       });
0094       const electron = new Mesh(electronGeometry, electronMaterial);
0095 
0096       const ionMaterial = new MeshBasicMaterial({
0097         color: 0xff0000,
0098         transparent: true,
0099         opacity: 0,
0100       });
0101       const ionGeometry = new SphereGeometry(2 * this.particleSize, 32, 32);
0102       const ion = new Mesh(ionGeometry, ionMaterial);
0103 
0104       electron.position.setZ(this.distanceFromOrigin);
0105       ion.position.setZ(-this.distanceFromOrigin);
0106 
0107       const particles = [electron, ion];
0108 
0109       this.scene.add(...particles);
0110 
0111       const particleTweens = [];
0112 
0113       for (const particle of particles) {
0114         new Tween(particle.material)
0115           .to(
0116             {
0117               opacity: 1,
0118             },
0119             300
0120           )
0121           .start();
0122 
0123         const particleToOrigin = new Tween(particle.position)
0124           .to(
0125             {
0126               z: 0,
0127             },
0128             this.duration
0129           )
0130           .start();
0131 
0132         particleTweens.push(particleToOrigin);
0133       }
0134 
0135       particleTweens[0].onComplete(() => {
0136         this.scene.remove(...particles);
0137         resolve();
0138       });
0139     });
0140   }
0141 }
0142 
0143 // Animation Task: AnimateThroughEventTask
0144 export class AnimateThroughEventTask implements AnimationTask {
0145   name = 'AnimateThroughEvent';
0146   duration: number;
0147   private startPos: number[];
0148   private tweens: Tween<any>[] = [];
0149   private onAnimationEnd?: () => void;
0150 
0151   constructor(
0152     private camera: Camera,
0153     startPos: number[],
0154     tweenDuration: number,
0155     onAnimationEnd?: () => void
0156   ) {
0157     this.startPos = startPos;
0158     this.duration = tweenDuration;
0159     this.onAnimationEnd = onAnimationEnd;
0160   }
0161 
0162   async start(): Promise<void> {
0163     return new Promise<void>((resolve) => {
0164       // Move to start
0165       const start = this.getCameraTween(this.startPos, 1000, Easing.Cubic.Out);
0166       // Move to position along the detector axis
0167       const alongAxisPosition = [0, 0, this.startPos[2]];
0168       const startXAxis = this.getCameraTween(alongAxisPosition, this.duration);
0169 
0170       const radius = 500;
0171       const numOfSteps = 24;
0172       const angle = 3 * Math.PI;
0173       const step = angle / numOfSteps;
0174 
0175       const rotationPositions = [];
0176       for (let i = 1; i <= numOfSteps; i++) {
0177         rotationPositions.push([
0178           radius * Math.sin(step * i), // x
0179           0, // y
0180           radius * Math.cos(step * i), // z
0181         ]);
0182       }
0183 
0184       // Go to origin
0185       const rotateStart = this.getCameraTween(
0186         [0, 0, radius],
0187         this.duration,
0188         Easing.Cubic.Out
0189       );
0190 
0191       let rotate = rotateStart;
0192       const rotationTime = this.duration * 4;
0193       const singleRotationTime = rotationTime / numOfSteps;
0194       // Rotating around the event
0195       for (const pos of rotationPositions) {
0196         const animation = this.getCameraTween(pos, singleRotationTime);
0197         rotate.chain(animation);
0198         rotate = animation;
0199       }
0200 
0201       // Go to the end position and then back to the starting point
0202       const endPos = [0, 0, -this.startPos[2]];
0203       const end = this.getCameraTween(endPos, this.duration, Easing.Cubic.In);
0204       const startClone = this.getCameraTween(
0205         this.startPos,
0206         this.duration,
0207         Easing.Cubic.Out
0208       );
0209       startClone.onComplete(() => {
0210         this.onAnimationEnd?.();
0211         resolve();
0212       });
0213       startClone.delay(500);
0214 
0215       start.chain(startXAxis);
0216       startXAxis.chain(rotateStart);
0217       rotate.chain(end);
0218       end.chain(startClone);
0219 
0220       this.tweens = [start, startXAxis, rotateStart, ...rotate['_chainedTweens'], end, startClone];
0221       start.start();
0222     });
0223   }
0224 
0225   stop?(): void {
0226     for (const tween of this.tweens) {
0227       tween.stop();
0228     }
0229   }
0230 
0231   pause?(): void {
0232     for (const tween of this.tweens) {
0233       tween.pause();
0234     }
0235   }
0236 
0237   resume?(): void {
0238     for (const tween of this.tweens) {
0239       tween.resume();
0240     }
0241   }
0242 
0243   private getCameraTween(
0244     pos: number[],
0245     duration: number = 1000,
0246     easing?: typeof Easing.Linear.None
0247   ) {
0248     const tween = new Tween(this.camera.position).to(
0249       { x: pos[0], y: pos[1], z: pos[2] },
0250       duration
0251     );
0252 
0253     if (easing) {
0254       tween.easing(easing);
0255     }
0256 
0257     return tween;
0258   }
0259 }
0260 
0261 // Animation Task: AnimateEventTask
0262 export class AnimateEventTask implements AnimationTask {
0263   name = 'AnimateEvent';
0264   duration: number;
0265   private onEnd?: () => void;
0266   private onAnimationStart?: () => void;
0267   private animationSphere: Sphere;
0268   private animationSphereTween: Tween<Sphere>;
0269   private animationSphereTweenClone: Tween<Sphere>;
0270   private eventObjectTweens: Tween<any>[] = [];
0271 
0272   constructor(
0273     private scene: Scene,
0274     private EVENT_DATA_ID: string,
0275     tweenDuration: number,
0276     onEnd?: () => void,
0277     onAnimationStart?: () => void
0278   ) {
0279     this.duration = tweenDuration;
0280     this.onEnd = onEnd;
0281     this.onAnimationStart = onAnimationStart;
0282     this.animationSphere = new Sphere(new Vector3(), 0);
0283     this.animationSphereTween = new Tween(this.animationSphere)
0284       .to({ radius: 3000 }, this.duration * 0.75)
0285       .onUpdate(() => this.onAnimationSphereUpdate()); // Remove extra parameter
0286     this.animationSphereTweenClone = new Tween(this.animationSphere)
0287       .to({ radius: 10000 }, this.duration * 0.25)
0288       .onUpdate(() => this.onAnimationSphereUpdate()); // Remove extra parameter
0289     this.animationSphereTween.chain(this.animationSphereTweenClone);
0290   }
0291 
0292   async start(): Promise<void> {
0293     return new Promise<void>((resolve) => {
0294       const eventData = this.scene.getObjectByName(this.EVENT_DATA_ID);
0295       if (!eventData) {
0296         console.error(
0297           'this.scene.getObjectByName(this.EVENT_DATA_ID) returned null or undefined'
0298         );
0299         resolve();
0300         return;
0301       }
0302 
0303       // Traverse over all event data
0304       eventData.traverse((eventObject: any) => {
0305         if (eventObject.geometry) {
0306           // Animation for extrapolating tracks without changing scale
0307           if (
0308             eventObject.name === 'Track' ||
0309             eventObject.name === 'LineHit'
0310           ) {
0311             // Check if geometry drawRange count exists
0312             let geometryPosCount =
0313               eventObject.geometry?.attributes?.position?.count;
0314             if (geometryPosCount) {
0315               // WORKAROUND
0316               // Changing position count for TubeGeometry because
0317               // what we get is not the actual and it has Infinity drawRange count
0318               if (eventObject.geometry instanceof TubeGeometry) {
0319                 geometryPosCount *= 6;
0320               }
0321 
0322               if (eventObject.geometry instanceof BufferGeometry) {
0323                 const oldDrawRangeCount = eventObject.geometry.drawRange.count;
0324                 eventObject.geometry.setDrawRange(0, 0);
0325                 const eventObjectTween = new Tween(
0326                   eventObject.geometry.drawRange
0327                 )
0328                   .to(
0329                     {
0330                       count: geometryPosCount,
0331                     },
0332                     this.duration * 0.75
0333                   )
0334                   .onComplete(() => {
0335                     eventObject.geometry.drawRange.count = oldDrawRangeCount;
0336                   });
0337                 this.eventObjectTweens.push(eventObjectTween);
0338               }
0339             }
0340           }
0341         }
0342       });
0343 
0344       this.eventObjectTweens[0]?.onStart(() => this.onAnimationStart?.());
0345       this.animationSphereTweenClone.onComplete(() => {
0346         // Remove this line
0347         // this.onAnimationSphereUpdate(new Sphere(new Vector3(), Infinity));
0348         this.onEnd?.();
0349         resolve();
0350       });
0351 
0352       for (const tween of this.eventObjectTweens) {
0353         tween.easing(Easing.Quartic.Out).start();
0354       }
0355       this.animationSphereTween.start();
0356     });
0357   }
0358 
0359   private onAnimationSphereUpdate() {
0360     // Now empty. Remove this function if not required.
0361   }
0362 
0363   stop?(): void {
0364     this.animationSphereTween.stop();
0365     this.animationSphereTweenClone.stop();
0366     for (const tween of this.eventObjectTweens) {
0367       tween.stop();
0368     }
0369   }
0370 
0371   pause?(): void {
0372     this.animationSphereTween.pause();
0373     this.animationSphereTweenClone.pause();
0374     for (const tween of this.eventObjectTweens) {
0375       tween.pause();
0376     }
0377   }
0378 
0379   resume?(): void {
0380     this.animationSphereTween.resume();
0381     this.animationSphereTweenClone.resume();
0382     for (const tween of this.eventObjectTweens) {
0383       tween.resume();
0384     }
0385   }
0386 }
0387 
0388 // Animation Task: AnimateEventWithClippingTask
0389 export class AnimateEventWithClippingTask implements AnimationTask {
0390   name = 'AnimateEventWithClipping';
0391   duration: number;
0392   private onEnd?: () => void;
0393   private onAnimationStart?: () => void;
0394   private clippingConstant: number;
0395   private animationClipPlanes: Plane[] = [];
0396   private allTweens: Tween<any>[] = [];
0397 
0398   constructor(
0399     private scene: Scene,
0400     private renderer: WebGLRenderer,
0401     private EVENT_DATA_ID: string,
0402     tweenDuration: number,
0403     onEnd?: () => void,
0404     onAnimationStart?: () => void,
0405     clippingConstant: number = 11000
0406   ) {
0407     this.duration = tweenDuration;
0408     this.onEnd = onEnd;
0409     this.onAnimationStart = onAnimationStart;
0410     this.clippingConstant = clippingConstant;
0411   }
0412 
0413   async start(): Promise<void> {
0414     return new Promise<void>((resolve) => {
0415       const allEventData = this.scene.getObjectByName(this.EVENT_DATA_ID);
0416       if (!allEventData) {
0417         console.error(
0418           'this.scene.getObjectByName(this.EVENT_DATA_ID) returned null or undefined'
0419         );
0420         resolve();
0421         return;
0422       }
0423 
0424       const sphere = new SphereGeometry(1, 8, 8);
0425       const position = sphere.attributes['position'];
0426       const vertex = new Vector3();
0427       for (let i = 0; i < position.count; i++) {
0428         vertex.fromBufferAttribute(position as BufferAttribute, i);
0429         this.animationClipPlanes.push(new Plane(vertex.clone(), 0));
0430       }
0431 
0432       const prevLocalClipping = this.renderer.localClippingEnabled;
0433       if (!prevLocalClipping) {
0434         this.renderer.localClippingEnabled = true;
0435       }
0436 
0437       allEventData.traverse((eventObject: any) => {
0438         if (eventObject.geometry && eventObject.material) {
0439           eventObject.material.clippingPlanes = this.animationClipPlanes;
0440         }
0441       });
0442 
0443       for (const animationClipPlane of this.animationClipPlanes) {
0444         animationClipPlane.constant = 0;
0445         const tween = new Tween(animationClipPlane)
0446           .to({ constant: this.clippingConstant }, this.duration)
0447           .onComplete(() => {
0448             if (animationClipPlane === this.animationClipPlanes[this.animationClipPlanes.length - 1]) {
0449               if (!prevLocalClipping) {
0450                 this.renderer.localClippingEnabled = false;
0451               }
0452               allEventData.traverse((eventObject: any) => {
0453                 if (eventObject.geometry && eventObject.material) {
0454                   eventObject.material.clippingPlanes = null;
0455                 }
0456               });
0457               this.onEnd?.();
0458               resolve();
0459             }
0460           });
0461         this.allTweens.push(tween);
0462       }
0463 
0464       this.allTweens[0].onStart(() => this.onAnimationStart?.());
0465 
0466       for (const tween of this.allTweens) {
0467         tween.start();
0468       }
0469     });
0470   }
0471 
0472   stop?(): void {
0473     for (const tween of this.allTweens) {
0474       tween.stop();
0475     }
0476   }
0477 
0478   pause?(): void {
0479     for (const tween of this.allTweens) {
0480       tween.pause();
0481     }
0482   }
0483 
0484   resume?(): void {
0485     for (const tween of this.allTweens) {
0486       tween.resume();
0487     }
0488   }
0489 }
0490 
0491 // Animation Sequence Class
0492 export class AnimationSequence {
0493   private tasks: AnimationTask[] = [];
0494   private currentTaskIndex: number = 0;
0495   private isPlaying: boolean = false;
0496   private isPaused: boolean = false;
0497 
0498   constructor(public name: string) {}
0499 
0500   addTask(task: AnimationTask): AnimationSequence {
0501     this.tasks.push(task);
0502     return this;
0503   }
0504 
0505   async run(): Promise<void> {
0506     if (this.isPlaying) {
0507       return; // Already running
0508     }
0509 
0510     this.isPlaying = true;
0511     this.isPaused = false;
0512 
0513     if (this.currentTaskIndex >= this.tasks.length) {
0514       this.currentTaskIndex = 0; // Reset if we've reached the end
0515     }
0516 
0517     for (
0518       let i = this.currentTaskIndex;
0519       i < this.tasks.length && this.isPlaying;
0520       i++
0521     ) {
0522       const task = this.tasks[i];
0523       this.currentTaskIndex = i;
0524       console.log(`Starting animation task: ${task.name}`);
0525 
0526       if (this.isPaused) {
0527         console.log(`Animation sequence paused at task: ${task.name}`);
0528         await this.waitForResume();
0529       }
0530 
0531       if (!this.isPlaying) {
0532         console.log(
0533           `Animation sequence stopped during task: ${task.name}`
0534         );
0535         break; // Exit loop if stopped
0536       }
0537 
0538       await task.start();
0539       console.log(`Finished animation task: ${task.name}`);
0540     }
0541 
0542     this.isPlaying = false;
0543     this.isPaused = false;
0544     this.currentTaskIndex = 0;
0545   }
0546 
0547   stop(): void {
0548     if (!this.isPlaying) return;
0549 
0550     this.isPlaying = false;
0551     this.isPaused = false;
0552     // Stop the currently running task
0553     const currentTask = this.tasks[this.currentTaskIndex];
0554     if (currentTask && currentTask.stop) {
0555       currentTask.stop();
0556     }
0557   }
0558 
0559   pause(): void {
0560     if (!this.isPlaying || this.isPaused) return;
0561 
0562     this.isPaused = true;
0563     // Pause the currently running task
0564     const currentTask = this.tasks[this.currentTaskIndex];
0565     if (currentTask && currentTask.pause) {
0566       currentTask.pause();
0567     }
0568   }
0569 
0570   resume(): void {
0571     if (!this.isPaused) return;
0572 
0573     this.isPaused = false;
0574     // Resume the currently paused task
0575     const currentTask = this.tasks[this.currentTaskIndex];
0576     if (currentTask && currentTask.resume) {
0577       currentTask.resume();
0578     }
0579   }
0580 
0581   private waitForResume(): Promise<void> {
0582     return new Promise((resolve) => {
0583       const checkResume = () => {
0584         if (!this.isPaused) {
0585           resolve();
0586         } else {
0587           setTimeout(checkResume, 100);
0588         }
0589       };
0590       checkResume();
0591     });
0592   }
0593 }