UNPKG

ngx-acuw

Version:

Angular components using WEBGL (threejs)

1 lines 186 kB
{"version":3,"file":"ngx-acuw.mjs","sources":["../../../projects/ngx-acuw/src/lib/image-as-particles/scripts/touch-texture.ts","../../../projects/ngx-acuw/src/lib/image-as-particles/scripts/shaders.ts","../../../projects/ngx-acuw/src/lib/tween/rxjs-tween.ts","../../../projects/ngx-acuw/src/lib/performance-monitor/performance-monitor.component.ts","../../../projects/ngx-acuw/src/lib/performance-monitor/performance-monitor.component.html","../../../projects/ngx-acuw/src/lib/image-as-particles/image-as-particles.component.ts","../../../projects/ngx-acuw/src/lib/performance-monitor/performance-monitor.module.ts","../../../projects/ngx-acuw/src/lib/image-as-particles/image-as-particles.module.ts","../../../projects/ngx-acuw/src/lib/image-transition/interfaces/direction.ts","../../../projects/ngx-acuw/src/lib/image-transition/shaders/imageTransitionShaders.ts","../../../projects/ngx-acuw/src/lib/image-transition/image-transition.component.ts","../../../projects/ngx-acuw/src/lib/image-transition/image-transition.component.html","../../../projects/ngx-acuw/src/lib/image-transition/image-transition.module.ts","../../../projects/ngx-acuw/src/lib/lightbox/lightbox-overlay.component.ts","../../../projects/ngx-acuw/src/lib/lightbox/lightbox-overlay.component.html","../../../projects/ngx-acuw/src/lib/lightbox/lightbox.component.ts","../../../projects/ngx-acuw/src/lib/lightbox/lightbox.module.ts","../../../projects/ngx-acuw/src/lib/controls/object-controls.ts","../../../projects/ngx-acuw/src/lib/carousel/carousel.component.ts","../../../projects/ngx-acuw/src/lib/carousel/carousel.module.ts","../../../projects/ngx-acuw/src/public-api.ts","../../../projects/ngx-acuw/src/ngx-acuw.ts"],"sourcesContent":["import { Texture } from 'three';\r\nimport { Trail } from './trail';\r\n\r\nexport class TouchTexture {\r\n // Variables\r\n private size: number;\r\n private maxAge: number;\r\n private radius: number;\r\n private trail: Trail[];\r\n private canvas!: HTMLCanvasElement;\r\n private ctx!: CanvasRenderingContext2D;\r\n public texture!: Texture;\r\n\r\n constructor() {\r\n this.size = 64;\r\n this.maxAge = 120;\r\n this.radius = 0.15;\r\n this.trail = new Array<Trail>();\r\n\r\n this.initTexture();\r\n }\r\n\r\n /**\r\n * Initializes the texture for the touch area\r\n */\r\n initTexture(): void {\r\n this.canvas = document.createElement('canvas');\r\n this.canvas.width = this.canvas.height = this.size;\r\n this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;\r\n this.ctx.fillStyle = 'black';\r\n this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\r\n\r\n this.texture = new Texture(this.canvas);\r\n\r\n this.canvas.id = 'touchTexture';\r\n this.canvas.style.width = this.canvas.style.height = `${this.canvas.width}px`;\r\n }\r\n\r\n /**\r\n * Updates the trail\r\n */\r\n update(): void {\r\n this.clear();\r\n\r\n // age points\r\n this.trail.forEach((point, i) => {\r\n point.age++;\r\n // remove old\r\n if (point.age > this.maxAge) {\r\n this.trail.splice(i, 1);\r\n }\r\n });\r\n\r\n this.trail.forEach((point, i) => {\r\n this.drawTouch(point);\r\n });\r\n\r\n this.texture.needsUpdate = true;\r\n }\r\n\r\n clear(): void {\r\n this.ctx.fillStyle = 'black';\r\n this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\r\n }\r\n\r\n addTouch(px: number, py: number): void {\r\n let force = 0;\r\n const last = this.trail[this.trail.length - 1];\r\n if (last) {\r\n const dx = last.x - px;\r\n const dy = last.y - py;\r\n const dd = dx * dx + dy * dy;\r\n force = Math.min(dd * 10000, 1);\r\n }\r\n this.trail.push({ x: px, y: py, age: 0, force });\r\n }\r\n\r\n drawTouch(point: Trail): void {\r\n const pos = {\r\n x: point.x * this.size,\r\n y: (1 - point.y) * this.size\r\n };\r\n\r\n let intensity = 1;\r\n if (point.age < this.maxAge * 0.3) {\r\n intensity = this.easeOutSine(point.age / (this.maxAge * 0.3), 0, 1, 1);\r\n } else {\r\n intensity = this.easeOutSine(1 - (point.age - this.maxAge * 0.3) / (this.maxAge * 0.7), 0, 1, 1);\r\n }\r\n\r\n intensity *= point.force;\r\n\r\n const radius = this.size * this.radius * intensity;\r\n const grd = this.ctx.createRadialGradient(pos.x, pos.y, radius * 0.25, pos.x, pos.y, radius);\r\n grd.addColorStop(0, `rgba(255, 255, 255, 0.2)`);\r\n grd.addColorStop(1, 'rgba(0, 0, 0, 0.0)');\r\n\r\n this.ctx.beginPath();\r\n this.ctx.fillStyle = grd;\r\n this.ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);\r\n this.ctx.fill();\r\n }\r\n\r\n private easeOutSine(t: number, b: number, c: number, d: number): number {\r\n return c * Math.sin(t / d * (Math.PI / 2)) + b;\r\n }\r\n}\r\n","export class Shaders {\r\n particleVertex = `\r\n // @author brunoimbrizi / http://brunoimbrizi.com\r\n\r\n precision highp float;\r\n #define GLSLIFY 1\r\n\r\n attribute float pindex;\r\n attribute vec3 position;\r\n attribute vec3 offset;\r\n attribute vec2 uv;\r\n attribute float angle;\r\n\r\n uniform mat4 modelViewMatrix;\r\n uniform mat4 projectionMatrix;\r\n\r\n uniform float uTime;\r\n uniform float uRandom;\r\n uniform float uDepth;\r\n uniform float uSize;\r\n uniform vec2 uTextureSize;\r\n uniform sampler2D uTexture;\r\n uniform sampler2D uTouch;\r\n\r\n varying vec2 vPUv;\r\n varying vec2 vUv;\r\n\r\n //\r\n // Description : Array and textureless GLSL 2D simplex noise function.\r\n // Author : Ian McEwan, Ashima Arts.\r\n // Maintainer : ijm\r\n // Lastmod : 20110822 (ijm)\r\n // License : Copyright (C) 2011 Ashima Arts. All rights reserved.\r\n // Distributed under the MIT License. See LICENSE file.\r\n // https://github.com/ashima/webgl-noise\r\n //\r\n\r\n vec3 mod289_1_0(vec3 x) {\r\n return x - floor(x * (1.0 / 289.0)) * 289.0;\r\n }\r\n\r\n vec2 mod289_1_0(vec2 x) {\r\n return x - floor(x * (1.0 / 289.0)) * 289.0;\r\n }\r\n\r\n vec3 permute_1_1(vec3 x) {\r\n return mod289_1_0(((x*34.0)+1.0)*x);\r\n }\r\n\r\n float snoise_1_2(vec2 v)\r\n {\r\n const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0\r\n 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)\r\n -0.577350269189626, // -1.0 + 2.0 * C.x\r\n 0.024390243902439); // 1.0 / 41.0\r\n // First corner\r\n vec2 i = floor(v + dot(v, C.yy) );\r\n vec2 x0 = v - i + dot(i, C.xx);\r\n\r\n // Other corners\r\n vec2 i1;\r\n //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0\r\n //i1.y = 1.0 - i1.x;\r\n i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\r\n // x0 = x0 - 0.0 + 0.0 * C.xx ;\r\n // x1 = x0 - i1 + 1.0 * C.xx ;\r\n // x2 = x0 - 1.0 + 2.0 * C.xx ;\r\n vec4 x12 = x0.xyxy + C.xxzz;\r\n x12.xy -= i1;\r\n\r\n // Permutations\r\n i = mod289_1_0(i); // Avoid truncation effects in permutation\r\n vec3 p = permute_1_1( permute_1_1( i.y + vec3(0.0, i1.y, 1.0 ))\r\n + i.x + vec3(0.0, i1.x, 1.0 ));\r\n\r\n vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);\r\n m = m*m ;\r\n m = m*m ;\r\n\r\n // Gradients: 41 points uniformly over a line, mapped onto a diamond.\r\n // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)\r\n\r\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\r\n vec3 h = abs(x) - 0.5;\r\n vec3 ox = floor(x + 0.5);\r\n vec3 a0 = x - ox;\r\n\r\n // Normalise gradients implicitly by scaling m\r\n // Approximation of: m *= inversesqrt( a0*a0 + h*h );\r\n m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );\r\n\r\n // Compute final noise value at P\r\n vec3 g;\r\n g.x = a0.x * x0.x + h.x * x0.y;\r\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\r\n return 130.0 * dot(m, g);\r\n }\r\n\r\n float random(float n) {\r\n return fract(sin(n) * 43758.5453123);\r\n }\r\n\r\n void main() {\r\n vUv = uv;\r\n\r\n // particle uv\r\n vec2 puv = offset.xy / uTextureSize;\r\n vPUv = puv;\r\n\r\n // pixel color\r\n vec4 colA = texture2D(uTexture, puv);\r\n float grey = colA.r * 0.21 + colA.g * 0.71 + colA.b * 0.07;\r\n\r\n // displacement\r\n vec3 displaced = offset;\r\n // randomise\r\n displaced.xy += vec2(random(pindex) - 0.5, random(offset.x + pindex) - 0.5) * uRandom;\r\n float rndz = (random(pindex) + snoise_1_2(vec2(pindex * 0.1, uTime * 0.1)));\r\n displaced.z += rndz * (random(pindex) * 2.0 * uDepth);\r\n // center\r\n displaced.xy -= uTextureSize * 0.5;\r\n\r\n // touch\r\n float t = texture2D(uTouch, puv).r;\r\n displaced.z += t * 20.0 * rndz;\r\n displaced.x += cos(angle) * t * 20.0 * rndz;\r\n displaced.y += sin(angle) * t * 20.0 * rndz;\r\n\r\n // particle size\r\n float psize = (snoise_1_2(vec2(uTime, pindex) * 0.5) + 2.0);\r\n psize *= max(grey, 0.2);\r\n psize *= uSize;\r\n\r\n // final position\r\n vec4 mvPosition = modelViewMatrix * vec4(displaced, 1.0);\r\n mvPosition.xyz += position * psize;\r\n vec4 finalPosition = projectionMatrix * mvPosition;\r\n\r\n gl_Position = finalPosition;\r\n }\r\n `;\r\n\r\n particleFragment = `\r\n // @author brunoimbrizi / http://brunoimbrizi.com\r\n\r\n precision highp float;\r\n #define GLSLIFY 1\r\n\r\n uniform sampler2D uTexture;\r\n\r\n varying vec2 vPUv;\r\n varying vec2 vUv;\r\n\r\n void main() {\r\n vec4 color = vec4(0.0);\r\n vec2 uv = vUv;\r\n vec2 puv = vPUv;\r\n\r\n // pixel color\r\n vec4 colA = texture2D(uTexture, puv);\r\n\r\n // greyscale\r\n float grey = colA.r * 0.21 + colA.g * 0.71 + colA.b * 0.07;\r\n //vec4 colB = vec4(grey, grey, grey, 1.0);\r\n vec4 colB = vec4(colA.r, colA.g, colA.b, 1.0);\r\n\r\n // circle\r\n float border = 0.3;\r\n float radius = 0.5;\r\n float dist = radius - distance(uv, vec2(0.5));\r\n float t = smoothstep(0.0, border, dist);\r\n\r\n // final color\r\n color = colB;\r\n color.a = t;\r\n\r\n gl_FragColor = color;\r\n }\r\n `;\r\n}\r\n","import { Observable, Observer } from 'rxjs';\r\n\r\nexport module RxjsTween {\r\n /**\r\n * Creates an observable that emits samples from an easing function on every animation frame\r\n * for a duration `d` ms.\r\n *\r\n * The first value will be emitted on the next animation frame,\r\n * and is the value of the easing function at `t = 0`.\r\n * The final value is guaranteed to be the easing function at `t = d`.\r\n * The observable completes one frame after the final value was emitted.\r\n * @param easingFunction the easing fuction to sample\r\n * @param b beginning value and 2nd parameter of the easing function\r\n * @param c change in value (or end value) and 3rd parameter of the easing function\r\n * @param d total duration of the tween in ms and 4th parameter of the easing function\r\n * @param s 5th parameter of the easing function (optional)\r\n */\r\n export function createTween(easingFunction: (t: number, b: number, pc: number, d: number, s?: number) => number,\r\n b: number[], c: number[], d: number, s?: number): Observable<number[]>;\r\n export function createTween(easingFunction: (t: number, b: number, pc: number, d: number, s?: number) => number,\r\n b: number, c: number, d: number, s?: number): Observable<number>;\r\n export function createTween(easingFunction: (t: number, b: number, pc: number, d: number, s?: number) => number,\r\n b: any, c: any, d: number, s?: number): Observable<any> {\r\n return new Observable((observer: Observer<number | number[]>) => {\r\n let startTime: number;\r\n const sample = (time: number): void => {\r\n startTime = startTime || time;\r\n const t = time - startTime;\r\n if (t < d) {\r\n if (Array.isArray(b) && Array.isArray(c)) {\r\n const tweenVals: number[] = new Array<number>();\r\n for (let idx = 0; idx < b.length; idx++) {\r\n tweenVals.push(easingFunction(t, b[idx], c[idx], d, s));\r\n }\r\n observer.next(tweenVals);\r\n }else {\r\n observer.next(easingFunction(t, b, c, d, s));\r\n }\r\n // Request the animation frame again\r\n requestAnimationFrame(sample);\r\n } else {\r\n // End value reached\r\n if (Array.isArray(b) && Array.isArray(c)) {\r\n const tweenVals: number[] = new Array<number>();\r\n for (let idx = 0; idx < b.length; idx++) {\r\n tweenVals.push(c[idx]);\r\n }\r\n // Emitt end value of arry\r\n observer.next(tweenVals);\r\n }else{\r\n // Emitt end value\r\n observer.next(c);\r\n }\r\n // Complete the observable\r\n observer.complete();\r\n }\r\n }\r\n // Initially request the animation frame\r\n requestAnimationFrame(sample);\r\n });\r\n }\r\n\r\n export function linear(t: number, b: number, pc: number, d: number): number {\r\n const c = pc - b;\r\n return c * t / d + b;\r\n }\r\n\r\n export function easeInOutQuad(t: number, b: number, pc: number, d: number): number {\r\n const c = pc - b;\r\n if ((t /= d / 2) < 1) {\r\n return c / 2 * t * t + b;\r\n } else {\r\n return -c / 2 * ((--t) * (t - 2) - 1) + b;\r\n }\r\n }\r\n}\r\n","import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, ViewChild } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'acuw-performance-monitor',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './performance-monitor.component.html',\r\n styleUrls: ['./performance-monitor.component.css']\r\n})\r\nexport class PerformanceMonitorComponent {\r\n\r\n @ViewChild('chart') chart!: ElementRef;\r\n\r\n fps = -1;\r\n fpsMin = -1;\r\n fpsMax = -1;\r\n private framesCnt = 0;\r\n private timeLastFpsCalc = 0;\r\n private fpsHistory = new Array<number>()\r\n private readonly maxHistoryLength = 60;\r\n\r\n constructor(private changeDetector: ChangeDetectorRef) {\r\n }\r\n\r\n /**\r\n * Method to be called at the end of a frame\r\n */\r\n end(): void {\r\n // When called the first time, set the current time and return\r\n if (this.timeLastFpsCalc === 0) {\r\n this.timeLastFpsCalc = Date.now();\r\n return;\r\n }\r\n // Increase the frames counter\r\n this.framesCnt++;\r\n // Get the milliseconds elapsed since January 1, 1970 for the current frame \r\n const currentFrameTime = Date.now();\r\n // Calculate the FPS only every second\r\n if (currentFrameTime >= this.timeLastFpsCalc + 1000) {\r\n // Calculate the frames per second\r\n this.fps = this.framesCnt / (currentFrameTime - this.timeLastFpsCalc) * 1000;\r\n // Calculate the min. frames per second\r\n if(this.fpsMin === -1 || this.fps < this.fpsMin){\r\n this.fpsMin = this.fps;\r\n }\r\n // Calculate the max. frames per second\r\n if(this.fpsMax === -1 || this.fps > this.fpsMax){\r\n this.fpsMax = this.fps;\r\n }\r\n // Trigger the change detection\r\n this.changeDetector.detectChanges();\r\n // Set the elapsed time to the timeLastFpsCalc\r\n this.timeLastFpsCalc = currentFrameTime;\r\n // Reset the frames counter\r\n this.framesCnt = 0;\r\n // Add fps to the history array\r\n this.fpsHistory.push(this.fps);\r\n if(this.fpsHistory.length >= this.maxHistoryLength) {\r\n this.fpsHistory.shift();\r\n }\r\n // Create / Update chart\r\n const canvasEl = this.chart.nativeElement as HTMLCanvasElement;\r\n const ctx = canvasEl.getContext('2d');\r\n if(ctx) {\r\n ctx.fillStyle = 'rgb(30, 30, 30)';\r\n ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);\r\n ctx.strokeStyle = 'rgb(255, 255, 255)';\r\n ctx.fillStyle = 'rgb(255, 255, 255)';\r\n ctx.beginPath();\r\n ctx.moveTo(0, canvasEl.height);\r\n for(let idx = 0; idx <= this.fpsHistory.length; idx++){\r\n ctx.lineTo(canvasEl.width / this.maxHistoryLength * idx, \r\n canvasEl.height - (this.fpsHistory[idx] / this.fpsMax * canvasEl.height));\r\n if(idx === this.fpsHistory.length - 1){\r\n ctx.lineTo(canvasEl.width / this.maxHistoryLength * idx, canvasEl.height);\r\n }\r\n }\r\n ctx.fill();\r\n }\r\n }\r\n }\r\n}\r\n","<div id=\"pm-container\">\r\n <div id=\"fps-display-container\">\r\n <span id=\"fps-display\">FPS: {{ fps != -1 ? (fps | number: '1.0-0') : '-' }}</span>\r\n <div id=\"min-max-display\">\r\n <span id=\"max-fps-display\">{{ fpsMax != -1 ? (fpsMax | number: '1.0-0') : '-' }} max</span>\r\n <span id=\"min-fps-display\">{{ fpsMin != -1 ? (fpsMin | number: '1.0-0') : '-' }} min</span>\r\n </div>\r\n </div>\r\n <canvas #chart width=\"90\" height=\"40\"></canvas>\r\n</div>","import {\r\n Component,\r\n ViewChild,\r\n ElementRef,\r\n AfterViewInit,\r\n Input,\r\n OnDestroy,\r\n HostListener,\r\n NgZone,\r\n} from '@angular/core';\r\nimport {\r\n BufferAttribute,\r\n Clock,\r\n InstancedBufferAttribute,\r\n InstancedBufferGeometry,\r\n LinearFilter,\r\n Mesh,\r\n MeshBasicMaterial,\r\n PerspectiveCamera,\r\n PlaneGeometry,\r\n Raycaster,\r\n RGBAFormat,\r\n Scene,\r\n TextureLoader,\r\n Vector2,\r\n WebGLRenderer,\r\n} from 'three';\r\nimport { Object3D, RawShaderMaterial, Texture } from 'three';\r\nimport { TouchTexture } from './scripts/touch-texture';\r\nimport { Shaders } from './scripts/shaders';\r\nimport { RxjsTween } from '../tween/rxjs-tween';\r\nimport { interval, Observable, Subscription } from 'rxjs';\r\nimport { animate, style, transition, trigger } from '@angular/animations';\r\nimport { PerformanceMonitorComponent } from '../performance-monitor/performance-monitor.component';\r\n\r\n@Component({\r\n selector: 'lib-image-as-particles',\r\n template: `\r\n <div\r\n #container\r\n class=\"threejs-container\"\r\n [style.background-color]=\"backgroundColor\"\r\n [style.justify-content]=\"justifyContent\"\r\n [style.align-items]=\"alignItems\"\r\n (mousemove)=\"onMouseMove($event)\"\r\n (touchmove)=\"onTouchMove($event)\"\r\n ></div>\r\n <div\r\n *ngIf=\"showTouchGestureInfo == true\"\r\n class=\"touch-gesture-info\"\r\n [@showHideGestureInformation]\r\n >\r\n <div>\r\n <span>Use two fingers for touch animation</span>\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n enable-background=\"new 0 0 24 24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"white\"\r\n width=\"18px\"\r\n height=\"18px\"\r\n >\r\n <g><rect fill=\"none\" height=\"24\" width=\"24\" x=\"0\" /></g>\r\n <g>\r\n <g>\r\n <g>\r\n <path\r\n d=\"M9,11.24V7.5C9,6.12,10.12,5,11.5,5S14,6.12,14,7.5v3.74c1.21-0.81,2-2.18,2-3.74C16,5.01,13.99,3,11.5,3S7,5.01,7,7.5 C7,9.06,7.79,10.43,9,11.24z M18.84,15.87l-4.54-2.26c-0.17-0.07-0.35-0.11-0.54-0.11H13v-6C13,6.67,12.33,6,11.5,6 S10,6.67,10,7.5v10.74c-3.6-0.76-3.54-0.75-3.67-0.75c-0.31,0-0.59,0.13-0.79,0.33l-0.79,0.8l4.94,4.94 C9.96,23.83,10.34,24,10.75,24h6.79c0.75,0,1.33-0.55,1.44-1.28l0.75-5.27c0.01-0.07,0.02-0.14,0.02-0.2 C19.75,16.63,19.37,16.09,18.84,15.87z\"\r\n />\r\n </g>\r\n </g>\r\n </g>\r\n </svg>\r\n </div>\r\n </div>\r\n <acuw-performance-monitor\r\n *ngIf=\"showPerformanceMonitor\"\r\n #performanceMonitor\r\n ></acuw-performance-monitor>\r\n `,\r\n styles: [\r\n `\r\n .threejs-container {\r\n position: relative;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 100%;\r\n height: 100%;\r\n background-color: #222222;\r\n }\r\n\r\n .touch-gesture-info {\r\n position: absolute;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: row;\r\n justify-content: center;\r\n top: 20px;\r\n color: white;\r\n }\r\n\r\n .touch-gesture-info div {\r\n background-color: rgba(0, 0, 0, 0.3);\r\n display: flex;\r\n flex-direction: row;\r\n padding: 6px 10px 6px 10px;\r\n border-radius: 5px;\r\n }\r\n `,\r\n ],\r\n animations: [\r\n trigger('showHideGestureInformation', [\r\n transition(':enter', [\r\n style({ opacity: '0' }),\r\n animate('300ms ease-in', style({ opacity: '1' })),\r\n ]),\r\n transition(':leave', [\r\n style({ opacity: '1' }),\r\n animate('300ms ease-in', style({ opacity: '0' })),\r\n ]),\r\n ]),\r\n ],\r\n})\r\nexport class ImageAsParticlesComponent implements AfterViewInit, OnDestroy {\r\n // Declare variables\r\n private renderer: WebGLRenderer = new WebGLRenderer({\r\n antialias: true,\r\n alpha: true,\r\n });\r\n private scene: Scene = new Scene();\r\n private camera!: PerspectiveCamera;\r\n private clock: Clock = new Clock(true);\r\n private texture: Texture = new Texture();\r\n private mesh!: Mesh;\r\n private hitArea!: Mesh;\r\n private width = 0;\r\n private height = 0;\r\n private touch: TouchTexture = new TouchTexture();\r\n private mouse: Vector2 = new Vector2();\r\n private raycaster: Raycaster = new Raycaster();\r\n private pImageUrl = '';\r\n private pImageChanging = false;\r\n private gestureInfo$: Observable<number> = interval(2000);\r\n private gestureInfoSubscription: Subscription = new Subscription();\r\n showTouchGestureInfo = false;\r\n justifyContent = 'center';\r\n alignItems = 'center';\r\n\r\n // Inputs\r\n @Input()\r\n set imageUrl(imageUrl: string) {\r\n this.pImageUrl = imageUrl;\r\n if (this.pImageChanging === true) {\r\n return;\r\n }\r\n if (this.mesh != null) {\r\n this.pImageChanging = true;\r\n this.triggerImageChange();\r\n }\r\n }\r\n get imageUrl(): string {\r\n return this.pImageUrl;\r\n }\r\n @Input() backgroundColor = '#000000';\r\n @Input() imageWidth = '100%';\r\n @Input() imageHeight = '100%';\r\n @Input()\r\n set horizontalAlignment(horizontalAlignment: string) {\r\n switch (horizontalAlignment) {\r\n case 'start':\r\n this.justifyContent = 'flex-start';\r\n break;\r\n case 'center':\r\n this.justifyContent = 'center';\r\n break;\r\n case 'end':\r\n this.justifyContent = 'flex-end';\r\n break;\r\n default:\r\n this.justifyContent = 'center';\r\n break;\r\n }\r\n }\r\n get horizontalAlignment(): string {\r\n return this.justifyContent;\r\n }\r\n @Input()\r\n set verticalAlignment(verticalAlignment: string) {\r\n switch (verticalAlignment) {\r\n case 'top':\r\n this.alignItems = 'flex-start';\r\n break;\r\n case 'center':\r\n this.alignItems = 'center';\r\n break;\r\n case 'bottom':\r\n this.alignItems = 'flex-end';\r\n break;\r\n default:\r\n this.alignItems = 'center';\r\n break;\r\n }\r\n }\r\n get verticalAlignment(): string {\r\n return this.alignItems;\r\n }\r\n @Input() animationEnabled = true;\r\n @Input() showPerformanceMonitor = false;\r\n\r\n @ViewChild('container') canvasRef!: ElementRef;\r\n @ViewChild('performanceMonitor')\r\n performanceMonitor!: PerformanceMonitorComponent;\r\n\r\n constructor(private ngZone: NgZone) {}\r\n\r\n ngAfterViewInit(): void {\r\n if (this.pImageUrl === '') {\r\n return;\r\n }\r\n\r\n const canvasWidth = this.canvasRef.nativeElement.clientWidth;\r\n const canvasHeight = this.canvasRef.nativeElement.clientHeight;\r\n // Set camera\r\n this.camera = new PerspectiveCamera(\r\n 50,\r\n canvasWidth / canvasHeight,\r\n 1,\r\n 10000\r\n );\r\n this.camera.position.z = 300;\r\n // Init particles\r\n this.initParticles(this.pImageUrl);\r\n // Init renderer\r\n this.renderer.setSize(canvasWidth - 1, canvasHeight);\r\n this.canvasRef.nativeElement.appendChild(this.renderer.domElement);\r\n // Start animation\r\n this.animate();\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.scene.clear();\r\n this.renderer.clear();\r\n this.texture.dispose();\r\n this.renderer.dispose();\r\n }\r\n\r\n /**\r\n * Creates the particles depending on the image and initializes the touch canvas\r\n * @param url url of the image\r\n */\r\n private initParticles(url: string): void {\r\n const loader = new TextureLoader();\r\n loader.load(url, (texture) => {\r\n this.texture = texture;\r\n this.texture.minFilter = LinearFilter;\r\n this.texture.magFilter = LinearFilter;\r\n this.texture.format = RGBAFormat;\r\n\r\n this.width = texture.image.width;\r\n this.height = texture.image.height;\r\n\r\n this.initPoints(true);\r\n this.initHitArea();\r\n this.initTouch();\r\n this.resize();\r\n this.show();\r\n });\r\n }\r\n\r\n /**\r\n * Initializes the points\r\n * @param discard discard pixels darker than threshold #22\r\n */\r\n private initPoints(discard: boolean): void {\r\n const numPoints: number = this.width * this.height;\r\n\r\n let numVisible = numPoints;\r\n let threshold = 0;\r\n let originalColors = new Float32Array();\r\n\r\n if (discard) {\r\n // discard pixels darker than threshold #22\r\n numVisible = 0;\r\n threshold = 34;\r\n\r\n const img = this.texture.image;\r\n const canvas = document.createElement('canvas');\r\n const ctx = canvas.getContext('2d');\r\n\r\n canvas.width = this.width;\r\n canvas.height = this.height;\r\n if (ctx != null) {\r\n ctx.scale(1, -1);\r\n ctx.drawImage(img, 0, 0, this.width, this.height * -1);\r\n const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);\r\n originalColors = Float32Array.from(imgData.data);\r\n\r\n for (let i = 0; i < numPoints; i++) {\r\n if (originalColors[i * 4 + 0] > threshold) {\r\n numVisible++;\r\n }\r\n }\r\n }\r\n }\r\n\r\n const uniforms = {\r\n uTime: { value: 0 },\r\n uRandom: { value: 1.0 },\r\n uDepth: { value: 2.0 },\r\n uSize: { value: 0.0 },\r\n uTextureSize: { value: new Vector2(this.width, this.height) },\r\n uTexture: { value: this.texture },\r\n uTouch: { value: null },\r\n };\r\n\r\n const shaders = new Shaders();\r\n const material = new RawShaderMaterial({\r\n uniforms,\r\n vertexShader: shaders.particleVertex,\r\n fragmentShader: shaders.particleFragment,\r\n depthTest: false,\r\n transparent: true,\r\n // blending: THREE.AdditiveBlending\r\n });\r\n\r\n const geometry = new InstancedBufferGeometry();\r\n\r\n // positions\r\n const positions = new BufferAttribute(new Float32Array(4 * 3), 3);\r\n positions.setXYZ(0, -0.5, 0.5, 0.0);\r\n positions.setXYZ(1, 0.5, 0.5, 0.0);\r\n positions.setXYZ(2, -0.5, -0.5, 0.0);\r\n positions.setXYZ(3, 0.5, -0.5, 0.0);\r\n geometry.setAttribute('position', positions);\r\n\r\n // uvs\r\n const uvs = new BufferAttribute(new Float32Array(4 * 2), 2);\r\n uvs.setXY(0, 0.0, 0.0);\r\n uvs.setXY(1, 1.0, 0.0);\r\n uvs.setXY(2, 0.0, 1.0);\r\n uvs.setXY(3, 1.0, 1.0);\r\n geometry.setAttribute('uv', uvs);\r\n\r\n // index\r\n geometry.setIndex(\r\n new BufferAttribute(new Uint16Array([0, 2, 1, 2, 3, 1]), 1)\r\n );\r\n\r\n const indices = new Uint16Array(numVisible);\r\n const offsets = new Float32Array(numVisible * 3);\r\n const angles = new Float32Array(numVisible);\r\n\r\n for (let i = 0, j = 0; i < numPoints; i++) {\r\n if (discard && originalColors[i * 4 + 0] <= threshold) {\r\n continue;\r\n }\r\n\r\n offsets[j * 3 + 0] = i % this.width;\r\n offsets[j * 3 + 1] = Math.floor(i / this.width);\r\n\r\n indices[j] = i;\r\n\r\n angles[j] = Math.random() * Math.PI;\r\n\r\n j++;\r\n }\r\n\r\n geometry.setAttribute(\r\n 'pindex',\r\n new InstancedBufferAttribute(indices, 1, false)\r\n );\r\n geometry.setAttribute(\r\n 'offset',\r\n new InstancedBufferAttribute(offsets, 3, false)\r\n );\r\n geometry.setAttribute(\r\n 'angle',\r\n new InstancedBufferAttribute(angles, 1, false)\r\n );\r\n\r\n this.mesh = new Mesh(geometry, material);\r\n const object3d = new Object3D();\r\n object3d.add(this.mesh);\r\n this.scene.add(object3d);\r\n }\r\n\r\n /**\r\n * Initializes the touch area\r\n */\r\n private initTouch(): void {\r\n (this.mesh.material as RawShaderMaterial).uniforms.uTouch.value =\r\n this.touch.texture;\r\n }\r\n\r\n /**\r\n * Initializes the hit area\r\n */\r\n private initHitArea(): void {\r\n const geometry = new PlaneGeometry(this.width, this.height, 1, 1);\r\n const material = new MeshBasicMaterial({\r\n color: 0xffffff,\r\n wireframe: true,\r\n depthTest: false,\r\n });\r\n material.visible = false;\r\n this.hitArea = new Mesh(geometry, material);\r\n this.mesh.add(this.hitArea);\r\n }\r\n\r\n /**\r\n * animation for showing the particles\r\n * @param time time of animation in ms\r\n */\r\n private show(time: number = 1000): void {\r\n // Tween in\r\n this.ngZone.runOutsideAngular(() => {\r\n RxjsTween.createTween(\r\n RxjsTween.easeInOutQuad,\r\n [0.5, 0.0, 70.0],\r\n [1.5, 2.0, 4.0],\r\n time\r\n ).subscribe(\r\n (val) => {\r\n (this.mesh.material as RawShaderMaterial).uniforms.uSize.value =\r\n val[0];\r\n (this.mesh.material as RawShaderMaterial).uniforms.uRandom.value =\r\n val[1];\r\n (this.mesh.material as RawShaderMaterial).uniforms.uDepth.value =\r\n val[2];\r\n },\r\n () => {},\r\n () => {\r\n this.pImageChanging = false;\r\n }\r\n );\r\n });\r\n }\r\n\r\n /**\r\n * animation for tween out the particles and destroy everything\r\n * @param time time of animation in ms\r\n */\r\n private triggerImageChange(time: number = 1000): void {\r\n const uSizeStart = (this.mesh.material as RawShaderMaterial).uniforms.uSize\r\n .value;\r\n const uRandomStart = (this.mesh.material as RawShaderMaterial).uniforms\r\n .uRandom.value;\r\n const uDepth = (this.mesh.material as RawShaderMaterial).uniforms.uDepth\r\n .value;\r\n this.ngZone.runOutsideAngular(() => {\r\n // Tween out\r\n RxjsTween.createTween(\r\n RxjsTween.easeInOutQuad,\r\n [uSizeStart, uRandomStart, uDepth],\r\n [0.0, 5.0, -20.0],\r\n time\r\n ).subscribe(\r\n (val) => {\r\n (this.mesh.material as RawShaderMaterial).uniforms.uSize.value =\r\n val[0];\r\n (this.mesh.material as RawShaderMaterial).uniforms.uRandom.value =\r\n val[1];\r\n (this.mesh.material as RawShaderMaterial).uniforms.uDepth.value =\r\n val[2];\r\n },\r\n () => {},\r\n () => {\r\n if (this.mesh != null) {\r\n if (this.mesh.parent != null) {\r\n this.mesh.parent.remove(this.mesh);\r\n }\r\n this.mesh.geometry.dispose();\r\n (this.mesh.material as RawShaderMaterial).dispose();\r\n }\r\n\r\n if (this.hitArea != null) {\r\n if (this.hitArea.parent != null) {\r\n this.hitArea.parent.remove(this.hitArea);\r\n }\r\n this.hitArea.geometry.dispose();\r\n (this.hitArea.material as RawShaderMaterial).dispose();\r\n }\r\n this.initParticles(this.pImageUrl);\r\n this.pImageChanging = false;\r\n }\r\n );\r\n });\r\n }\r\n\r\n /**\r\n * Method for triggering the animation\r\n */\r\n private animate(): void {\r\n this.ngZone.runOutsideAngular(() => {\r\n window.requestAnimationFrame(() => this.animate());\r\n if (this.animationEnabled === true) {\r\n const delta = this.clock.getDelta();\r\n if (this.mesh != null) {\r\n if (this.touch) {\r\n this.touch.update();\r\n }\r\n (this.mesh.material as RawShaderMaterial).uniforms.uTime.value +=\r\n delta;\r\n }\r\n this.renderer.render(this.scene, this.camera);\r\n }\r\n if (this.performanceMonitor && this.showPerformanceMonitor) {\r\n this.performanceMonitor.end();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Handle mouse move event\r\n * @param event mouse event\r\n */\r\n onMouseMove(event: MouseEvent): void {\r\n // getBoundingClientRect retruns the distance in pixels of the top left corner of the element\r\n // to the top left corner of the viewport\r\n const domRect = (\r\n this.canvasRef.nativeElement as HTMLElement\r\n ).getBoundingClientRect();\r\n // get the offset distance between the canvas, which contains the particles, to the outer container element\r\n const canvasEl: HTMLElement = this.canvasRef.nativeElement\r\n .children[0] as HTMLElement;\r\n // Calculate the relative mouse position\r\n this.mouse.x =\r\n ((event.clientX - domRect.left - canvasEl.offsetLeft) /\r\n canvasEl.clientWidth) *\r\n 2 -\r\n 1;\r\n this.mouse.y =\r\n (-(event.clientY - domRect.top - canvasEl.offsetTop) /\r\n canvasEl.clientHeight) *\r\n 2 +\r\n 1;\r\n // console.info('raw: x= ' + event.clientX + ' , y= ' + event.clientY);\r\n // console.info('normalized: x= ' + this.mouse.x + ' , y= ' + this.mouse.y);\r\n this.raycaster.setFromCamera(this.mouse, this.camera);\r\n\r\n if (this.hitArea === undefined) {\r\n return;\r\n }\r\n const intersects = this.raycaster.intersectObject(this.hitArea);\r\n if (\r\n intersects !== undefined &&\r\n intersects.length > 0 &&\r\n this.touch &&\r\n intersects[0].uv !== undefined\r\n ) {\r\n this.touch.addTouch(intersects[0].uv.x, intersects[0].uv.y);\r\n }\r\n }\r\n\r\n /**\r\n * Handle touch move envent\r\n * @param event mouse event\r\n */\r\n onTouchMove(event: TouchEvent): void {\r\n if (event.touches.length < 2) {\r\n this.showTouchGestureInfo = true;\r\n this.gestureInfoSubscription.unsubscribe();\r\n this.gestureInfoSubscription = this.gestureInfo$.subscribe({\r\n next: () => {\r\n this.showTouchGestureInfo = false;\r\n this.gestureInfoSubscription.unsubscribe();\r\n },\r\n });\r\n return;\r\n }\r\n event.preventDefault();\r\n // getBoundingClientRect retruns the distance in pixels of the top left corner of the element\r\n // to the top left corner of the viewport\r\n const domRect = (\r\n this.canvasRef.nativeElement as HTMLElement\r\n ).getBoundingClientRect();\r\n // get the offset distance between the canvas, which contains the particles, to the outer container element\r\n const canvasEl: HTMLElement = this.canvasRef.nativeElement\r\n .children[0] as HTMLElement;\r\n // Calculate the relative mouse position\r\n this.mouse.x =\r\n ((event.touches[0].clientX - domRect.left - canvasEl.offsetLeft) /\r\n canvasEl.clientWidth) *\r\n 2 -\r\n 1;\r\n this.mouse.y =\r\n (-(event.touches[0].clientY - domRect.top - canvasEl.offsetTop) /\r\n canvasEl.clientHeight) *\r\n 2 +\r\n 1;\r\n this.raycaster.setFromCamera(this.mouse, this.camera);\r\n\r\n const intersects = this.raycaster.intersectObject(this.hitArea);\r\n if (\r\n intersects !== undefined &&\r\n intersects.length > 0 &&\r\n this.touch &&\r\n intersects[0].uv !== undefined\r\n ) {\r\n this.touch.addTouch(intersects[0].uv.x, intersects[0].uv.y);\r\n }\r\n }\r\n\r\n @HostListener('window:resize') resize(): void {\r\n if (this.height !== undefined) {\r\n this.camera.aspect =\r\n this.canvasRef.nativeElement.clientWidth /\r\n this.canvasRef.nativeElement.clientHeight;\r\n this.camera.updateProjectionMatrix();\r\n const fovHeight =\r\n 2 *\r\n Math.tan((this.camera.fov * Math.PI) / 180 / 2) *\r\n this.camera.position.z;\r\n const scale = fovHeight / this.height;\r\n this.mesh.scale.set(scale, scale, 1);\r\n // this.hitArea.scale.set(scale, scale, 1);\r\n if (this.renderer !== undefined) {\r\n const width =\r\n this.imageWidth == null\r\n ? this.canvasRef.nativeElement.clientWidth\r\n : this.distanceAsNumber(\r\n this.imageWidth,\r\n this.canvasRef.nativeElement.clientWidth\r\n );\r\n const height =\r\n this.imageHeight == null\r\n ? this.canvasRef.nativeElement.clientHeight\r\n : this.distanceAsNumber(\r\n this.imageHeight,\r\n this.canvasRef.nativeElement.clientHeight\r\n );\r\n this.renderer.setSize(width, height);\r\n }\r\n }\r\n }\r\n\r\n private distanceAsNumber(distance: string, parentDistance: number): number {\r\n let returnVal = 0;\r\n if (distance.includes('px')) {\r\n returnVal = Number.parseInt(distance.replace('px', ''), 10);\r\n } else if (distance.includes('%')) {\r\n returnVal =\r\n (Number.parseInt(distance.replace('%', ''), 10) / 100) * parentDistance;\r\n } else {\r\n returnVal = Number.parseInt(distance, 10);\r\n }\r\n return returnVal;\r\n }\r\n}\r\n","import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { PerformanceMonitorComponent } from './performance-monitor.component';\r\n\r\n\r\n\r\n@NgModule({\r\n declarations: [\r\n PerformanceMonitorComponent\r\n ],\r\n imports: [\r\n CommonModule\r\n ],\r\n exports: [\r\n PerformanceMonitorComponent\r\n ]\r\n})\r\nexport class PerformanceMonitorModule { }\r\n","import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { ImageAsParticlesComponent } from './image-as-particles.component';\r\nimport { PerformanceMonitorModule } from '../performance-monitor/performance-monitor.module';\r\n\r\n\r\n\r\n@NgModule({\r\n declarations: [ImageAsParticlesComponent],\r\n imports: [\r\n CommonModule, PerformanceMonitorModule\r\n ],\r\n exports: [ImageAsParticlesComponent]\r\n})\r\nexport class ImageAsParticlesModule { }\r\n","export enum Direction {\r\n forward,\r\n backward,\r\n }","export class ImageTransitionShaders{\r\n vertex = `varying vec2 vUv;void main() {vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );}`;\r\n splitTransitionFrag = `\r\n uniform float progress;\r\n uniform float intensity;\r\n uniform sampler2D texture1;\r\n uniform sampler2D texture2;\r\n uniform vec4 resolution1;\r\n uniform vec4 resolution2;\r\n varying vec2 vUv;\r\n mat2 rotate(float a) {\r\n float s = sin(a);\r\n float c = cos(a);\r\n return mat2(c, -s, s, c);\r\n }\r\n void main()\t{\r\n vec2 newUV1 = (vUv - vec2(0.5,0.5))*resolution1.zw + vec2(0.5,0.5);\r\n vec2 newUV2 = (vUv - vec2(0.5,0.5))*resolution2.zw + vec2(0.5,0.5);\r\n vec2 uvDivided1 = fract(newUV1*vec2(intensity,1.));\r\n vec2 uvDivided2 = fract(newUV2*vec2(intensity,1.));\r\n vec2 uvDisplaced1 = newUV1 + rotate(3.1415926/4.)*uvDivided1*progress*0.1;\r\n vec2 uvDisplaced2 = newUV2 + rotate(3.1415926/4.)*uvDivided2*(1. - progress)*0.1;\r\n vec4 t1 = texture2D(texture1,uvDisplaced1);\r\n vec4 t2 = texture2D(texture2,uvDisplaced2);\r\n // Use black background color\r\n // Top right\r\n vec2 tr1 = step(newUV1, vec2(1.0, 1.0));\r\n vec2 tr2 = step(newUV2, vec2(1.0, 1.0));\r\n float pct1 = tr1.x * tr1.y;\r\n float pct2 = tr2.x * tr2.y;\r\n // Bottom left\r\n vec2 bl1 = step(vec2(0.0, 0.0), newUV1);\r\n vec2 bl2 = step(vec2(0.0, 0.0), newUV2);\r\n pct1 *= bl1.x * bl1.y;\r\n pct2 *= bl2.x * bl2.y;\r\n vec4 t1wb = t1 * vec4(pct1,pct1,pct1,1.0);\r\n vec4 t2wb = t2 * vec4(pct2,pct2,pct2,1.0);\r\n gl_FragColor = mix(t1wb, t2wb, progress);\r\n }\r\n `;\r\n fadeFrag = `\r\n uniform float progress;\r\n uniform sampler2D texture1;\r\n uniform sampler2D texture2;\r\n uniform vec4 resolution1;\r\n uniform vec4 resolution2;\r\n varying vec2 vUv;\r\n mat2 rotate(float a) {\r\n float s = sin(a);\r\n float c = cos(a);\r\n return mat2(c, -s, s, c);\r\n }\r\n void main()\t{\r\n vec2 newUV1 = (vUv - vec2(0.5,0.5))*resolution1.zw + vec2(0.5,0.5);\r\n vec2 newUV2 = (vUv - vec2(0.5,0.5))*resolution2.zw + vec2(0.5,0.5);\r\n vec2 uvDisplaced1 = newUV1 + vec2(1.0,0)*progress*0.1;\r\n vec2 uvDisplaced2 = newUV2 + vec2(1.0,0)*(1. - progress)*0.1;\r\n vec4 t1 = texture2D(texture1,uvDisplaced1);\r\n vec4 t2 = texture2D(texture2,uvDisplaced2);\r\n // Use black background color\r\n // Top right\r\n vec2 tr1 = step(newUV1, vec2(1.0, 1.0));\r\n vec2 tr2 = step(newUV2, vec2(1.0, 1.0));\r\n float pct1 = tr1.x * tr1.y;\r\n float pct2 = tr2.x * tr2.y;\r\n // Bottom left\r\n vec2 bl1 = step(vec2(0.0, 0.0), newUV1);\r\n vec2 bl2 = step(vec2(0.0, 0.0), newUV2);\r\n pct1 *= bl1.x * bl1.y;\r\n pct2 *= bl2.x * bl2.y;\r\n vec4 t1wb = t1 * vec4(pct1,pct1,pct1,1.0);\r\n vec4 t2wb = t2 * vec4(pct2,pct2,pct2,1.0);\r\n gl_FragColor = mix(t1wb, t2wb, progress);\r\n }\r\n `;\r\n noiseFrag = `\r\n\t\tuniform float time;\r\n\t\tuniform float progress;\r\n\t\tuniform float width;\r\n\t\tuniform float scaleX;\r\n\t\tuniform float scaleY;\r\n\t\tuniform sampler2D texture1;\r\n\t\tuniform sampler2D texture2;\r\n\t\tuniform sampler2D displacement;\r\n\t\tuniform vec4 resolution1;\r\n\t\tuniform vec4 resolution2;\r\n\t\tvarying vec2 vUv;\r\n\t\tvarying vec4 vPosition;\r\n\t\t//\tClassic Perlin 3D Noise\r\n\t\t//\tby Stefan Gustavson\r\n\t\t//\r\n\t\tvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\r\n\t\tvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\r\n\t\tvec4 fade(vec4 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\r\n\t\tfloat cnoise(vec4 P){\r\n\t\t ;\r\n\t\t vec4 Pi0 = floor(P); // Integer part for indexing\r\n\t\t vec4 Pi1 = Pi0 + 1.0; // Integer part + 1\r\n\t\t Pi0 = mod(Pi0, 289.0);\r\n\t\t Pi1 = mod(Pi1, 289.0);\r\n\t\t vec4 Pf0 = fract(P); // Fractional part for interpolation\r\n\t\t vec4 Pf1 = Pf0 - 1.0; // Fractional part - 1.0\r\n\t\t vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\r\n\t\t vec4 iy = vec4(Pi0.yy, Pi1.yy);\r\n\t\t vec4 iz0 = vec4(Pi0.zzzz);\r\n\t\t vec4 iz1 = vec4(Pi1.zzzz);\r\n\t\t vec4 iw0 = vec4(Pi0.wwww);\r\n\t\t vec4 iw1 = vec4(Pi1.wwww);\r\n\t\t vec4 ixy = permute(permute(ix) + iy);\r\n\t\t vec4 ixy0 = permute(ixy + iz0);\r\n\t\t vec4 ixy1 = permute(ixy + iz1);\r\n\t\t vec4 ixy00 = permute(ixy0 + iw0);\r\n\t\t vec4 ixy01 = permute(ixy0 + iw1);\r\n\t\t vec4 ixy10 = permute(ixy1 + iw0);\r\n\t\t vec4 ixy11 = permute(ixy1 + iw1);\r\n\t\t vec4 gx00 = ixy00 / 7.0;\r\n\t\t vec4 gy00 = floor(gx00) / 7.0;\r\n\t\t vec4 gz00 = floor(gy00) / 6.0;\r\n\t\t gx00 = fract(gx00) - 0.5;\r\n\t\t gy00 = fract(gy00) - 0.5;\r\n\t\t gz00 = fract(gz00) - 0.5;\r\n\t\t vec4 gw00 = vec4(0.75) - abs(gx00) - abs(gy00) - abs(gz00);\r\n\t\t vec4 sw00 = step(gw00, vec4(0.0));\r\n\t\t gx00 -= sw00 * (step(0.0, gx00) - 0.5);\r\n\t\t gy00 -= sw00 * (step(0.0, gy00) - 0.5);\r\n\t\t vec4 gx01 = ixy01 / 7.0;\r\n\t\t vec4 gy01 = floor(gx01) / 7.0;\r\n\t\t vec4 gz01 = floor(gy01) / 6.0;\r\n\t\t gx01 = fract(gx01) - 0.5;\r\n\t\t gy01 = fract(gy01) - 0.5;\r\n\t\t gz01 = fract(gz01) - 0.5;\r\n\t\t vec4 gw01 = vec4(0.75) - abs(gx01) - abs(gy01) - abs(gz01);\r\n\t\t vec4 sw01 = step(gw01, vec4(0.0));\r\n\t\t gx01 -= sw01 * (step(0.0, gx01) - 0.5);\r\n\t\t gy01 -= sw01 * (step(0.0, gy01) - 0.5);\r\n\t\t vec4 gx10 = ixy10 / 7.0;\r\n\t\t vec4 gy10 = floor(gx10) / 7.0;\r\n\t\t vec4 gz10 = floor(gy10) / 6.0;\r\n\t\t gx10 = fract(gx10) - 0.5;\r\n\t\t gy10 = fract(gy10) - 0.5;\r\n\t\t gz10 = fract(gz10) - 0.5;\r\n\t\t vec4 gw10 = vec4(0.75) - abs(gx10) - abs(gy10) - abs(gz10);\r\n\t\t vec4 sw10 = step(gw10, vec4(0.0));\r\n\t\t gx10 -= sw10 * (step(0.0, gx10) - 0.5);\r\n\t\t gy10 -= sw10 * (step(0.0, gy10) - 0.5);\r\n\t\t vec4 gx11 = ixy11 / 7.0;\r\n\t\t vec4 gy11 = floor(gx11) / 7.0;\r\n\t\t vec4 gz11 = floor(gy11) / 6.0;\r\n\t\t gx11 = fract(gx11) - 0.5;\r\n\t\t gy11 = fract(gy11) - 0.5;\r\n\t\t gz11 = fract(gz11) - 0.5;\r\n\t\t vec4 gw11 = vec4(0.75) - abs(gx11) - abs(gy11) - abs(gz11);\r\n\t\t vec4 sw11 = step(gw11, vec4(0.0));\r\n\t\t gx11 -= sw11 * (step(0.0, gx11) - 0.5);\r\n\t\t gy11 -= sw11 * (step(0.0, gy11) - 0.5);\r\n\t\t vec4 g0000 = vec4(gx00.x,gy00.x,gz00.x,gw00.x);\r\n\t\t vec4 g1000 = vec4(gx00.y,gy00.y,gz00.y,gw00.y);\r\n\t\t vec4 g0100 = vec4(gx00.z,gy00.z,gz00.z,gw00.z);\r\n\t\t vec4 g1100 = vec4(gx00.w,gy00.w,gz00.w,gw00.w);\r\n\t\t vec4 g0010 = vec4(gx10.x,gy10.x,gz10.x,gw10.x);\r\n\t\t vec4 g1010 = vec4(gx10.y,gy10.y,gz10.y,gw10.y);\r\n\t\t vec4 g0110 = vec4(gx10.z,gy10.z,gz10.z,gw10.z);\r\n\t\t vec4 g1110 = vec4(gx10.w,gy10.w,gz10.w,gw10.w);\r\n\t\t vec4 g0001 = vec4(gx01.x,gy01.x,gz01.x,gw01.x);\r\n\t\t vec4 g1001 = vec4(gx01.y,gy01.y,gz01.y,gw01.y);\r\n\t\t vec4 g0101 = vec4(gx01.z,gy01.z,gz01.z,gw01.z);\r\n\t\t vec4 g1101 = vec4(gx01.w,gy01.w,gz01.w,gw01.w);\r\n\t\t vec4 g0011 = vec4(gx11.x,gy11.x,gz11.x,gw11.x);\r\n\t\t vec4 g1011 = vec4(gx11.y,gy11.y,gz11.y,gw11.y);\r\n\t\t vec4 g0111 = vec4(gx11.z,gy11.z,gz11.z,gw11.z);\r\n\t\t vec4 g1111 = vec4(gx11.w,gy11.w,gz11.w,gw11.w);\r\n\t\t vec4 norm00 = taylorInvSqrt(vec4(dot(g0000, g0000), dot(g0100, g0100), dot(g1000, g1000), dot(g1100, g1100)));\r\n\t\t g0000 *= norm00.x;\r\n\t\t g0100 *= norm00.y;\r\n\t\t g1000 *= norm00.z;\r\n\t\t g1100 *= norm00.w;\r\n\t\t vec4 norm01 = taylorInvSqrt(vec4(dot(g0001, g0001), dot(g0101, g0101), dot(g1001, g1001), dot(g1101, g1101)));\r\n\t\t g0001 *= norm01.x;\r\n\t\t g0101 *= norm01.y;\r\n\t\t g1001 *= norm01.z;\r\n\t\t g1101 *= norm01.w;\r\n\t\t vec4 norm10 = taylorInvSqrt(vec4(dot(g0010, g0010), dot(g0110, g0110), dot(g1010, g1010), dot(g1110, g1110)));\r\n\t\t g0010 *= norm10.x;\r\n\t\t g0110 *= norm10.y;\r\n\t\t g1010 *= norm10.z;\r\n\t\t g1110 *= norm10.w;\r\n\t\t vec4 norm11 = taylorInvSqrt(vec4(dot(g0011, g0011), dot(g0111, g0111), dot(g1011, g1011), dot(g1111, g1111)));\r\n\t\t g0011 *= norm11.x;\r\n\t\t g0111 *= norm11.y;\r\n\t\t g1011 *= norm11.z;\r\n\t\t g1111 *= norm11.w;\r\n\t\t float n0000 = dot(g0000, Pf0);\r\n\t\t float n1000 = dot(g1000, vec4(Pf1.x, Pf0.yzw));\r\n\t\t float n0100 = dot(g0100, vec4(Pf0.x, Pf1.y, Pf0.zw));\r\n\t\t float n1100 = dot(g1100, vec4(Pf1.xy, Pf0.zw));\r\n\t\t float n0010 = dot(g0010, vec4(Pf0.xy, Pf1.z, Pf0.w));\r\n\t\t float n1010 = dot(g1010, vec4(Pf1.x, Pf0.y, Pf1.z, Pf0.w));\r\n\t\t float n0110 = dot(g0110, vec4(Pf0.x, Pf1.yz, Pf0.w));\r\n\t\t float n1110 = dot(g1110, vec4(Pf1.xyz, Pf0.w));\r\n\t\t float n0001 = dot(g0001, vec4(Pf0.xyz, Pf1.w));\r\n\t\t float n1001 = dot(g1001, vec4(Pf1.x, Pf0.yz, Pf1.w));\r\n\t\t float n0101 = dot(g0101, vec4(Pf0.x, Pf1.y, Pf0.z, Pf1.w));\r\n\t\t float n1101 = dot(g1101, vec4(Pf1.xy, Pf0.z, Pf1.w));\r\n\t\t float n0011 = dot(g0011, vec4(Pf0.xy, Pf1.zw));\r\n\t\t float n1011 = dot(g1011, vec4(Pf1.x, Pf0.y, Pf1.zw));\r\n\t\t float n0111 = dot(g0111, vec4(Pf0.x, Pf1.yzw));\r\n\t\t float n1111 = dot(g1111, Pf1);\r\n\t\t vec4 fade_xyzw = fade(Pf0);\r\n\t\t vec4 n_0w = mix(vec4(n0000, n1000, n0100, n1100), vec4(n0001, n1001, n0101, n1101), fade_xyzw.w);\r\n\t\t vec4 n_1w = mix(vec4(n0010, n1010, n0110, n1110), vec4(n0011, n1011, n0111, n1111), fade_xyzw.w);\r\n\t\t vec4 n_zw = mix(n_0w, n_1w, fade_xyzw.z);\r\n\t\t vec2 n_yzw = mix(n_zw.xy, n_zw.zw, fade_xyzw.y);\r\n\t\t float n