mirror of
https://github.com/SamEyeBam/animate.git
synced 2026-02-04 01:14:15 +00:00
V1.1
Giant refactor. added layers. ui overhaul. added save/load and we now got presets
This commit is contained in:
24
Tests/react (rename)/.gitignore
vendored
Normal file
24
Tests/react (rename)/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
12
Tests/react (rename)/README.md
Normal file
12
Tests/react (rename)/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# React + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
|
||||
33
Tests/react (rename)/eslint.config.js
Normal file
33
Tests/react (rename)/eslint.config.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
|
||||
export default [
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...js.configs.recommended.rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
13
Tests/react (rename)/index.html
Normal file
13
Tests/react (rename)/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
4160
Tests/react (rename)/package-lock.json
generated
Normal file
4160
Tests/react (rename)/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
Tests/react (rename)/package.json
Normal file
31
Tests/react (rename)/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "react",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-three/drei": "^10.0.7",
|
||||
"@react-three/fiber": "^9.1.2",
|
||||
"leva": "^0.10.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"three": "^0.176.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.22.0",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"eslint": "^9.22.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^16.0.0",
|
||||
"vite": "^6.3.1"
|
||||
}
|
||||
}
|
||||
1
Tests/react (rename)/public/vite.svg
Normal file
1
Tests/react (rename)/public/vite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
21
Tests/react (rename)/src/App.css
Normal file
21
Tests/react (rename)/src/App.css
Normal file
@@ -0,0 +1,21 @@
|
||||
/* Remove default App.css styles if they interfere */
|
||||
/* You might want to keep some or none depending on desired layout */
|
||||
|
||||
/* Example: Remove padding and max-width from #root if set here */
|
||||
/* #root {
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
} */
|
||||
|
||||
/* Remove logo styles etc. */
|
||||
/* .logo { ... } */
|
||||
/* .logo:hover { ... } */
|
||||
/* ... etc ... */
|
||||
|
||||
/* @keyframes logo-spin { ... } */
|
||||
|
||||
/* @media (prefers-reduced-motion: no-preference) { ... } */
|
||||
|
||||
/* .card { ... } */
|
||||
|
||||
/* .read-the-docs { ... } */
|
||||
59
Tests/react (rename)/src/App.jsx
Normal file
59
Tests/react (rename)/src/App.jsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Canvas } from '@react-three/fiber'
|
||||
import { OrbitControls } from '@react-three/drei'
|
||||
import { useControls, Leva } from 'leva' // Import leva
|
||||
import PhyllotaxisSystem from './PhyllotaxisSystem' // Import Phyllotaxis system
|
||||
// import PolyTwistSystem from './PolyTwistSystem' // Import PolyTwist system
|
||||
import './App.css'
|
||||
|
||||
// Define animation options
|
||||
const animationOptions = {
|
||||
Phyllotaxis: 'Phyllotaxis',
|
||||
PolyTwist: 'PolyTwist',
|
||||
// Add more animation names here as you implement them
|
||||
}
|
||||
|
||||
function App() {
|
||||
// Leva controls for animation selection and global settings
|
||||
const { selectedAnimation, speedMultiplier } = useControls({
|
||||
selectedAnimation: {
|
||||
options: animationOptions,
|
||||
value: animationOptions.Phyllotaxis, // Default selection
|
||||
label: 'Animation',
|
||||
},
|
||||
speedMultiplier: {
|
||||
value: 1.0, // Global speed multiplier
|
||||
min: 0.01,
|
||||
max: 5.0,
|
||||
step: 0.01,
|
||||
label: 'Global Speed',
|
||||
},
|
||||
})
|
||||
|
||||
// Function to render the selected animation component
|
||||
const renderSelectedAnimation = () => {
|
||||
switch (selectedAnimation) {
|
||||
case animationOptions.Phyllotaxis:
|
||||
return <PhyllotaxisSystem speedMultiplier={speedMultiplier} />
|
||||
// case animationOptions.PolyTwist:
|
||||
// return <PolyTwistSystem speedMultiplier={speedMultiplier} />
|
||||
// Add cases for other animations
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Leva panel will be added here automatically */}
|
||||
<Leva /> {/* Add the Leva panel, initially collapsed */}
|
||||
<Canvas camera={{ position: [0, 0, 500], fov: 75 }}> {/* Adjusted camera Z */}
|
||||
<ambientLight intensity={0.8} /> {/* Increased ambient light */}
|
||||
<pointLight position={[100, 100, 100]} intensity={1.5} /> {/* Adjusted point light */}
|
||||
{renderSelectedAnimation()} {/* Render the chosen animation */}
|
||||
<OrbitControls />
|
||||
</Canvas>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
130
Tests/react (rename)/src/PhyllotaxisSystem.jsx
Normal file
130
Tests/react (rename)/src/PhyllotaxisSystem.jsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import React, { useRef, useMemo } from 'react';
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import * as THREE from 'three';
|
||||
import { useControls, folder } from 'leva'; // Import leva controls
|
||||
|
||||
// Default values matching original config
|
||||
const DEFAULTS = {
|
||||
width: 24,
|
||||
nMax: 300,
|
||||
color1: '#2D81FC',
|
||||
color2: '#FC0362',
|
||||
pointSize: 5.0, // Added point size control
|
||||
};
|
||||
|
||||
function PhyllotaxisSystem({ speedMultiplier }) { // Accept global speed multiplier
|
||||
const pointsRef = useRef();
|
||||
const positionBufferRef = useRef();
|
||||
const colorBufferRef = useRef();
|
||||
|
||||
// Leva controls specific to Phyllotaxis
|
||||
const { width, nMax, color1, color2, pointSize } = useControls('Phyllotaxis', {
|
||||
// Group controls in a folder
|
||||
width: { value: DEFAULTS.width, min: 1, max: 50, step: 1, label: 'Width Factor' },
|
||||
nMax: { value: DEFAULTS.nMax, min: 10, max: 5000, step: 10, label: 'Particle Count' },
|
||||
pointSize: { value: DEFAULTS.pointSize, min: 0.1, max: 20, step: 0.1, label: 'Point Size' },
|
||||
Colors: folder({ // Sub-folder for colors
|
||||
color1: { value: DEFAULTS.color1, label: 'Color 1' },
|
||||
color2: { value: DEFAULTS.color2, label: 'Color 2' },
|
||||
})
|
||||
});
|
||||
|
||||
// Memoize particle data generation, recalculate if controls change
|
||||
const particles = useMemo(() => {
|
||||
const positions = new Float32Array(nMax * 3);
|
||||
const colors = new Float32Array(nMax * 3);
|
||||
const tempColor = new THREE.Color();
|
||||
const col1 = new THREE.Color(color1);
|
||||
const col2 = new THREE.Color(color2);
|
||||
|
||||
for (let i = 0; i < nMax; i++) {
|
||||
const n = i;
|
||||
const radius = width * Math.sqrt(n);
|
||||
const angle = 0; // Initial angle
|
||||
|
||||
positions[i * 3] = radius * Math.cos(angle);
|
||||
positions[i * 3 + 1] = radius * Math.sin(angle);
|
||||
positions[i * 3 + 2] = 0;
|
||||
|
||||
tempColor.copy(col1).lerp(col2, n / nMax);
|
||||
colors[i * 3] = tempColor.r;
|
||||
colors[i * 3 + 1] = tempColor.g;
|
||||
colors[i * 3 + 2] = tempColor.b;
|
||||
}
|
||||
return { positions, colors };
|
||||
// Dependencies: recalculate when these control values change
|
||||
}, [nMax, width, color1, color2]);
|
||||
|
||||
// Update positions in the animation loop
|
||||
useFrame((state) => {
|
||||
if (!positionBufferRef.current || !colorBufferRef.current) return;
|
||||
|
||||
// Use combined speed (global * local if needed, here just global)
|
||||
const time = state.clock.elapsedTime * speedMultiplier * 0.3; // Adjusted speed factor
|
||||
const positions = positionBufferRef.current.array;
|
||||
|
||||
for (let i = 0; i < nMax; i++) {
|
||||
const n = i;
|
||||
const radius = width * Math.sqrt(n);
|
||||
const angle = n * time; // Angle driven by time and index
|
||||
|
||||
positions[i * 3] = radius * Math.cos(angle);
|
||||
positions[i * 3 + 1] = radius * Math.sin(angle);
|
||||
// z remains 0
|
||||
}
|
||||
positionBufferRef.current.needsUpdate = true;
|
||||
|
||||
// Update point size if material exists
|
||||
if (pointsRef.current && pointsRef.current.material) {
|
||||
pointsRef.current.material.size = pointSize;
|
||||
}
|
||||
});
|
||||
|
||||
// Effect to update buffers when particles data changes (due to control changes)
|
||||
React.useEffect(() => {
|
||||
if (positionBufferRef.current) {
|
||||
positionBufferRef.current.array = particles.positions;
|
||||
positionBufferRef.current.needsUpdate = true;
|
||||
}
|
||||
if (colorBufferRef.current) {
|
||||
colorBufferRef.current.array = particles.colors;
|
||||
colorBufferRef.current.needsUpdate = true;
|
||||
}
|
||||
}, [particles]);
|
||||
|
||||
|
||||
return (
|
||||
<points ref={pointsRef}>
|
||||
<bufferGeometry>
|
||||
{/* Use keys to force re-creation if nMax changes drastically */}
|
||||
<bufferAttribute
|
||||
key={`pos-${nMax}`}
|
||||
ref={positionBufferRef}
|
||||
attach="attributes-position"
|
||||
count={particles.positions.length / 3} // Use actual length
|
||||
array={particles.positions}
|
||||
itemSize={3}
|
||||
usage={THREE.DynamicDrawUsage} // Mark as dynamic
|
||||
/>
|
||||
<bufferAttribute
|
||||
key={`col-${nMax}`}
|
||||
ref={colorBufferRef}
|
||||
attach="attributes-color"
|
||||
count={particles.colors.length / 3} // Use actual length
|
||||
array={particles.colors}
|
||||
itemSize={3}
|
||||
usage={THREE.DynamicDrawUsage} // Mark as dynamic
|
||||
/>
|
||||
</bufferGeometry>
|
||||
<pointsMaterial
|
||||
size={pointSize}
|
||||
vertexColors={true}
|
||||
sizeAttenuation={true}
|
||||
depthWrite={false} // Often good for particles
|
||||
blending={THREE.AdditiveBlending} // Example blending mode
|
||||
/>
|
||||
</points>
|
||||
);
|
||||
}
|
||||
|
||||
export default PhyllotaxisSystem;
|
||||
1
Tests/react (rename)/src/assets/react.svg
Normal file
1
Tests/react (rename)/src/assets/react.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
44
Tests/react (rename)/src/filters.js
Normal file
44
Tests/react (rename)/src/filters.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// filepath: c:\Users\samkl\Documents\GitHub\animate\docs\react\src\filters.js
|
||||
import * as THREE from 'three'; // Might be needed for future filters
|
||||
|
||||
// Additive filter application function
|
||||
export function applyFilters(baseValue, filters = [], time) {
|
||||
let offset = 0;
|
||||
filters.forEach(filter => {
|
||||
switch (filter.type) {
|
||||
case 'sin':
|
||||
// Ensure defaults if properties are missing
|
||||
offset += Math.sin(time * (filter.frequency ?? 1)) * (filter.amplitude ?? 0);
|
||||
break;
|
||||
// --- Add Noise Filter ---
|
||||
case 'noise':
|
||||
// Simple pseudo-random noise using time and an offset
|
||||
// Use a combination of sin functions for a smoother noise
|
||||
const noiseVal = (Math.sin(time * (filter.frequency ?? 1) * 1.3 + filter.id * 10) + Math.sin(time * (filter.frequency ?? 1) * 2.7 + filter.id * 5)) / 2;
|
||||
offset += noiseVal * (filter.amplitude ?? 0);
|
||||
break;
|
||||
// --- End Noise Filter ---
|
||||
default:
|
||||
console.warn(`Unknown filter type: ${filter.type}`);
|
||||
}
|
||||
});
|
||||
return baseValue + offset;
|
||||
}
|
||||
|
||||
// Function to get default parameters for a filter type
|
||||
export function getDefaultFilterParams(type) {
|
||||
switch (type) {
|
||||
case 'sin':
|
||||
return { amplitude: 10, frequency: 1 };
|
||||
case 'noise': // Default for noise
|
||||
return { amplitude: 5, frequency: 1 }; // Added frequency for noise control
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Available filter types (for dropdowns)
|
||||
export const FILTER_TYPES = {
|
||||
SIN: 'sin',
|
||||
NOISE: 'noise',
|
||||
};
|
||||
87
Tests/react (rename)/src/index.css
Normal file
87
Tests/react (rename)/src/index.css
Normal file
@@ -0,0 +1,87 @@
|
||||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #000000;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
/* Remove display flex and place-items */
|
||||
/* display: flex; */
|
||||
/* place-items: center; */
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
overflow: hidden; /* Prevent scrollbars from body */
|
||||
}
|
||||
|
||||
/* Make root and canvas full screen */
|
||||
#root {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* Remove text-align center if you don't want leva centered initially */
|
||||
/* text-align: center; */
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block; /* Ensure canvas takes up block space */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
8
Tests/react (rename)/src/main.jsx
Normal file
8
Tests/react (rename)/src/main.jsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import React from 'react' // Use React instead of StrictMode for now
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<App />
|
||||
)
|
||||
138
Tests/react (rename)/src/useFilteredControl.js
Normal file
138
Tests/react (rename)/src/useFilteredControl.js
Normal file
@@ -0,0 +1,138 @@
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import { button, buttonGroup, folder, monitor } from 'leva';
|
||||
import { applyFilters, getDefaultFilterParams, FILTER_TYPES } from './filters';
|
||||
|
||||
/**
|
||||
* Custom hook to manage a numeric control value with additive filters in Leva.
|
||||
*
|
||||
* @param {string} key - A unique key for this control (used in Leva schema).
|
||||
* @param {number} initialValue - The initial base value.
|
||||
* @param {object} options - Leva control options (min, max, step, label, etc.).
|
||||
* @returns {[Function, object]} A tuple containing:
|
||||
* - A function `getFilteredValue(time)` that returns the value with filters applied for the given time.
|
||||
* - A Leva schema fragment for this control and its filters.
|
||||
*/
|
||||
export function useFilteredControl(key, initialValue, options = {}) {
|
||||
const [baseValue, setBaseValue] = useState(initialValue);
|
||||
const [filters, setFilters] = useState([]);
|
||||
|
||||
const label = options.label || key;
|
||||
|
||||
// --- Filter Management Functions ---
|
||||
const addFilter = useCallback((type) => {
|
||||
const newFilter = {
|
||||
id: Date.now() + Math.random(),
|
||||
type: type,
|
||||
...getDefaultFilterParams(type),
|
||||
};
|
||||
setFilters((prev) => [...prev, newFilter]);
|
||||
}, []);
|
||||
|
||||
const removeFilter = useCallback((id) => {
|
||||
setFilters((prev) => prev.filter((f) => f.id !== id));
|
||||
}, []);
|
||||
|
||||
const updateFilter = useCallback((id, newParams) => {
|
||||
setFilters((prev) =>
|
||||
prev.map((f) => (f.id === id ? { ...f, ...newParams } : f))
|
||||
);
|
||||
}, []);
|
||||
|
||||
const clearFilters = useCallback(() => {
|
||||
setFilters([]);
|
||||
}, []);
|
||||
|
||||
// --- Leva Schema Generation ---
|
||||
const schema = useMemo(() => {
|
||||
const controlSchema = {};
|
||||
|
||||
// 1. Base Value Slider
|
||||
controlSchema[key] = {
|
||||
...options,
|
||||
value: baseValue,
|
||||
label: `Base ${label}`,
|
||||
onChange: setBaseValue,
|
||||
};
|
||||
|
||||
// 2. Filters Folder
|
||||
const filterControls = {};
|
||||
// Prefix keys with the control key to ensure uniqueness
|
||||
filterControls[`${key}_Add Filter`] = buttonGroup({
|
||||
// Dynamically create buttons for available filter types
|
||||
...Object.entries(FILTER_TYPES).reduce((acc, [name, type]) => {
|
||||
acc[name] = () => addFilter(type);
|
||||
return acc;
|
||||
}, {}),
|
||||
});
|
||||
filterControls[`${key}_Applied`] = monitor(filters.length, { graph: false });
|
||||
filterControls[`${key}_Clear`] = button(clearFilters, {
|
||||
label: 'Clear All',
|
||||
disabled: filters.length === 0
|
||||
});
|
||||
|
||||
// 3. Individual Filter Controls
|
||||
filters.forEach((filter, index) => {
|
||||
const filterKey = `${key}_filter_${filter.id}`;
|
||||
const filterFolderContent = {};
|
||||
|
||||
filterFolderContent[`type_${filter.id}`] = {
|
||||
value: filter.type,
|
||||
options: Object.values(FILTER_TYPES),
|
||||
label: 'Type',
|
||||
onChange: (newType) => updateFilter(filter.id, { type: newType, ...getDefaultFilterParams(newType) }),
|
||||
};
|
||||
|
||||
// Common Amplitude Control (adjust range based on param if needed)
|
||||
filterFolderContent[`amp_${filter.id}`] = {
|
||||
value: filter.amplitude,
|
||||
min: 0,
|
||||
max: options.max ? options.max * 0.5 : 50, // Example: Max amplitude is half of base max
|
||||
step: (options.max ? options.max * 0.5 : 50) / 100, // Example step
|
||||
label: 'Amplitude',
|
||||
onChange: (v) => updateFilter(filter.id, { amplitude: v }),
|
||||
};
|
||||
|
||||
// Common Frequency Control
|
||||
filterFolderContent[`freq_${filter.id}`] = {
|
||||
value: filter.frequency,
|
||||
min: 0.1,
|
||||
max: 10,
|
||||
step: 0.1,
|
||||
label: 'Frequency',
|
||||
onChange: (v) => updateFilter(filter.id, { frequency: v }),
|
||||
};
|
||||
|
||||
filterFolderContent[`remove_${filter.id}`] = button(() => removeFilter(filter.id), { label: 'Remove' });
|
||||
|
||||
// Add the folder for this specific filter
|
||||
filterControls[filterKey] = folder(filterFolderContent, {
|
||||
collapsed: true,
|
||||
label: `${label} Filter ${index + 1} (${filter.type})`,
|
||||
});
|
||||
});
|
||||
|
||||
// Add the main filters folder under the base control's key
|
||||
controlSchema[`${label} Filters`] = folder(filterControls);
|
||||
|
||||
return controlSchema;
|
||||
|
||||
}, [key, label, options, baseValue, filters, addFilter, removeFilter, updateFilter, clearFilters]);
|
||||
|
||||
// --- Filtered Value Calculation ---
|
||||
const getFilteredValue = useCallback((time) => {
|
||||
// Ensure baseValue is treated as a number
|
||||
const numericBaseValue = Number(baseValue);
|
||||
if (isNaN(numericBaseValue)) {
|
||||
console.warn(`Base value for ${key} is not a number:`, baseValue);
|
||||
return 0; // Or initialValue
|
||||
}
|
||||
const filtered = applyFilters(numericBaseValue, filters, time);
|
||||
// Optional: Clamp the filtered value to the original min/max if desired
|
||||
if (options.min !== undefined && options.max !== undefined) {
|
||||
return Math.max(options.min, Math.min(filtered, options.max));
|
||||
}
|
||||
return filtered;
|
||||
}, [baseValue, filters, key, options.min, options.max]);
|
||||
|
||||
return [getFilteredValue, schema];
|
||||
}
|
||||
7
Tests/react (rename)/vite.config.js
Normal file
7
Tests/react (rename)/vite.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
185
Tests/tomp4_test/fresh.html
Normal file
185
Tests/tomp4_test/fresh.html
Normal file
@@ -0,0 +1,185 @@
|
||||
<!-- <script src="https://unpkg.com/@ffmpeg/ffmpeg@0.8.1/dist/ffmpeg.min.js"></script> -->
|
||||
<script src="https://unpkg.com/@ffmpeg/ffmpeg@0.10.0/dist/ffmpeg.min.js"></script>
|
||||
|
||||
<canvas id="canvas" style="height: 900px; width: 900px"></canvas>
|
||||
|
||||
<video id="myVideo" controls="controls"></video>
|
||||
<video id="output-video" controls="controls"></video>
|
||||
|
||||
<a id="dl" href="" download="download.mp4"></a>
|
||||
|
||||
<div id="message"></div>
|
||||
|
||||
<script>
|
||||
let c = document.getElementById("canvas");
|
||||
let ctx = c.getContext("2d");
|
||||
// ctx.canvas.width = window.innerWidth;
|
||||
// ctx.canvas.height = window.innerHeight;
|
||||
ctx.canvas.width = 900;
|
||||
ctx.canvas.height = 900;
|
||||
|
||||
centerX = ctx.canvas.width / 2;
|
||||
centerY = ctx.canvas.height / 2;
|
||||
|
||||
let deg_per_sec = 30 / 5000;
|
||||
let timeLimit = 10;
|
||||
let fps = 60;
|
||||
|
||||
rotation = 0; //was = j = angle
|
||||
currentFrame = 0; //was = i
|
||||
|
||||
const { createFFmpeg } = FFmpeg;
|
||||
const ffmpeg = createFFmpeg({
|
||||
log: true,
|
||||
});
|
||||
|
||||
const transcode = async (webcamData) => {
|
||||
const message = document.getElementById("message");
|
||||
const name = "record.webm";
|
||||
await ffmpeg.load();
|
||||
message.innerHTML = "Start transcoding";
|
||||
await ffmpeg.write(name, webcamData);
|
||||
await ffmpeg.transcode(name, "output.mp4");
|
||||
message.innerHTML = "Complete transcoding";
|
||||
const data = ffmpeg.read("output.mp4");
|
||||
|
||||
const video = document.getElementById("output-video");
|
||||
video.src = URL.createObjectURL(
|
||||
new Blob([data.buffer], { type: "video/mp4" })
|
||||
);
|
||||
dl.href = video.src;
|
||||
dl.innerHTML = "download mp4";
|
||||
};
|
||||
|
||||
fn().then(async ({ url, blob }) => {
|
||||
transcode(new Uint8Array(await blob.arrayBuffer()));
|
||||
});
|
||||
|
||||
function fn() {
|
||||
var recordedChunks = [];
|
||||
|
||||
var time = 0;
|
||||
var canvas = document.getElementById("canvas");
|
||||
|
||||
return new Promise(function (res, rej) {
|
||||
var stream = canvas.captureStream(60);
|
||||
|
||||
mediaRecorder = new MediaRecorder(stream, {
|
||||
mimeType: "video/webm; codecs=vp9",
|
||||
});
|
||||
|
||||
mediaRecorder.start(time);
|
||||
|
||||
mediaRecorder.ondataavailable = function (e) {
|
||||
recordedChunks.push(event.data);
|
||||
// for demo, removed stop() call to capture more than one frame
|
||||
};
|
||||
|
||||
mediaRecorder.onstop = function (event) {
|
||||
var blob = new Blob(recordedChunks, {
|
||||
type: "video/webm",
|
||||
});
|
||||
var url = URL.createObjectURL(blob);
|
||||
res({ url, blob }); // resolve both blob and url in an object
|
||||
|
||||
myVideo.src = url;
|
||||
// removed data url conversion for brevity
|
||||
};
|
||||
|
||||
// for demo, draw random lines and then stop recording
|
||||
// var i = 0,
|
||||
// tid = setInterval(() => {
|
||||
// if (currentFrame > timeLimit / (1 / fps)) {
|
||||
// // draw 20 lines
|
||||
// clearInterval(tid);
|
||||
// mediaRecorder.stop();
|
||||
// }
|
||||
// // let canvas = document.querySelector("canvas");
|
||||
// // let cx = canvas.getContext("2d");
|
||||
// // cx.beginPath();
|
||||
// // cx.strokeStyle = "green";
|
||||
// // cx.moveTo(Math.random() * 600, Math.random() * 600);
|
||||
// // cx.lineTo(Math.random() * 600, Math.random() * 600);
|
||||
// // cx.stroke();
|
||||
|
||||
// render_clear();
|
||||
// // Draw_Phyllotaxis(rotation + 3.1);
|
||||
// Draw_Phyllotaxis(rotation + 1.5);
|
||||
|
||||
// rotation += deg_per_sec / fps; // was = j = angle, now = rotation
|
||||
// currentFrame += 1; // was = i
|
||||
// }, 1000 / fps);
|
||||
function render() {
|
||||
if (currentFrame < timeLimit / (1 / fps)) {
|
||||
setTimeout(() => {
|
||||
render();
|
||||
render_clear();
|
||||
|
||||
// Draw_Phyllotaxis(rotation + 3.1);
|
||||
Draw_Phyllotaxis(rotation + 1.5);
|
||||
console.log(rotation + 3.1);
|
||||
// Draw_Phyllotaxis(rotation/5000);
|
||||
|
||||
// Draw_center(); //Debugging
|
||||
// DrawBorder();
|
||||
}, 1000 / fps);
|
||||
rotation += deg_per_sec / fps; // was = j = angle, now = rotation
|
||||
currentFrame += 1; // was = i
|
||||
}
|
||||
else{
|
||||
mediaRecorder.stop();
|
||||
}
|
||||
}
|
||||
|
||||
render();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function Draw_Phyllotaxis(angle) {
|
||||
// colour1 = [255, 170, 0];
|
||||
// colour2 = [255, 0, 221];
|
||||
colour1 = [45, 129, 252];
|
||||
colour2 = [252, 3, 98];
|
||||
|
||||
var c = 24; //something to do with width. but not width
|
||||
|
||||
for (let n = 0; n < 300; n += 1) {
|
||||
ncolour = LerpRGB(colour1, colour2, Math.cos(rad(n / 2)));
|
||||
var a = n * angle; //137.5;
|
||||
var r = c * Math.sqrt(n);
|
||||
var x = r * Math.cos(a) + centerX;
|
||||
var y = r * Math.sin(a) + centerY;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, 8, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = colourToText(ncolour);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
function rad(degrees) {
|
||||
var pi = Math.PI;
|
||||
return degrees * (pi / 180);
|
||||
}
|
||||
|
||||
function LerpRGB(a, b, t) {
|
||||
if (t < 0) {
|
||||
t *= -1;
|
||||
}
|
||||
var newColor = [0, 0, 0];
|
||||
newColor[0] = a[0] + (b[0] - a[0]) * t;
|
||||
newColor[1] = a[1] + (b[1] - a[1]) * t;
|
||||
newColor[2] = a[2] + (b[2] - a[2]) * t;
|
||||
return newColor;
|
||||
}
|
||||
|
||||
function colourToText(colour) {
|
||||
return "rgb(" + colour[0] + "," + colour[1] + "," + colour[2] + ")";
|
||||
}
|
||||
|
||||
function render_clear() {
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
}
|
||||
</script>
|
||||
177
Tests/tomp4_test/phyllotaxis.html
Normal file
177
Tests/tomp4_test/phyllotaxis.html
Normal file
@@ -0,0 +1,177 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- ahhh -->
|
||||
|
||||
<body style="margin: 0; overflow: hidden">
|
||||
<canvas
|
||||
id="myCanvas"
|
||||
width="10"
|
||||
height="10"
|
||||
style="display: block; box-sizing: border-box"
|
||||
>
|
||||
Your browser does not support the HTML5 canvas tag.</canvas
|
||||
>
|
||||
<script src="https://unpkg.com/@ffmpeg/ffmpeg@0.10.0/dist/ffmpeg.min.js"></script>
|
||||
<script>
|
||||
const { createFFmpeg } = FFmpeg;
|
||||
const ffmpeg = createFFmpeg({
|
||||
log: true,
|
||||
});
|
||||
|
||||
const transcode = async (webcamData) => {
|
||||
const message = document.getElementById("message");
|
||||
const name = "record.webm";
|
||||
await ffmpeg.load();
|
||||
message.innerHTML = "Start transcoding";
|
||||
await ffmpeg.write(name, webcamData);
|
||||
await ffmpeg.transcode(name, "output.mp4");
|
||||
message.innerHTML = "Complete transcoding";
|
||||
const data = ffmpeg.read("output.mp4");
|
||||
|
||||
const video = document.getElementById("output-video");
|
||||
video.src = URL.createObjectURL(
|
||||
new Blob([data.buffer], { type: "video/mp4" })
|
||||
);
|
||||
dl.href = video.src;
|
||||
dl.innerHTML = "download mp4";
|
||||
};
|
||||
|
||||
fn().then(async ({ url, blob }) => {
|
||||
transcode(new Uint8Array(await blob.arrayBuffer()));
|
||||
});
|
||||
|
||||
function fn() {
|
||||
var recordedChunks = [];
|
||||
|
||||
var time = 0;
|
||||
var canvas = document.getElementById("canvas");
|
||||
|
||||
return new Promise(function (res, rej) {
|
||||
var stream = canvas.captureStream(60);
|
||||
|
||||
mediaRecorder = new MediaRecorder(stream, {
|
||||
mimeType: "video/webm; codecs=vp9",
|
||||
});
|
||||
|
||||
mediaRecorder.start(time);
|
||||
|
||||
mediaRecorder.ondataavailable = function (e) {
|
||||
recordedChunks.push(event.data);
|
||||
// for demo, removed stop() call to capture more than one frame
|
||||
};
|
||||
|
||||
mediaRecorder.onstop = function (event) {
|
||||
var blob = new Blob(recordedChunks, {
|
||||
type: "video/webm",
|
||||
});
|
||||
var url = URL.createObjectURL(blob);
|
||||
res({ url, blob }); // resolve both blob and url in an object
|
||||
|
||||
myVideo.src = url;
|
||||
// removed data url conversion for brevity
|
||||
};
|
||||
|
||||
// for demo, draw random lines and then stop recording
|
||||
var i = 0,
|
||||
tid = setInterval(() => {
|
||||
if (i++ > 20) {
|
||||
// draw 20 lines
|
||||
clearInterval(tid);
|
||||
mediaRecorder.stop();
|
||||
}
|
||||
let canvas = document.querySelector("canvas");
|
||||
let cx = canvas.getContext("2d");
|
||||
cx.beginPath();
|
||||
cx.strokeStyle = "green";
|
||||
cx.moveTo(Math.random() * 100, Math.random() * 100);
|
||||
cx.lineTo(Math.random() * 100, Math.random() * 100);
|
||||
cx.stroke();
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
|
||||
let c = document.getElementById("myCanvas");
|
||||
let ctx = c.getContext("2d");
|
||||
ctx.canvas.width = window.innerWidth;
|
||||
ctx.canvas.height = window.innerHeight;
|
||||
|
||||
let deg_per_sec = 30 / 5000;
|
||||
let time = 0.1;
|
||||
let fps = 60;
|
||||
|
||||
centerX = ctx.canvas.width / 2;
|
||||
centerY = ctx.canvas.height / 2;
|
||||
|
||||
rotation = 0; //was = j = angle
|
||||
currentFrame = 0; //was = i
|
||||
|
||||
function render() {
|
||||
if (currentFrame < time / (1 / fps)) {
|
||||
setTimeout(() => {
|
||||
render();
|
||||
render_clear();
|
||||
|
||||
// Draw_Phyllotaxis(rotation + 3.1);
|
||||
Draw_Phyllotaxis(rotation + 1.5);
|
||||
console.log(rotation + 3.1);
|
||||
// Draw_Phyllotaxis(rotation/5000);
|
||||
|
||||
// Draw_center(); //Debugging
|
||||
// DrawBorder();
|
||||
}, 1000 / fps);
|
||||
rotation += deg_per_sec / fps; // was = j = angle, now = rotation
|
||||
currentFrame += 1; // was = i
|
||||
}
|
||||
}
|
||||
|
||||
render();
|
||||
|
||||
function Draw_Phyllotaxis(angle) {
|
||||
// colour1 = [255, 170, 0];
|
||||
// colour2 = [255, 0, 221];
|
||||
colour1 = [45, 129, 252];
|
||||
colour2 = [252, 3, 98];
|
||||
|
||||
var c = 24; //something to do with width. but not width
|
||||
|
||||
for (let n = 0; n < 300; n += 1) {
|
||||
ncolour = LerpRGB(colour1, colour2, Math.cos(rad(n / 2)));
|
||||
var a = n * angle; //137.5;
|
||||
var r = c * Math.sqrt(n);
|
||||
var x = r * Math.cos(a) + centerX;
|
||||
var y = r * Math.sin(a) + centerY;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, 8, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = colourToText(ncolour);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
function rad(degrees) {
|
||||
var pi = Math.PI;
|
||||
return degrees * (pi / 180);
|
||||
}
|
||||
|
||||
function LerpRGB(a, b, t) {
|
||||
if (t < 0) {
|
||||
t *= -1;
|
||||
}
|
||||
var newColor = [0, 0, 0];
|
||||
newColor[0] = a[0] + (b[0] - a[0]) * t;
|
||||
newColor[1] = a[1] + (b[1] - a[1]) * t;
|
||||
newColor[2] = a[2] + (b[2] - a[2]) * t;
|
||||
return newColor;
|
||||
}
|
||||
|
||||
function colourToText(colour) {
|
||||
return "rgb(" + colour[0] + "," + colour[1] + "," + colour[2] + ")";
|
||||
}
|
||||
|
||||
function render_clear() {
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
37
Tests/webGl/New/index.html
Normal file
37
Tests/webGl/New/index.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>WebGL Template</title>
|
||||
<style>
|
||||
/* Set canvas to occupy the full width and height of the window */
|
||||
canvas {
|
||||
display: block;
|
||||
width: 800px;
|
||||
height: 800px;
|
||||
margin: 0 560px;
|
||||
padding: 0;
|
||||
|
||||
}
|
||||
|
||||
.canvas2d {
|
||||
position: absolute !important;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
body{
|
||||
margin:0
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
<canvas id="canvas2d" class="canvas2d"></canvas>
|
||||
<script src="index.js">
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
194
Tests/webGl/New/index.js
Normal file
194
Tests/webGl/New/index.js
Normal file
@@ -0,0 +1,194 @@
|
||||
// Get the canvas element and create a WebGL context
|
||||
const canvas = document.getElementById('canvas');
|
||||
const canvas2d = document.getElementById('canvas2d');
|
||||
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
||||
let ctx = canvas2d.getContext("2d");
|
||||
ctx.canvas.width = 800;
|
||||
ctx.canvas.height = 800;
|
||||
|
||||
// Check if WebGL is available
|
||||
if (!gl) {
|
||||
alert('WebGL not supported in your browser.');
|
||||
} else {
|
||||
// Set the canvas size and WebGL viewport
|
||||
canvas.width = 800;
|
||||
canvas.height = 800;
|
||||
let centerX = ctx.canvas.width / 2;
|
||||
let centerY = ctx.canvas.height / 2;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Define the vertex shader source code
|
||||
const glsl = x => x;
|
||||
const vertexShaderSource = glsl`
|
||||
attribute vec2 position;
|
||||
uniform float u_rotation;
|
||||
uniform vec2 u_pos;
|
||||
void main() {
|
||||
float centerX = 0.0;
|
||||
float centerY = 0.0;
|
||||
float xPos = position.x;
|
||||
float yPos = position.y;
|
||||
float x = xPos * cos(u_rotation) - yPos * sin(u_rotation);
|
||||
float y = yPos * cos(u_rotation) + xPos * sin(u_rotation);
|
||||
gl_Position = vec4(x+u_pos.x , y+u_pos.y , 0, 1);
|
||||
}
|
||||
`;
|
||||
|
||||
// Define the fragment shader source code
|
||||
const fragmentShaderSource = `
|
||||
precision mediump float;
|
||||
uniform vec4 fColor;
|
||||
void main() {
|
||||
gl_FragColor = fColor;
|
||||
}
|
||||
`;
|
||||
|
||||
// Create and compile the vertex and fragment shaders
|
||||
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
|
||||
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
|
||||
|
||||
// Create a program, attach the shaders, and link the program
|
||||
const program = createProgram(gl, vertexShader, fragmentShader);
|
||||
|
||||
// Get the attribute and uniform locations
|
||||
const positionAttributeLocation = gl.getAttribLocation(program, 'position');
|
||||
const positionLocalAttributeLocation = gl.getUniformLocation(program, 'u_pos');
|
||||
const rotationUniformLocation = gl.getUniformLocation(program, 'u_rotation');
|
||||
const fColorLocation = gl.getUniformLocation(program, "fColor");
|
||||
|
||||
// Create a buffer to store vertex positions
|
||||
const positionBuffer = gl.createBuffer();
|
||||
|
||||
function polyPoints(sides) {
|
||||
let pointsArr = [];
|
||||
let width = 1;
|
||||
let rotation = 0
|
||||
pointsArr.push(width * Math.cos((rotation * Math.PI) / 180));
|
||||
pointsArr.push(width * Math.sin((rotation * Math.PI) / 180))
|
||||
for (let i = 0; i < sides; i++) {
|
||||
pointsArr.push(width * Math.cos((i * 2 * Math.PI) / sides + (rotation * Math.PI) / 180))
|
||||
pointsArr.push(width * Math.sin((i * 2 * Math.PI) / sides + (rotation * Math.PI) / 180))
|
||||
}
|
||||
return pointsArr
|
||||
}
|
||||
|
||||
|
||||
function rad(degrees) {
|
||||
var pi = Math.PI;
|
||||
return degrees * (pi / 180);
|
||||
}
|
||||
|
||||
// Function to create and compile a shader
|
||||
function createShader(gl, type, source) {
|
||||
const shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
return shader;
|
||||
}
|
||||
console.error(gl.getShaderInfoLog(shader));
|
||||
gl.deleteShader(shader);
|
||||
}
|
||||
|
||||
// Function to create a program and link shaders
|
||||
function createProgram(gl, vertexShader, fragmentShader) {
|
||||
const program = gl.createProgram();
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
||||
return program;
|
||||
}
|
||||
console.error(gl.getProgramInfoLog(program));
|
||||
gl.deleteProgram(program);
|
||||
}
|
||||
// Function to draw the scene
|
||||
let prevTime = 0
|
||||
function drawScene(time) {
|
||||
// Convert time to seconds
|
||||
// console.log(time - prevTime)
|
||||
prevTime = time
|
||||
time *= 0.001;
|
||||
|
||||
// Clear the canvas with a black background
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
// Use the shader program
|
||||
gl.useProgram(program);
|
||||
|
||||
// Enable the position attribute
|
||||
gl.enableVertexAttribArray(positionAttributeLocation);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
// Create a buffer to store vertex positions
|
||||
let blue = [0.17, 0.49, 0.85] //blue
|
||||
let green = [0.17, 0.85, 0.46] //green
|
||||
let pink = [0.96, 0.24, 0.86] //green
|
||||
|
||||
render_clear();
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
DrawPolygon(4, 400, (0+time)*i*0.1, "Crimson")
|
||||
// drawPoly(4, time * i * 0.001, blue, 0, 0)
|
||||
}
|
||||
|
||||
Draw_center()
|
||||
drawCircle()
|
||||
// console.log(time)
|
||||
// Request the next frame to be drawn
|
||||
requestAnimationFrame(drawScene);
|
||||
}
|
||||
|
||||
const positions1 = polyPoints(4)
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions1), gl.STATIC_DRAW);
|
||||
function drawPoly(sides, speed, colour, x, y) {
|
||||
gl.uniform1f(rotationUniformLocation, speed);
|
||||
gl.uniform4f(fColorLocation, colour[0], colour[1], colour[2], 1);
|
||||
gl.uniform2f(positionLocalAttributeLocation, x, y);
|
||||
|
||||
gl.drawArrays(gl.LINE_LOOP, 0, positions1.length / 2);
|
||||
}
|
||||
|
||||
// Start rendering the scene
|
||||
requestAnimationFrame(drawScene);
|
||||
function Draw_center() {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX - 400, centerY);
|
||||
ctx.lineTo(centerX + 400, centerY);
|
||||
ctx.moveTo(centerX, centerY - 400);
|
||||
ctx.lineTo(centerX, centerY + 400);
|
||||
ctx.strokeStyle = "green";
|
||||
ctx.stroke();
|
||||
// console.log("drawn center")
|
||||
}
|
||||
function drawCircle() {
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, 400, 0, 2 * Math.PI, false);
|
||||
ctx.strokeStyle = "green";
|
||||
ctx.stroke();
|
||||
}
|
||||
function DrawPolygon(sides, width, rotation, colour) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(
|
||||
centerX + width * Math.cos((rotation * Math.PI) / 180),
|
||||
centerY + width * Math.sin((rotation * Math.PI) / 180)
|
||||
);
|
||||
|
||||
for (var i = 1; i <= sides; i += 1) {
|
||||
ctx.lineTo(
|
||||
centerX + width * Math.cos((i * 2 * Math.PI) / sides + (rotation * Math.PI) / 180),
|
||||
centerY + width * Math.sin((i * 2 * Math.PI) / sides + (rotation * Math.PI) / 180)
|
||||
);
|
||||
}
|
||||
|
||||
ctx.strokeStyle = colour;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.stroke();
|
||||
}
|
||||
function render_clear() {
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
ctx.fillStyle = "rgb(0,0,0,0)";
|
||||
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
}
|
||||
}
|
||||
112
Tests/webGl/circle fractal/css/styles.css
Normal file
112
Tests/webGl/circle fractal/css/styles.css
Normal file
@@ -0,0 +1,112 @@
|
||||
body {
|
||||
-webkit-box-sizing: border-box;
|
||||
/* Safari/Chrome, other WebKit */
|
||||
-moz-box-sizing: border-box;
|
||||
/* Firefox, other Gecko */
|
||||
box-sizing: border-box;
|
||||
/* Opera/IE 8+ */
|
||||
height: 100%
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#toolbar {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
|
||||
position: absolute;
|
||||
padding: 0px 20px 0px 20px;
|
||||
width: 500px;
|
||||
height: 100vh;
|
||||
overflow-y: scroll;
|
||||
background-color: rgb(189, 189, 189);
|
||||
}
|
||||
|
||||
#custom {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
|
||||
/* position: absolute; */
|
||||
padding: 0px 20px 0px 20px;
|
||||
/* width: 500px; */
|
||||
/* height: 100vh; */
|
||||
background-color: rgb(189, 189, 189);
|
||||
}
|
||||
|
||||
.button {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
margin: 8px 0px;
|
||||
}
|
||||
|
||||
.controlFrameButton {
|
||||
width: 8%;
|
||||
}
|
||||
.controlPauseButton {
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
}
|
||||
/* <!-- HTML !-->
|
||||
<button class="button-8" role="button">Button 8</button> */
|
||||
|
||||
/* CSS */
|
||||
.button-8 {
|
||||
background-color: #e1ecf4;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #7aa7c7;
|
||||
box-shadow: rgba(255, 255, 255, .7) 0 1px 0 0 inset;
|
||||
box-sizing: border-box;
|
||||
color: #1f3f55;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
/* font-family: -apple-system,system-ui,"Segoe UI","Liberation Sans",sans-serif; */
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 1.15385;
|
||||
/* margin: 0; */
|
||||
outline: none;
|
||||
padding: 8px .8em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
touch-action: manipulation;
|
||||
vertical-align: baseline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.button-8:hover,
|
||||
.button-8:focus {
|
||||
background-color: #b3d3ea;
|
||||
color: #2c5777;
|
||||
}
|
||||
|
||||
.button-8:focus {
|
||||
box-shadow: 0 0 0 4px rgba(0, 149, 255, .15);
|
||||
}
|
||||
|
||||
.button-8:active {
|
||||
background-color: #a0c7e4;
|
||||
box-shadow: none;
|
||||
color: #2c5777;
|
||||
}
|
||||
|
||||
.buttonReset{
|
||||
background-color: #f4e1e1;
|
||||
}
|
||||
58
Tests/webGl/circle fractal/index.html
Normal file
58
Tests/webGl/circle fractal/index.html
Normal file
@@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- ahhh -->
|
||||
|
||||
<head>
|
||||
<title>Document</title>
|
||||
<link rel="stylesheet" href="./css/styles.css">
|
||||
</head>
|
||||
|
||||
<body style="margin:0;">
|
||||
<canvas id="canvas" style="display: block;box-sizing: border-box;"></canvas>
|
||||
<div id="toolbar">
|
||||
<br>
|
||||
<select id="shape-selector">
|
||||
<option value="EyePrototype">EyePrototype</option>
|
||||
<option value="Nodal_expanding">Nodal_expanding</option>
|
||||
<option value="MaryFace">MaryFace</option>
|
||||
<option value="CircleExpand">CircleExpand</option>
|
||||
<option value="PolyTwistColourWidth">PolyTwistColourWidth</option>
|
||||
<option value="FloralPhyllo">FloralPhyllo</option>
|
||||
<option value="FloralPhyllo_Accident">FloralPhyllo_Accident</option>
|
||||
<option value="Spiral1">Spiral1</option>
|
||||
<option value="FloralAccident">FloralAccident</option>
|
||||
<option value="Phyllotaxis">Phyllotaxis</option>
|
||||
<option value="SquareTwist_angle">SquareTwist_angle</option>
|
||||
</select>
|
||||
<div id="custom"></div>
|
||||
<br>
|
||||
<p>Controls:</p>
|
||||
<p>
|
||||
Press "Space" to pause and start the animation
|
||||
</p>
|
||||
<p>
|
||||
Press "P" to show/hide the control panel
|
||||
</p>
|
||||
<br>
|
||||
<p>Speed</p>
|
||||
<input type="text" id="inputDegPerSec" value="10" onchange="ChangeDegPerSec(this.value)">
|
||||
<br>
|
||||
<p>Controls</p>
|
||||
<div class="controls">
|
||||
<button class="controlFrameButton button-8" onclick="BackwardFrame()"><</button>
|
||||
<button class="controlPauseButton button-8" id="pauseButton" onclick="TogglePause()">Play</button>
|
||||
<button class="controlFrameButton button-8" onclick="ForwardFrame()">></button>
|
||||
</div>
|
||||
<button class="buttonReset button-8" id="resetButton" onclick="Reset()">Reset Rotation</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onclick="manualToggleSettings()" class="button">Show/hide</button>
|
||||
</div>
|
||||
</body>
|
||||
<script src="./js/helper.js" type="text/javascript"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="./js/math.js" type="text/javascript"></script>
|
||||
<script src="./js/objects.js" type="text/javascript"></script>
|
||||
<script src="./js/index.js"></script>
|
||||
|
||||
</html>
|
||||
274
Tests/webGl/circle fractal/js/helper.js
Normal file
274
Tests/webGl/circle fractal/js/helper.js
Normal file
@@ -0,0 +1,274 @@
|
||||
async function fetchConfig(className) {
|
||||
// const config = await $.getJSON("config.json");
|
||||
const config = {
|
||||
|
||||
EyePrototype: [
|
||||
{ type: "range", min: -20, max: 20, defaultValue: 0, property: "Val1" },
|
||||
// { type: "color", defaultValue: "#00fffb", property: "colourPupil" },
|
||||
],
|
||||
};
|
||||
return config[className];
|
||||
}
|
||||
|
||||
function addControl(item, instance) {
|
||||
console.log(item);
|
||||
let parentDiv = document.getElementById("custom");
|
||||
|
||||
let title = document.createElement("p");
|
||||
title.innerText = item.property + ": " + item.defaultValue;
|
||||
title.id = "elText" + item.property;
|
||||
|
||||
let control = document.createElement("input");
|
||||
control.type = item.type;
|
||||
|
||||
if (item.type === "range") {
|
||||
control.min = item.min;
|
||||
control.max = item.max;
|
||||
}
|
||||
|
||||
control.value = item.defaultValue;
|
||||
control.className = "control";
|
||||
control.id = "el" + item.property;
|
||||
|
||||
const listener = (event) => {
|
||||
const newValue = event.target.value;
|
||||
instance[item.property] =
|
||||
item.type === "range" ? parseInt(newValue, 10) : newValue;
|
||||
|
||||
title.innerText = item.property + ": " + newValue;
|
||||
};
|
||||
|
||||
control.addEventListener("input", listener);
|
||||
|
||||
parentDiv.appendChild(title);
|
||||
parentDiv.appendChild(control);
|
||||
|
||||
return { element: control, listener };
|
||||
}
|
||||
|
||||
function drawEyelid(width, x1, y1, colour) {
|
||||
x1 -= centerX;
|
||||
y1 -= centerY;
|
||||
|
||||
const angle = Math.atan2(y1, x1);
|
||||
const cosAngle = Math.cos(angle);
|
||||
const sinAngle = Math.sin(angle);
|
||||
|
||||
const x2 = cosAngle * width;
|
||||
const y2 = sinAngle * width;
|
||||
|
||||
const x3Old = width / 2;
|
||||
const y3Old = width / 2;
|
||||
const x4Old = width / 2;
|
||||
const y4Old = -width / 2;
|
||||
|
||||
const x3 = x3Old * cosAngle - y3Old * sinAngle;
|
||||
const y3 = x3Old * sinAngle + y3Old * cosAngle;
|
||||
const x4 = x4Old * cosAngle - y4Old * sinAngle;
|
||||
const y4 = x4Old * sinAngle + y4Old * cosAngle;
|
||||
|
||||
x1 += centerX;
|
||||
y1 += centerY;
|
||||
const x2Final = x2 + x1;
|
||||
const y2Final = y2 + y1;
|
||||
const x3Final = x3 + x1;
|
||||
const y3Final = y3 + y1;
|
||||
const x4Final = x4 + x1;
|
||||
const y4Final = y4 + y1;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.quadraticCurveTo(x3Final, y3Final, x2Final, y2Final);
|
||||
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.quadraticCurveTo(x4Final, y4Final, x2Final, y2Final);
|
||||
ctx.fillStyle = colour;
|
||||
ctx.fill();
|
||||
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = "black";
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function drawEyelidAccident(x1, y1) {
|
||||
let leafWidth = 120;
|
||||
let leafHeight = 60;
|
||||
x1 -= centerX;
|
||||
y1 -= centerY;
|
||||
let angle = Math.atan(y1 / x1);
|
||||
// if(angle >=Math.PI){
|
||||
// angle -=Math.PI
|
||||
// console.log("greater called")
|
||||
// }
|
||||
angle = Math.abs(angle);
|
||||
let x2Old = 0 + leafWidth;
|
||||
let y2Old = 0;
|
||||
|
||||
let x3Old = 0 + leafWidth / 2;
|
||||
let y3Old = 0 + leafHeight / 2;
|
||||
|
||||
let x4Old = 0 + leafWidth / 2;
|
||||
let y4Old = 0 - leafHeight / 2;
|
||||
|
||||
let x2 = x2Old * Math.cos(angle) - y2Old * Math.sin(angle);
|
||||
let y2 = x2Old * Math.sin(angle) + y2Old * Math.cos(angle);
|
||||
|
||||
let x3 = x3Old * Math.cos(angle) - y3Old * Math.sin(angle);
|
||||
let y3 = x3Old * Math.sin(angle) + y3Old * Math.cos(angle);
|
||||
|
||||
let x4 = x4Old * Math.cos(angle) - y4Old * Math.sin(angle);
|
||||
let y4 = x4Old * Math.sin(angle) + y4Old * Math.cos(angle);
|
||||
|
||||
let oldx1 = x1;
|
||||
let oldy1 = y1;
|
||||
|
||||
x1 += centerX; // +x2/2
|
||||
y1 += centerY; // +x2/2
|
||||
x2 += x1;
|
||||
y2 += y1;
|
||||
x3 += x1;
|
||||
y3 += y1;
|
||||
x4 += x1;
|
||||
y4 += y1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.quadraticCurveTo(x3, y3, x2, y2);
|
||||
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.quadraticCurveTo(x4, y4, x2, y2);
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fill();
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.quadraticCurveTo(x3, y3, x2, y2);
|
||||
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.quadraticCurveTo(x4, y4, x2, y2);
|
||||
ctx.strokeStyle = "orange";
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function DrawPolygon(sides, width, rotation, colour, line_width) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(
|
||||
centerX + width * Math.cos((rotation * Math.PI) / 180),
|
||||
centerY + width * Math.sin((rotation * Math.PI) / 180)
|
||||
);
|
||||
|
||||
for (var i = 1; i <= sides; i += 1) {
|
||||
ctx.lineTo(
|
||||
centerX +
|
||||
width *
|
||||
Math.cos((i * 2 * Math.PI) / sides + (rotation * Math.PI) / 180),
|
||||
centerY +
|
||||
width * Math.sin((i * 2 * Math.PI) / sides + (rotation * Math.PI) / 180)
|
||||
);
|
||||
}
|
||||
ctx.strokeStyle = colour;
|
||||
ctx.lineWidth = line_width;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function rad(degrees) {
|
||||
return (degrees * Math.PI) / 180;
|
||||
}
|
||||
|
||||
function colourToText(colour) {
|
||||
return "rgb(" + colour[0] + "," + colour[1] + "," + colour[2] + ")";
|
||||
}
|
||||
|
||||
|
||||
function waveNormal(x, max) {
|
||||
let val = Math.sin((x / max) * Math.PI * 2 - max * (Math.PI / (max * 2))) / 2 + 0.5
|
||||
return val
|
||||
}
|
||||
|
||||
function LerpHex(a, b, amount) {
|
||||
var ah = parseInt(a.replace(/#/g, ""), 16),
|
||||
ar = ah >> 16,
|
||||
ag = (ah >> 8) & 0xff,
|
||||
ab = ah & 0xff,
|
||||
bh = parseInt(b.replace(/#/g, ""), 16),
|
||||
br = bh >> 16,
|
||||
bg = (bh >> 8) & 0xff,
|
||||
bb = bh & 0xff,
|
||||
rr = ar + amount * (br - ar),
|
||||
rg = ag + amount * (bg - ag),
|
||||
rb = ab + amount * (bb - ab);
|
||||
|
||||
return (
|
||||
"#" + (((1 << 24) + (rr << 16) + (rg << 8) + rb) | 0).toString(16).slice(1)
|
||||
);
|
||||
}
|
||||
|
||||
function LerpRGB(a, b, t) {
|
||||
if (t < 0) {
|
||||
t *= -1;
|
||||
}
|
||||
var newColor = [0, 0, 0];
|
||||
newColor[0] = a[0] + (b[0] - a[0]) * t;
|
||||
newColor[1] = a[1] + (b[1] - a[1]) * t;
|
||||
newColor[2] = a[2] + (b[2] - a[2]) * t;
|
||||
return newColor;
|
||||
}
|
||||
|
||||
function lerpRGB(a, b, t) {
|
||||
const result = [0, 0, 0];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
result[i] = (1 - t) * a[i] + t * b[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function drawCenter(width) {
|
||||
console.log("center?")
|
||||
ctx.strokeStyle = "pink";
|
||||
ctx.lineWidth = 1
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX - width, centerY);
|
||||
ctx.lineTo(centerX + width, centerY);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX, centerY - width);
|
||||
ctx.lineTo(centerX, centerY + width);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function render_clear() {
|
||||
// ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
// ctx.fillStyle = "black";
|
||||
// ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
}
|
||||
|
||||
function rotatePointTmp(x, y, centerXX, centerYY, rotation) {
|
||||
let xFromC = x - centerXX;
|
||||
let yFromC = y - centerYY;
|
||||
let d = (xFromC ** 2 + yFromC ** 2) ** 0.5
|
||||
// let orgAngle = Math.atan2(yFromC/xFromC)
|
||||
let orgAngle = Math.atan2(xFromC, yFromC)
|
||||
let tmp = Math.cos(rad(orgAngle - rotation)) * d
|
||||
// console.log(Math.cos((-90)*(Math.PI/180)))
|
||||
console.log(orgAngle)
|
||||
console.log(rad(rotation))
|
||||
console.log(Math.cos(orgAngle - rad(rotation)) * d)
|
||||
console.log(d)
|
||||
// console.log(d)
|
||||
let newPointX = Math.cos(orgAngle - rad(rotation+90)) * d + centerXX;
|
||||
let newPointY = Math.sin(orgAngle - rad(rotation+90)) * d + centerYY;
|
||||
return [newPointX, newPointY]
|
||||
}
|
||||
|
||||
function rotatePoint(x,y,rotation){
|
||||
let nCos = Math.cos(rad(rotation))
|
||||
// console.log(nCos*(180/Math.PI))
|
||||
// console.log(rad(rotation))
|
||||
let nSin = Math.sin(rad(rotation))
|
||||
let newX = x*nCos - y*nSin
|
||||
let newY = y*nCos + x*nSin
|
||||
return [newX,newY]
|
||||
}
|
||||
148
Tests/webGl/circle fractal/js/index.js
Normal file
148
Tests/webGl/circle fractal/js/index.js
Normal file
@@ -0,0 +1,148 @@
|
||||
//jshint esversion:8
|
||||
let c = document.getElementById("canvas");
|
||||
const gl = canvas.getContext('webgl');
|
||||
// canvas.width = 800;
|
||||
// canvas.height = 800;
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
// let ctx = c.getContext("2d");
|
||||
// ctx.canvas.width = window.innerWidth;
|
||||
// ctx.canvas.height = window.innerHeight;
|
||||
// centerX = ctx.canvas.width / 2;
|
||||
// centerY = ctx.canvas.height / 2;
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
console.log('addEventListener - resize');
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
}, true);
|
||||
|
||||
|
||||
let deg_per_sec = 10;
|
||||
let targetFps = 60;
|
||||
let frameDuration = 1000 / targetFps;
|
||||
|
||||
let rotation = 0; //was = j = angle
|
||||
let paused = true;
|
||||
render_clear();
|
||||
|
||||
let drawObj = null;
|
||||
function createInstance(className, args) {
|
||||
const classMap = {
|
||||
|
||||
EyePrototype: EyePrototype,
|
||||
// Add more class constructors here as needed
|
||||
};
|
||||
|
||||
if (classMap.hasOwnProperty(className)) {
|
||||
return new classMap[className](...args);
|
||||
} else {
|
||||
throw new Error(`Unknown class name: ${className}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function updateDrawObj() {
|
||||
const shapeSelector = document.getElementById("shape-selector");
|
||||
const selectedShape = shapeSelector.value;
|
||||
const config = await fetchConfig(selectedShape);
|
||||
if (drawObj) {
|
||||
drawObj.remove(); // Remove the previous instance
|
||||
}
|
||||
// Extract default values from the configuration
|
||||
const defaultValues = config
|
||||
// .filter((item) => item.type !== "color") // Exclude color inputs
|
||||
.map((item) => item.defaultValue);
|
||||
|
||||
drawObj = createInstance(selectedShape, defaultValues);
|
||||
|
||||
// drawObj = await createShapeWithRandomProperties(813311281, config1);
|
||||
console.log(drawObj)
|
||||
drawObj.initialise(config);
|
||||
}
|
||||
|
||||
updateDrawObj();
|
||||
|
||||
function render() {
|
||||
setTimeout(() => {
|
||||
requestAnimationFrame(() => {
|
||||
render_clear();
|
||||
if (drawObj) {
|
||||
drawObj.draw(rotation);
|
||||
}
|
||||
|
||||
if (!paused) {
|
||||
rotation += deg_per_sec / targetFps;
|
||||
}
|
||||
// drawCenter(300)
|
||||
});
|
||||
render();
|
||||
}, frameDuration);
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById("shape-selector")
|
||||
.addEventListener("change", updateDrawObj);
|
||||
|
||||
let toolbarShowing = true;
|
||||
document.addEventListener("keydown", toggleSettings);
|
||||
|
||||
function manualToggleSettings() {
|
||||
console.log("hi")
|
||||
toolbarShowing = !toolbarShowing;
|
||||
let tb = document.getElementById("toolbar");
|
||||
if (toolbarShowing) {
|
||||
tb.style.display = "flex";
|
||||
} else {
|
||||
tb.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSettings(e) {
|
||||
if (e.key == "p") {
|
||||
toolbarShowing = !toolbarShowing;
|
||||
}
|
||||
if (e.code === "Space") {
|
||||
paused = !paused;
|
||||
}
|
||||
|
||||
let tb = document.getElementById("toolbar");
|
||||
if (toolbarShowing) {
|
||||
tb.style.display = "flex";
|
||||
} else {
|
||||
tb.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function TogglePause() {
|
||||
let pb = document.getElementById("pauseButton");
|
||||
paused = !paused;
|
||||
|
||||
if (paused) {
|
||||
pb.textContent = "Play";
|
||||
} else {
|
||||
pb.textContent = "Pause";
|
||||
}
|
||||
}
|
||||
function Reset() {
|
||||
rotation = 0; //was = j = angle
|
||||
currentFrame = 0;
|
||||
}
|
||||
|
||||
function ForwardFrame() {
|
||||
rotation += deg_per_sec / fps; // was = j = innerRotation, now = rotation
|
||||
currentFrame += 1; // was = i
|
||||
}
|
||||
function BackwardFrame() {
|
||||
rotation -= deg_per_sec / fps; // was = j = innerRotation, now = rotation
|
||||
currentFrame -= 1; // was = i
|
||||
}
|
||||
|
||||
function ChangeDegPerSec(newValue) {
|
||||
deg_per_sec = newValue;
|
||||
}
|
||||
|
||||
window.onload = render;
|
||||
77
Tests/webGl/circle fractal/js/math.js
Normal file
77
Tests/webGl/circle fractal/js/math.js
Normal file
@@ -0,0 +1,77 @@
|
||||
function rotateMatrix2d(p, angle) {
|
||||
// cos0 sin0
|
||||
// -sin0 cos0
|
||||
const angleD = rad(angle);
|
||||
const r = [
|
||||
[Math.cos(angleD), Math.sin(angleD)],
|
||||
[-Math.sin(angleD), Math.cos(angleD)],
|
||||
];
|
||||
const newPoint = [
|
||||
p[0] * r[0][0] + p[1] * r[0][1],
|
||||
p[0] * r[1][0] + p[1] * r[1][1],
|
||||
];
|
||||
return newPoint;
|
||||
}
|
||||
|
||||
function rotateMatrix3dX(p, angle) {
|
||||
// cos0 sin0
|
||||
// -sin0 cos0
|
||||
const angleD = rad(angle);
|
||||
const r = [
|
||||
[1, 0, 0],
|
||||
[0, Math.cos(angleD), -Math.sin(angleD)],
|
||||
[0, Math.sin(angleD), Math.cos(angleD)],
|
||||
];
|
||||
const newPoint = [
|
||||
p[0] * r[0][0] + p[1] * r[0][1] + p[2] * r[0][2],
|
||||
p[0] * r[1][0] + p[1] * r[1][1] + p[2] * r[1][2],
|
||||
p[0] * r[2][0] + p[1] * r[2][1] + p[2] * r[2][2],
|
||||
];
|
||||
return newPoint;
|
||||
}
|
||||
|
||||
function rotateMatrix3dY(p, angle) {
|
||||
// cos0 sin0
|
||||
// -sin0 cos0
|
||||
const angleD = rad(angle);
|
||||
const r = [
|
||||
[Math.cos(angleD), 0, Math.sin(angleD)],
|
||||
[0, 1, 0],
|
||||
[-Math.sin(angleD), 0, Math.cos(angleD)],
|
||||
];
|
||||
const newPoint = [
|
||||
p[0] * r[0][0] + p[1] * r[0][1] + p[2] * r[0][2],
|
||||
p[0] * r[1][0] + p[1] * r[1][1] + p[2] * r[1][2],
|
||||
p[0] * r[2][0] + p[1] * r[2][1] + p[2] * r[2][2],
|
||||
];
|
||||
return newPoint;
|
||||
}
|
||||
function rotateMatrix3dZ(p, angle) {
|
||||
// cos0 sin0
|
||||
// -sin0 cos0
|
||||
const angleD = rad(angle);
|
||||
const r = [
|
||||
[Math.cos(angleD), -Math.sin(angleD), 0],
|
||||
[Math.sin(angleD), Math.cos(angleD), 0],
|
||||
[0, 0, 1],
|
||||
];
|
||||
const newPoint = [
|
||||
p[0] * r[0][0] + p[1] * r[0][1] + p[2] * r[0][2],
|
||||
p[0] * r[1][0] + p[1] * r[1][1] + p[2] * r[1][2],
|
||||
p[0] * r[2][0] + p[1] * r[2][1] + p[2] * r[2][2],
|
||||
];
|
||||
return newPoint;
|
||||
}
|
||||
|
||||
function projectionOrth(v) {
|
||||
const p = [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
];
|
||||
|
||||
const nPoint = [
|
||||
p[0][0] * v[0] + p[0][1] * v[1] + p[0][2] * v[2],
|
||||
p[1][0] * v[0] + p[1][1] * v[1] + p[1][2] * v[2],
|
||||
];
|
||||
return nPoint;
|
||||
}
|
||||
153
Tests/webGl/circle fractal/js/objects.js
Normal file
153
Tests/webGl/circle fractal/js/objects.js
Normal file
@@ -0,0 +1,153 @@
|
||||
class BaseShape {
|
||||
constructor() {
|
||||
this.controls = []; // Keep track of created elements and event listeners
|
||||
this.speedMultiplier = 100;
|
||||
}
|
||||
|
||||
initialise(config) {
|
||||
for (let item of config) {
|
||||
const { element, listener } = addControl(item, this);
|
||||
this.controls.push({ element, listener });
|
||||
}
|
||||
|
||||
const { element, listener } = addControl({ type: "range", min: 1, max: 500, defaultValue: 100, property: "speedMultiplier", }, this);
|
||||
this.controls.push({ element, listener });
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.controls.forEach(({ element, listener }) => {
|
||||
if (element && listener) {
|
||||
element.removeEventListener("input", listener);
|
||||
}
|
||||
if (element && element.parentElement) {
|
||||
element.parentElement.removeChild(element);
|
||||
const titleElement = document.getElementById("elText" + element.id.slice(2));
|
||||
titleElement.parentElement.removeChild(titleElement);
|
||||
}
|
||||
});
|
||||
this.controls = [];
|
||||
}
|
||||
|
||||
draw() {
|
||||
throw new Error("Draw function not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class EyePrototype extends BaseShape {
|
||||
constructor(Val1) {
|
||||
super();
|
||||
this.Val1 = Val1;
|
||||
this.iVal1Location = "";
|
||||
this.iResolutionLocation = ""//gl.getUniformLocation(this.program, 'iResolution');
|
||||
this.program = gl.createProgram();
|
||||
}
|
||||
initialise(config) {
|
||||
for (let item of config) {
|
||||
const { element, listener } = addControl(item, this);
|
||||
this.controls.push({ element, listener });
|
||||
}
|
||||
|
||||
const { element, listener } = addControl({ type: "range", min: 1, max: 500, defaultValue: 100, property: "speedMultiplier", }, this);
|
||||
this.controls.push({ element, listener });
|
||||
|
||||
const vertexShaderSource = `
|
||||
attribute vec2 position;
|
||||
void main() {
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// Create fragment shader using the code you wrote
|
||||
const fragmentShaderSource = `
|
||||
precision mediump float;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
uniform float iVal1;
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
||||
vec2 uv = (fragCoord * 2.0 - iResolution.xy) / iResolution.y;
|
||||
vec2 uv0 = uv;
|
||||
vec3 colour = vec3(1.0, 1.5, 2.0);
|
||||
vec3 colour2 = vec3(0.2, 0.2, 1.0);
|
||||
float pi = 3.1415926;
|
||||
float val1Div = iVal1/10.0;
|
||||
|
||||
|
||||
for (float i = 0.0; i < 2.0; i++) {
|
||||
uv = fract(uv * val1Div) - 0.5;
|
||||
float distance = length(uv);
|
||||
|
||||
distance = sin(distance * pi * 5.0 - iTime) / (pi * 5.0);
|
||||
distance = abs(distance);
|
||||
distance = 0.02 / distance;
|
||||
|
||||
colour = mix(colour, colour2, length(uv));
|
||||
|
||||
colour *= distance;
|
||||
}
|
||||
|
||||
float distance = length(uv0);
|
||||
distance = sin(distance * pi * 5.0 - iTime) / (pi * 5.0);
|
||||
distance = abs(distance);
|
||||
distance = 0.02 / distance;
|
||||
|
||||
colour = mix(colour, colour2, length(uv0));
|
||||
colour *= distance;
|
||||
|
||||
fragColor = vec4(colour, 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoord = gl_FragCoord.xy;
|
||||
vec2 iResolution = vec2(${canvas.width.toFixed(1)}, ${canvas.height.toFixed(1)});
|
||||
|
||||
float iTime = ${performance.now().toFixed(3)} / 1000.0;
|
||||
vec4 fragColor;
|
||||
mainImage(fragColor, fragCoord);
|
||||
gl_FragColor = fragColor;
|
||||
}
|
||||
`;
|
||||
|
||||
// Compile and link shaders
|
||||
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
|
||||
gl.shaderSource(vertexShader, vertexShaderSource);
|
||||
gl.compileShader(vertexShader);
|
||||
|
||||
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
gl.shaderSource(fragmentShader, fragmentShaderSource);
|
||||
gl.compileShader(fragmentShader);
|
||||
|
||||
// const this.program = gl.createProgram();
|
||||
gl.attachShader(this.program, vertexShader);
|
||||
gl.attachShader(this.program, fragmentShader);
|
||||
gl.linkProgram(this.program);
|
||||
gl.useProgram(this.program);
|
||||
|
||||
// Create vertex buffer
|
||||
const positionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);
|
||||
const positionAttributeLocation = gl.getAttribLocation(this.program, 'position');
|
||||
gl.enableVertexAttribArray(positionAttributeLocation);
|
||||
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
// Set uniforms
|
||||
this.iResolutionLocation = gl.getUniformLocation(this.program, 'iResolution');
|
||||
this.iVal1Location = gl.getUniformLocation(this.program, 'iVal1');
|
||||
|
||||
}
|
||||
|
||||
draw(rotation) {
|
||||
|
||||
gl.uniform2f(this.iResolutionLocation, canvas.width, canvas.height);
|
||||
gl.uniform1f(gl.getUniformLocation(this.program, 'iTime'), performance.now() / 1000.0);
|
||||
gl.uniform1f(this.iVal1Location, this.Val1);
|
||||
|
||||
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
// requestAnimationFrame(render);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
21
Tests/webGl/first test/index.html
Normal file
21
Tests/webGl/first test/index.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebGL Centered Square</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
98
Tests/webGl/first test/main.js
Normal file
98
Tests/webGl/first test/main.js
Normal file
@@ -0,0 +1,98 @@
|
||||
const canvas = document.getElementById('canvas');
|
||||
const gl = canvas.getContext('webgl');
|
||||
|
||||
if (!gl) {
|
||||
alert('WebGL is not supported in your browser.');
|
||||
}
|
||||
|
||||
function resizeCanvas() {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
resizeCanvas();
|
||||
|
||||
const vertices = [
|
||||
-0.5, -0.5,
|
||||
0.5, -0.5,
|
||||
0.5, 0.5,
|
||||
-0.5, 0.5,
|
||||
];
|
||||
|
||||
const vertexBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
|
||||
|
||||
const vertexShaderSource = `
|
||||
attribute vec2 a_position;
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform vec2 u_translation;
|
||||
uniform vec2 u_scale;
|
||||
|
||||
void main() {
|
||||
vec2 scaledPosition = a_position * u_scale;
|
||||
vec2 position = scaledPosition + u_translation;
|
||||
|
||||
vec2 zeroToOne = position / u_resolution;
|
||||
vec2 zeroToTwo = zeroToOne * 2.0;
|
||||
vec2 clipSpace = zeroToTwo - 1.0;
|
||||
|
||||
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
|
||||
}
|
||||
`;
|
||||
|
||||
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
|
||||
gl.shaderSource(vertexShader, vertexShaderSource);
|
||||
gl.compileShader(vertexShader);
|
||||
|
||||
const fragmentShaderSource = `
|
||||
precision mediump float;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(1, 0, 0, 1);
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
gl.shaderSource(fragmentShader, fragmentShaderSource);
|
||||
gl.compileShader(fragmentShader);
|
||||
|
||||
const program = gl.createProgram();
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
gl.useProgram(program);
|
||||
|
||||
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
|
||||
gl.enableVertexAttribArray(positionAttributeLocation);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
||||
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
const resolutionUniformLocation = gl.getUniformLocation(program, 'u_resolution');
|
||||
const translationUniformLocation = gl.getUniformLocation(program, 'u_translation');
|
||||
const scaleUniformLocation = gl.getUniformLocation(program, 'u_scale');
|
||||
|
||||
function drawScene() {
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.uniform2f(resolutionUniformLocation, canvas.width, canvas.height);
|
||||
|
||||
const squareWidth = 50;
|
||||
const squareHeight = 50;
|
||||
const xCenter = (canvas.width - squareWidth) / 2;
|
||||
const yCenter = (canvas.height - squareHeight) / 2;
|
||||
gl.uniform2f(translationUniformLocation, xCenter, yCenter);
|
||||
|
||||
gl.uniform2f(scaleUniformLocation, squareWidth, squareHeight);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
|
||||
|
||||
requestAnimationFrame(drawScene);
|
||||
}
|
||||
|
||||
drawScene();
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Tests/webGl/my-threejs-test/.parcel-cache/data.mdb
Normal file
BIN
Tests/webGl/my-threejs-test/.parcel-cache/data.mdb
Normal file
Binary file not shown.
BIN
Tests/webGl/my-threejs-test/.parcel-cache/lock.mdb
Normal file
BIN
Tests/webGl/my-threejs-test/.parcel-cache/lock.mdb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
32322
Tests/webGl/my-threejs-test/dist/index.975ef6c8.js
vendored
Normal file
32322
Tests/webGl/my-threejs-test/dist/index.975ef6c8.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
Tests/webGl/my-threejs-test/dist/index.975ef6c8.js.map
vendored
Normal file
1
Tests/webGl/my-threejs-test/dist/index.975ef6c8.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
22
Tests/webGl/my-threejs-test/dist/index.html
vendored
Normal file
22
Tests/webGl/my-threejs-test/dist/index.html
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Phyllotaxis Animation with Three.js and ShaderMaterial</title>
|
||||
<style>body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> -->
|
||||
<script src="/index.975ef6c8.js" defer=""></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
23
Tests/webGl/my-threejs-test/index.html
Normal file
23
Tests/webGl/my-threejs-test/index.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Phyllotaxis Animation with Three.js and ShaderMaterial</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> -->
|
||||
<script src="./src/index.js" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
3714
Tests/webGl/my-threejs-test/package-lock.json
generated
Normal file
3714
Tests/webGl/my-threejs-test/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
Tests/webGl/my-threejs-test/package.json
Normal file
21
Tests/webGl/my-threejs-test/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "my-threejs-test",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "parcel index.html",
|
||||
"build": "parcel build index.html"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"parcel": "^2.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@parcel/transformer-glsl": "^2.12.0",
|
||||
"postprocessing": "^6.35.3",
|
||||
"three": "^0.163.0"
|
||||
}
|
||||
}
|
||||
19
Tests/webGl/my-threejs-test/shaders/fragment.glsl
Normal file
19
Tests/webGl/my-threejs-test/shaders/fragment.glsl
Normal file
@@ -0,0 +1,19 @@
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
float distance = length(gl_PointCoord - vec2(0.5, 0.5));
|
||||
float radius = 0.5; // The radius of the circle, where 0.5 is the edge of the point sprite
|
||||
float intensity = 0.5; // Gaussian for smooth intensity increase
|
||||
// float intensity = exp(-pow(distance * 3.0, 2.0)); // Gaussian for smooth intensity increase
|
||||
|
||||
// If the distance is greater than the radius, discard the fragment (make it transparent)
|
||||
if (distance > radius) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// Use the intensity to modulate the color within the circle
|
||||
vec3 brightColor = vColor * intensity;
|
||||
brightColor = clamp(brightColor, 0.0, 2.5);
|
||||
|
||||
gl_FragColor = vec4(brightColor, 1.0); // Full alpha within the circle
|
||||
}
|
||||
33
Tests/webGl/my-threejs-test/shaders/vertex.glsl
Normal file
33
Tests/webGl/my-threejs-test/shaders/vertex.glsl
Normal file
@@ -0,0 +1,33 @@
|
||||
uniform float time;
|
||||
uniform vec3 color1;
|
||||
uniform vec3 color2;
|
||||
varying vec3 vColor;
|
||||
|
||||
const float PI = 3.1415926535897932384626433832795;
|
||||
const float goldenAngle = PI * (3.0 - sqrt(5.0));
|
||||
|
||||
void main() {
|
||||
// Calculate the normalized distance from center
|
||||
float index = float(gl_VertexID);
|
||||
float radiuss = sqrt(index) * 5.0;
|
||||
float normalizedRadius = radiuss / 200.0; // Adjust this divisor based on your pattern's size
|
||||
|
||||
// Calculate the angle for each point based on its index
|
||||
float angle = index * (goldenAngle + time * 0.001);
|
||||
if (mod(index,2.0) != 0.0){
|
||||
angle *=-1.0;
|
||||
}
|
||||
|
||||
// Color changes over time - Adding a slow wave effect based on the distance from center
|
||||
float colorTime = time * -2.1; // Slow down the color change over time
|
||||
float colorWave = sin(normalizedRadius * PI * 2.0 + colorTime); // Wave based on distance and time
|
||||
float colorMixFactor = colorWave * 0.5 + 0.5; // Normalize the wave between 0 and 1
|
||||
|
||||
vColor = mix(color1, color2, colorMixFactor); // Mix the colors based on the computed factor
|
||||
|
||||
// Calculate the animated position
|
||||
vec3 animatedPosition = vec3(radiuss * cos(angle), radiuss * sin(angle), 0.0);
|
||||
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(animatedPosition, 1.0);
|
||||
gl_PointSize = 10.0;
|
||||
}
|
||||
110
Tests/webGl/my-threejs-test/src/index.js
Normal file
110
Tests/webGl/my-threejs-test/src/index.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import * as THREE from 'three';
|
||||
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
|
||||
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
|
||||
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
|
||||
|
||||
import vertexShader from '../shaders/vertex.glsl';
|
||||
import fragmentShader from '../shaders/fragment.glsl';
|
||||
|
||||
// Now you can use `vertexShader` and `fragmentShader` as strings in your Three.js material
|
||||
// Basic setup
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
renderer = new THREE.WebGLRenderer({ alpha: true });
|
||||
renderer.setClearColor(0x000000, 0); // Transparent background color
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
// scene.add(renderer.domElement);
|
||||
|
||||
|
||||
// Phyllotaxis setup
|
||||
const pointsGeometry = new THREE.BufferGeometry();
|
||||
const n_points = 5000;
|
||||
const positions = new Float32Array(n_points * 3);
|
||||
const goldenAngle = Math.PI * (3 - Math.sqrt(5));
|
||||
// const goldenAngle = 0;
|
||||
|
||||
for (let i = 0; i < n_points; i++) {
|
||||
const angle = i * goldenAngle;
|
||||
const r = Math.sqrt(i) * 5;
|
||||
positions[i * 3] = r * Math.cos(angle); // x
|
||||
positions[i * 3 + 1] = r * Math.sin(angle); // y
|
||||
positions[i * 3 + 2] = 0; // z
|
||||
}
|
||||
|
||||
pointsGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||
|
||||
// ShaderMaterial
|
||||
const shaderMaterial = new THREE.ShaderMaterial({
|
||||
uniforms: {
|
||||
time: { value: 0.0 },
|
||||
color1: { value: new THREE.Color(0x2D81FC) }, // Blue
|
||||
color2: { value: new THREE.Color(0xFC0362) }, // Pink
|
||||
},
|
||||
vertexShader: vertexShader,
|
||||
fragmentShader: fragmentShader,
|
||||
transparent: true,
|
||||
blending: THREE.AdditiveBlending,
|
||||
});
|
||||
|
||||
const points = new THREE.Points(pointsGeometry, shaderMaterial);
|
||||
// Assign the points mesh to the bloom layer
|
||||
scene.add(points);
|
||||
points.layers.enable(1);
|
||||
|
||||
camera.position.z = 200;
|
||||
|
||||
// Compile the shader and catch any errors
|
||||
renderer.compile(scene, camera);
|
||||
|
||||
// Create layers
|
||||
const bloomLayer = new THREE.Layers();
|
||||
bloomLayer.set(1); // Setting the bloom layer to layer 1
|
||||
|
||||
// Set up the bloom pass
|
||||
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.0, 0.4, 0.85);
|
||||
bloomPass.threshold = 0.1
|
||||
bloomPass.strength = 2.5
|
||||
bloomPass.radius = 0.55
|
||||
bloomPass.renderToScreen = true
|
||||
// Create a render scene pass
|
||||
const renderScene = new RenderPass(scene, camera);
|
||||
|
||||
// Set up the effect composer
|
||||
const composer = new EffectComposer(renderer);
|
||||
composer.setSize(window.innerWidth, window.innerHeight);
|
||||
composer.addPass(renderScene);
|
||||
composer.addPass(bloomPass);
|
||||
|
||||
renderer.outputEncoding = THREE.sRGBEncoding;
|
||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||
// renderer.toneMappingExposure = Math.pow( 0.9, 4.0 )
|
||||
renderer.toneMappingExposure = 1.0
|
||||
|
||||
|
||||
renderer.autoClear = false;
|
||||
points.layers.set(1);
|
||||
// Animation loop
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
shaderMaterial.uniforms.time.value = performance.now() / 5000;
|
||||
|
||||
renderer.clear();
|
||||
|
||||
camera.layers.set(1);
|
||||
composer.render();
|
||||
|
||||
renderer.clearDepth();
|
||||
camera.layers.set(0);
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
animate();
|
||||
|
||||
// Handle window resize
|
||||
window.addEventListener('resize', onWindowResize, false);
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
37
Tests/webGl/sams_webgl/index.html
Normal file
37
Tests/webGl/sams_webgl/index.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>WebGL Template</title>
|
||||
<style>
|
||||
/* Set canvas to occupy the full width and height of the window */
|
||||
canvas {
|
||||
display: block;
|
||||
width: 800px;
|
||||
height: 800px;
|
||||
margin: 0 560px;
|
||||
padding: 0;
|
||||
|
||||
}
|
||||
|
||||
.canvas2d {
|
||||
position: absolute !important;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
body{
|
||||
margin:0
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
<canvas id="canvas2d" class="canvas2d"></canvas>
|
||||
<script src="index.js">
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
204
Tests/webGl/sams_webgl/index.js
Normal file
204
Tests/webGl/sams_webgl/index.js
Normal file
@@ -0,0 +1,204 @@
|
||||
// Get the canvas element and create a WebGL context
|
||||
const canvas = document.getElementById('canvas');
|
||||
const canvas2d = document.getElementById('canvas2d');
|
||||
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
||||
let ctx = canvas2d.getContext("2d");
|
||||
ctx.canvas.width = 800;
|
||||
ctx.canvas.height = 800;
|
||||
|
||||
// Check if WebGL is available
|
||||
if (!gl) {
|
||||
alert('WebGL not supported in your browser.');
|
||||
} else {
|
||||
// Set the canvas size and WebGL viewport
|
||||
canvas.width = 800;
|
||||
canvas.height = 800;
|
||||
let centerX = ctx.canvas.width / 2;
|
||||
let centerY = ctx.canvas.height / 2;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Define the vertex shader source code
|
||||
const vertexShaderSource = `
|
||||
vec3 hsv2rgb(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
attribute vec2 position;
|
||||
uniform float u_rotation;
|
||||
uniform vec2 u_pos;
|
||||
|
||||
varying vec4 vColor;
|
||||
|
||||
void main() {
|
||||
vColor = vec4(hsv2rgb(vec3(u_rotation, 0.5+pow(position.y+0.4, 10.0), 1.0)), 1.0);
|
||||
|
||||
float centerX = 0.0;
|
||||
float centerY = 0.0;
|
||||
float xPos = position.x;
|
||||
float yPos = position.y;
|
||||
|
||||
float rot_cos = cos(u_rotation);
|
||||
float rot_sin = sin(u_rotation);
|
||||
|
||||
float x = xPos * rot_cos - yPos * rot_sin;
|
||||
float y = yPos * rot_cos + xPos * rot_sin;
|
||||
gl_Position = vec4(x+u_pos.x , y+u_pos.y , 0, 1);
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
// Define the fragment shader source code
|
||||
const fragmentShaderSource = `
|
||||
precision mediump float;
|
||||
varying vec4 vColor;
|
||||
uniform vec4 fColor;
|
||||
void main() {
|
||||
gl_FragColor = vColor;
|
||||
}
|
||||
`;
|
||||
|
||||
// Create and compile the vertex and fragment shaders
|
||||
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
|
||||
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
|
||||
|
||||
// Create a program, attach the shaders, and link the program
|
||||
const program = createProgram(gl, vertexShader, fragmentShader);
|
||||
|
||||
// Get the attribute and uniform locations
|
||||
const positionAttributeLocation = gl.getAttribLocation(program, 'position');
|
||||
// const positionAttributeLocation1 = gl.getAttribLocation(program, 'u_pos');
|
||||
const rotationUniformLocation = gl.getUniformLocation(program, 'u_rotation');
|
||||
const fColorLocation = gl.getUniformLocation(program, "fColor");
|
||||
|
||||
// Create a buffer to store vertex positions
|
||||
const positionBuffer = gl.createBuffer();
|
||||
|
||||
// Bind the buffer and send the vertex positions to the GPU
|
||||
|
||||
|
||||
function polyPoints(sides) {
|
||||
let pointsArr = [];
|
||||
let width = 1;
|
||||
let rotation = 0
|
||||
pointsArr.push(width * Math.cos((rotation * Math.PI) / 180));
|
||||
pointsArr.push(width * Math.sin((rotation * Math.PI) / 180))
|
||||
for (let i = 0; i < sides; i++) {
|
||||
pointsArr.push(width * Math.cos((i * 2 * Math.PI) / sides + (rotation * Math.PI) / 180))
|
||||
pointsArr.push(width * Math.sin((i * 2 * Math.PI) / sides + (rotation * Math.PI) / 180))
|
||||
}
|
||||
return pointsArr
|
||||
}
|
||||
|
||||
|
||||
function rad(degrees) {
|
||||
var pi = Math.PI;
|
||||
return degrees * (pi / 180);
|
||||
}
|
||||
|
||||
// Function to create and compile a shader
|
||||
function createShader(gl, type, source) {
|
||||
const shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
return shader;
|
||||
}
|
||||
console.error(gl.getShaderInfoLog(shader));
|
||||
gl.deleteShader(shader);
|
||||
}
|
||||
|
||||
// Function to create a program and link shaders
|
||||
function createProgram(gl, vertexShader, fragmentShader) {
|
||||
const program = gl.createProgram();
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
||||
return program;
|
||||
}
|
||||
console.error(gl.getProgramInfoLog(program));
|
||||
gl.deleteProgram(program);
|
||||
}
|
||||
// Function to draw the scene
|
||||
let prevTime = 0
|
||||
function drawScene(time) {
|
||||
// Convert time to seconds
|
||||
// console.log(time - prevTime)
|
||||
prevTime = time
|
||||
time *= 0.001;
|
||||
|
||||
// Clear the canvas with a black background
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
// Use the shader program
|
||||
gl.useProgram(program);
|
||||
|
||||
// Enable the position attribute
|
||||
|
||||
// Create a buffer to store vertex positions
|
||||
let blue = [0.17, 0.49, 0.85] //blue
|
||||
let green = [0.17, 0.85, 0.46] //green
|
||||
let pink = [0.96, 0.24, 0.86] //green
|
||||
|
||||
// drawPoly(3, time * 1, blue, 0, 0)
|
||||
// drawPoly(3, time * 0.8, pink, 0.35, 0.35)
|
||||
// drawPoly(5,time*0.6)
|
||||
// drawPoly(6,time*0.4)
|
||||
// drawPoly(7,time*0.2)
|
||||
//
|
||||
|
||||
// prepare the gl state for the draw (can be here as drawPoly gets called multiple times with the same state)
|
||||
// This could be improved by using a VAO to get the gpu to run over the for loop for you
|
||||
gl.enableVertexAttribArray(positionAttributeLocation);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
|
||||
drawPoly(4, time * i * 0.001, blue, 0, 0)
|
||||
}
|
||||
|
||||
|
||||
Draw_center()
|
||||
drawCircle()
|
||||
// console.log(time)
|
||||
// Request the next frame to be drawn
|
||||
requestAnimationFrame(drawScene);
|
||||
}
|
||||
|
||||
function drawPoly(sides, speed, colour, x, y) {
|
||||
gl.uniform1f(rotationUniformLocation, speed);
|
||||
gl.uniform4f(fColorLocation, colour[0], colour[1], colour[2], 1);
|
||||
gl.uniform2f(gl.getUniformLocation(program, 'u_pos'), x, y);
|
||||
const positions = polyPoints(sides)
|
||||
// const positionBuffer1 = gl.createBuffer();
|
||||
|
||||
// Assume the buffers are already bound
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.DYNAMIC_DRAW);
|
||||
gl.drawArrays(gl.LINE_LOOP, 0, positions.length / 2);
|
||||
}
|
||||
|
||||
// Start rendering the scene
|
||||
requestAnimationFrame(drawScene);
|
||||
function Draw_center() {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX - 400, centerY);
|
||||
ctx.lineTo(centerX + 400, centerY);
|
||||
ctx.moveTo(centerX, centerY - 400);
|
||||
ctx.lineTo(centerX, centerY + 400);
|
||||
ctx.strokeStyle = "green";
|
||||
ctx.stroke();
|
||||
// console.log("drawn center")
|
||||
}
|
||||
function drawCircle() {
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, 400, 0, 2 * Math.PI, false);
|
||||
ctx.strokeStyle = "green";
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user