Compare commits

...

55 Commits

Author SHA1 Message Date
Sheldan
ef162a7dc2 survivors: adding enemy health scaling
adding dmg bonus items and dmg scaling stat for player
2025-09-15 01:14:43 +02:00
Sheldan
26ad150b59 survivors: also showing taken damage 2025-09-14 18:51:04 +02:00
Sheldan
9bb7ec99c0 survivors: adding health regen and simple particle system 2025-09-14 18:09:12 +02:00
Sheldan
db2110c921 survivors: healing 1 health if picking up health
fixing pull range up only increasing absolute
2025-09-14 15:00:18 +02:00
Sheldan
b3dbc9cc80 survivors: fixing build 2025-09-10 00:32:23 +02:00
Sheldan
ef2ea386c5 survivors: split between base stats, temp stats and effective stats for players
changed the way how stats are increased/decreased
moving stats/status into separate files
2025-09-09 22:43:06 +02:00
Sheldan
fa477afb9a survivors: split between base stats, temp stats and effective stats for players
changed the way how stats are increased/decreased
2025-09-09 22:39:14 +02:00
Sheldan
28ef7b9c6f survivors: fixing code for mose move 2025-09-08 23:02:35 +02:00
Sheldan
a8be84ebd3 survivors: dont limit the movement area of the control to make it more useful 2025-09-08 22:58:18 +02:00
Sheldan
27f3d2e916 survivors: making controls slightly larger 2025-09-08 22:54:38 +02:00
Sheldan
9fea67dbcc survivors: changing mobile detection 2025-09-08 22:54:07 +02:00
Sheldan
add005d963 survivors: only displaying the controls if we are on mobile
adding method to detect if mobile
2025-09-08 22:52:10 +02:00
Sheldan
681ba1b632 survivors: renaming drawDot to fillDot
adding area for touch movement instead of buttons simulating the keyboard
2025-09-08 22:42:47 +02:00
Sheldan
272a86d7fc survivors: fixing ui for touch events 2025-09-07 20:11:34 +02:00
Sheldan
70c19f2851 survivors: adding ui elements to use instead of keyboard 2025-09-07 20:01:20 +02:00
Sheldan
c9c063b477 survivors: adding spear (kinda)
changed range calculation for melee weapons
2025-09-07 19:12:54 +02:00
Sheldan
27862e19df survivors: chainBall now attacks the furthest away enemy within range
refactoring player stats a bit
changing schedule the enemies are spawned
2025-09-07 15:25:50 +02:00
Sheldan
70130f47a4 survivors: adding pull range upgrade
renaming dagger to chain ball
increasing returning speed for chain ball
2025-09-07 15:06:40 +02:00
Sheldan
33310100f7 survivors: adding new "melee" weapon 2025-09-06 00:50:45 +02:00
Sheldan
71b2afacc4 survivors: speeding up player projectiles 2025-09-05 23:29:00 +02:00
Sheldan
007d2568b3 survivors: spawning player in the middle of the window 2025-09-05 22:34:06 +02:00
Sheldan
7214a64b77 survivors: adding items and item drops
adding rarities to items
adding drops to enemies
2025-09-05 22:33:09 +02:00
Sheldan
f2e62a7e74 survivors: adding spread weapon 2025-09-04 20:56:36 +02:00
Sheldan
c01ac53312 survivors: fixing piercings of projectiles
trying to work against too fast projectiles by using a different direction to base the angle from (they quickly changed direction, I guess because they overshot the target)
2025-08-30 23:51:48 +02:00
Sheldan
1124e62bb7 survivors: homing projectiles now continuously try to find a new target after their initial has died 2025-08-30 20:31:22 +02:00
Sheldan
b591fc2dee survivors: fixing drops being duplicated (sometimes)
adding item container system to not edit the list while iterating over
2025-08-30 20:24:35 +02:00
Sheldan
e44355bf21 survivors: fixing homing projectiles behaving correctly after losing the target 2025-08-30 19:30:49 +02:00
Sheldan
39da3d8abd survivors: also spawning basic enemy continuously and randomly
better dynamic placement of stat labels in the player info
2025-08-30 11:01:05 +02:00
Sheldan
a52754ce0d survivors: adding functional container enemy with random chances on death
split between create and spawn methods
adding level drop
adding ability to player to level
fixing hud not showing the label value
adding current level display to hud
adding ability to weapons to level
2025-08-29 22:58:44 +02:00
Sheldan
59f1a4b164 survivors: moving shoot interval to weapon stats
changing weapon stats
2025-08-22 15:52:36 +02:00
Sheldan
e714fc35f6 survivors: madding death split mechanic to projectiles
adding generic method for coordinate splitting around points
refactored projectile stats and weapon stats
2025-08-22 15:29:23 +02:00
Sheldan
8ecfbf499f survivors: moving some stats of projectiles to weapons as they can originate from there 2025-08-22 14:39:42 +02:00
Sheldan
85c83a8827 survivors: moving more abilities to RangeWeapon 2025-08-22 14:36:04 +02:00
Sheldan
dff1a6a760 survivors: giving different weapons different projectile speed 2025-08-22 13:09:33 +02:00
Sheldan
e99b8b6bf8 survivors: adding weapon stats and range weapon stat 2025-08-22 13:00:24 +02:00
Sheldan
e91368d380 survivors: maybe made the behaviour better when projectiles flew through the target and then had to continue moving 2025-08-22 11:53:58 +02:00
Sheldan
dbf34061f0 survivors: fixing enemies not being able to spawn projectiles due to missing speed 2025-08-22 00:29:47 +02:00
Sheldan
7b8745d7d2 survivors: homing behaving a bit better with this, but not great 2025-08-22 00:28:02 +02:00
Sheldan
66ef2eaa31 survivors: adding collision for faster projectiles to also consider collisions between ticks
differentiating between pistol and homing pistol
using projectile speed for straight projectile
2025-08-22 00:23:22 +02:00
Sheldan
07b64154e1 survivors: add ability to dynamically position weapons
change size of weapon to be used for offset only, draw size is different
increasing speed of gun projectile
2025-08-21 21:53:59 +02:00
Sheldan
603bf3addc survivors: restricting player to world bounds
reworking how repeated collisions are handled for projectiles for the purpose of piercing projectiles
2025-08-21 20:54:56 +02:00
Sheldan
8ca64a19b7 survivors: adding health enemies dropping health packs 2025-08-21 18:36:31 +02:00
Sheldan
71f48404c9 survivors: reducing jumpiness of homing projectiles re-directing by limiting it to a certain angle
adding rad degree conversion utils checking world border for projectiles
2025-08-21 17:48:16 +02:00
Sheldan
c8767f1119 survivors: adding simple drops
moving size to world object
adding dying for enemies and projectiles and theoretically for players
adding pull range for player
adding money stat display
made pistol homing
2025-08-21 16:44:20 +02:00
Sheldan
18c323430c survivors: fixing config 2025-08-21 13:37:55 +02:00
Sheldan
e75946d749 survivors: changing vite config for hosting 2025-08-21 13:26:30 +02:00
Sheldan
cffb10aed6 survivors: changing output dir 2025-08-21 13:19:51 +02:00
Sheldan
9b5ab25c4d survivors: adding first version 2025-08-21 13:16:29 +02:00
Sheldan
8a6e3b86df collatzConjecture: fixing download button
dotLines: fixing controls
2025-08-09 00:00:43 +02:00
Sheldan
8c86e89470 dotLines: initial adding 2025-08-08 23:50:33 +02:00
Sheldan
7337a4c917 collatzConjecture: initial adding
clusterFilter: fixing package.json
2025-08-08 23:45:28 +02:00
Sheldan
8ef5e61588 circleBs: fixing buttons
clusterFilter: initial version
2025-08-08 23:28:12 +02:00
Sheldan
c8cd522c94 circleBs: adding circleBs 2025-08-08 22:58:43 +02:00
Sheldan
fd799ea724 balls: increase fps to 60 2025-08-08 22:45:05 +02:00
Sheldan
0de74e11a3 bubbles: added 2025-08-08 22:01:36 +02:00
65 changed files with 9523 additions and 5 deletions

View File

@@ -29,29 +29,65 @@ jobs:
with:
node-version: 21
- name: Orbits Install dependencies
run: npm install
run: npm ci
working-directory: orbits
- name: Orbits Build
run: npx vite build
working-directory: orbits
- name: recBubbles Install dependencies
run: npm install
run: npm ci
working-directory: recBubbles
- name: recBubbles Build
run: npx vite build
working-directory: recBubbles
- name: balls Install dependencies
run: npm install
run: npm ci
working-directory: balls
- name: balls Build
run: npx vite build
working-directory: balls
- name: fireWorks Install dependencies
run: npm install
run: npm ci
working-directory: fireWorks
- name: fireWorks Build
run: npx vite build
working-directory: fireWorks
- name: bubbles Install dependencies
run: npm ci
working-directory: bubbles
- name: bubbles Build
run: npx vite build
working-directory: bubbles
- name: circleBs Install dependencies
run: npm ci
working-directory: circleBs
- name: circleBs Build
run: npx vite build
working-directory: circleBs
- name: clusterFilter Install dependencies
run: npm ci
working-directory: clusterFilter
- name: clusterFilter Build
run: npx vite build
working-directory: clusterFilter
- name: collatzConjecture Install dependencies
run: npm ci
working-directory: collatzConjecture
- name: collatzConjecture Build
run: npx vite build
working-directory: collatzConjecture
- name: dotLines Install dependencies
run: npm ci
working-directory: dotLines
- name: dotLines Build
run: npx vite build
working-directory: dotLines
- name: survivors install dependencies
run: npm ci
working-directory: absurd-survivors
- name: survivors build
run: npx vite build
working-directory: absurd-survivors
- name: Move index
run: cp index.html dist/
- name: Move overview images

24
absurd-survivors/.gitignore vendored Normal file
View 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?

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>overtuned survivors</title>
<meta name="description" content="">
</head>
<body>
<canvas id="canvas" style="display: block; background-color: black"></canvas>
<script type="module" src="src/main.ts"></script>
</body>
</html>

1088
absurd-survivors/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
{
"name": "overtuned-survivors",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"canvas-common": "file:../canvas-common",
"typescript": "~5.8.3",
"vite": "^7.1.2"
}
}

View File

@@ -0,0 +1,321 @@
import type {Acting, ChanceEntry, Drawable, Healthy, Placeable, Shooting} from "./interfaces.ts";
import {fillDot, moveInDirectionOf} from "./utils.ts";
import {Vector} from "./base.ts";
import {World} from "./World.ts";
import type {Projectile} from "./projectile.ts";
import {StraightProjectile} from "./projectile.ts";
import {HealthPack, ItemDrop, LevelDrop, MoneyDrop} from "./drop.ts";
import {ItemManagement} from "./items.ts";
import {EnemyStats, ProjectileStats} from "./stats.ts";
import {EnemyStatus} from "./status.ts";
import {NumberDisplayParticle} from "./particles.ts";
export abstract class Enemy implements Placeable, Drawable, Acting, Healthy {
protected _position: Vector;
protected speed: number;
protected world: World;
protected size: number
protected status: EnemyStatus;
protected drops: KillChanceTable;
constructor(position: Vector, enemyStats: EnemyStats) {
this.drops = new KillChanceTable();
this.drops.addDrop( {chance: 10, creationMethod: this.spawnMoney})
this._position = position.clone();
let health = enemyStats.getEffectiveHealth()
this.status = new EnemyStatus(health);
}
draw(ctx: CanvasRenderingContext2D) {
}
act() {
this.move()
}
move() {
}
getPosition(): Vector {
return this._position;
}
takeDamage(damage: number) {
this.status.health -= damage;
NumberDisplayParticle.spawnNumberParticle(this.world, damage, this._position, 'white')
if(this.status.dead) {
this.die()
this.world.removeEnemy(this)
}
}
die() {
let draw = this.drops.draw();
if(draw) {
draw.creationMethod(this)
}
}
spawnMoney(enemy: Enemy) {
MoneyDrop.spawnMoneyDrop(enemy.world, enemy._position);
}
getSize() {
return this.size;
}
dead() {
return this.status.dead
}
tick(seconds: number, tick: number) {
}
}
export class BasicEnemy extends Enemy {
constructor(position: Vector, enemyStats: EnemyStats) {
super(position, enemyStats);
}
protected color: string;
protected impactDamage: number;
protected impactCooldown: number = 0;
protected impactInterval: number = 60;
draw(ctx: CanvasRenderingContext2D) {
fillDot(this._position, this.getSize(), this.color, ctx)
}
move() {
this._position = moveInDirectionOf(this._position, this.world.player.position, this.speed)
}
act() {
super.act();
if(this._position.distanceTo(this.world.player.position) < this.getSize() && this.impactCooldown <= 0) {
this.world.player.takeDamage(this.impactDamage)
this.impactCooldown = this.impactInterval;
}
this.impactCooldown -= 1;
}
static spawnBasicEnemy(world: World, position?: Vector) {
world.addEnemy(this.generateBasicEnemy(world, position))
}
static generateBasicEnemy(world: World, position?: Vector): BasicEnemy {
if(position === undefined) {
position = world.randomPlace()
}
let enemyStats = new EnemyStats().withHealthFactor(world.getEnemyHealthFactor());
let basicEnemy = new BasicEnemy(position, enemyStats);
basicEnemy.size = 5;
basicEnemy.world = world;
basicEnemy.speed = 0.5;
basicEnemy.color = 'orange'
basicEnemy.impactDamage = 2;
return basicEnemy;
}
}
export class ShootingEnemy extends BasicEnemy implements Shooting {
private shootCooldown: number = 0;
private shootInterval: number;
private projectiles: Projectile[] = []
constructor(position: Vector, enemyStats: EnemyStats) {
super(position, enemyStats);
}
removeProjectile(projectile: Projectile) {
this.projectiles = this.projectiles.filter(item => item !== projectile)
}
act() {
super.act();
if(this.shootCooldown <= 0) {
this.createProjectile()
this.shootCooldown = this.shootInterval;
}
this.shootCooldown -= 1;
}
createProjectile() {
let stats = new ProjectileStats()
.withPiercings(0)
.withSize(1)
.withDamage(5)
.withSpeed(2)
let projectile = StraightProjectile.createStraightProjectile(this.world, this._position, this.world.player.position, this, stats)
this.projectiles.push(projectile)
return projectile
}
static spawnShootingEnemy(world: World, position?: Vector) {
world.addEnemy(this.generateShootingEnemy(world, position))
}
static generateShootingEnemy(world: World, position?: Vector) {
if(position === undefined) {
position = world.randomPlace()
}
let enemyStats = new EnemyStats().withHealthFactor(world.getEnemyHealthFactor());;
let shootingEnemy = new ShootingEnemy(position, enemyStats);
shootingEnemy.size = 5;
shootingEnemy.world = world;
shootingEnemy.speed = 0.5;
shootingEnemy.color = 'green'
shootingEnemy.impactDamage = 2;
shootingEnemy.shootInterval = 100
return shootingEnemy
}
}
export class HealthEnemy extends Enemy {
constructor(position: Vector, enemyStats: EnemyStats) {
super(position, enemyStats);
}
protected color: string;
draw(ctx: CanvasRenderingContext2D) {
fillDot(this._position, this.size, this.color, ctx)
}
move() {
}
act() {
super.act();
}
die() {
HealthPack.spawnHealthPack(this.world, this._position)
}
static spawnHealthEnemy(world: World, position?: Vector) {
world.addEnemy(this.createHealthEnemy(world, position))
}
static createHealthEnemy(world: World, position?: Vector) {
if(position === undefined) {
position = world.randomPlace()
}
let enemyStats = new EnemyStats().withHealthFactor(world.getEnemyHealthFactor());;
let basicEnemy = new HealthEnemy(position, enemyStats);
basicEnemy.size = 5;
basicEnemy.world = world;
basicEnemy.speed = 0;
basicEnemy.color = 'purple'
return basicEnemy;
}
getSize() {
return this.size
}
}
export class ContainerEnemy extends Enemy {
private drops: KillChanceTable;
constructor(position: Vector, enemyStats: EnemyStats) {
super(position, enemyStats);
this.status.health = 5;
this.drops = new KillChanceTable();
ItemManagement.getItemsWithRarityFactor().forEach(drop => {
this.drops.addDrop(drop)
})
this.drops.calculateProbs()
}
protected color: string;
draw(ctx: CanvasRenderingContext2D) {
fillDot(this._position, this.size, this.color, ctx)
}
move() {
}
act() {
super.act();
}
die() {
let draw = this.drops.draw();
if(draw) {
let item = draw.creationMethod(this);
ItemDrop.spawnItemDrop(this.world, item, this._position)
}
}
spawnHealthPack(enemy: ContainerEnemy) {
HealthPack.spawnHealthPack(enemy.world, enemy._position)
}
spawnLevelUp(enemy: ContainerEnemy) {
LevelDrop.spawnLevelDrop(enemy.world, enemy._position)
}
spawnEnemy(enemy: ContainerEnemy) {
ShootingEnemy.spawnShootingEnemy(enemy.world, enemy._position)
}
static spawnContainerEnemy(world: World, position?: Vector) {
world.addEnemy(this.createContainerEnemy(world, position))
}
static createContainerEnemy(world: World, position?: Vector) {
if(position === undefined) {
position = world.randomPlace()
}
let enemyStats = new EnemyStats().withHealthFactor(world.getEnemyHealthFactor());;
let basicEnemy = new ContainerEnemy(position, enemyStats);
basicEnemy.size = 5;
basicEnemy.world = world;
basicEnemy.speed = 0;
basicEnemy.color = 'brown'
return basicEnemy;
}
getSize() {
return this.size
}
}
export class KillChanceTable {
private chances: ChanceEntry[] = []
addDrop(entry: ChanceEntry) {
this.chances.push(entry)
}
calculateProbs() {
let sum = this.chances.reduce((sum, entry) => sum + entry.chance, 0)
this.chances.forEach(value => value.chance /= sum)
}
draw() {
if(this.chances.length === 0) {
return undefined;
}
let change = Math.random();
for (const value of this.chances) {
change -= value.chance;
if(change <= 0) {
return value;
}
}
return this.chances[this.chances.length - 1]
}
}

View File

@@ -0,0 +1,177 @@
import type {Acting, Drawable, Healthy, Item, Weapon} from "./interfaces.ts";
import {Vector} from "./base.ts";
import {fillDot, getCoordinatesSplit} from "./utils.ts";
import {PlayerStats} from "./stats.ts";
import {PlayerStatus} from "./status.ts";
import {World} from "./World.ts";
import { NumberDisplayParticle} from "./particles.ts";
export class Player implements Drawable, Acting, Healthy {
private _position: Vector;
private _baseStats: PlayerStats;
private _effectiveStats: PlayerStats;
private _tempStats: PlayerStats;
private _color: string;
private _status: PlayerStatus;
private _weapons: Weapon[] = []
private _items: Item[] = []
private _healTick: number = 0;
private static readonly HEAL_TICK_INTERVAL = 20;
private _world: World;
// temp
private _speed: Vector;
private _toHeal: number = 0;
constructor(position: Vector) {
this._position = position;
this._effectiveStats = new PlayerStats()
}
draw(ctx: CanvasRenderingContext2D) {
fillDot(this.position, this._effectiveStats.size, this._color, ctx)
this._weapons.forEach(weapon => weapon.draw(ctx))
}
public static generatePlayer(position?: Vector): Player {
if(position === undefined) {
position = new Vector(500, 500)
}
let player = new Player(position);
player._color = 'blue';
player._baseStats = PlayerStats.defaultPlayerStats();
let tempStats = new PlayerStats();
tempStats.resetToBasic()
player._tempStats = tempStats;
player.statsChanged()
player._speed = new Vector(0, 0)
player._status = new PlayerStatus(10, 0, 0);
return player;
}
addWeapon(weapon: Weapon) {
let weaponCount = this._weapons.length + 1;
let points = getCoordinatesSplit(weaponCount)
for (let i = 0; i < points.length - 1; i++){
const value = points[i];
let affectedWeapon = this._weapons[i];
affectedWeapon.setOffset(value.multiply(affectedWeapon.getSize()))
}
weapon.setOffset(points[points.length - 1].multiply(weapon.getSize()))
this._weapons.push(weapon)
}
addItem(item: Item) {
this._items.push(item)
}
set world(value: World) {
this._world = value;
}
move(direction: Vector) {
this._position = this.position.add(direction)
}
takeDamage(damage: number) {
NumberDisplayParticle.spawnNumberParticle(this._world, damage, this._position, 'red')
this._status.health -= damage;
}
statsChanged() {
this._effectiveStats.resetToBasic()
this._effectiveStats.mergeStats(this._baseStats)
this._effectiveStats.mergeStats(this._tempStats)
}
heal(amount: number) {
this._status.health += amount;
this._status.health = Math.min(this._status.health, this._effectiveStats.health)
}
get health(): number {
return this._status.health;
}
get position(): Vector {
return this._position;
}
get color(): string {
return this._color;
}
get effectiveStats(): PlayerStats {
return this._effectiveStats;
}
get status(): PlayerStatus {
return this._status;
}
get speed(): Vector {
return this._speed;
}
act() {
this._weapons.forEach(weapon => weapon.act())
}
die() {
}
getPosition(): Vector {
return this._position;
}
getSize() {
return this._effectiveStats.size
}
dead() {
return this.status.dead
}
changeBaseStat(value: number, statFun: (stats: PlayerStats, value: number) => void) {
this._baseStats.changeStat(value, statFun)
this.statsChanged()
}
changeTempStat(value: number, statFun: (stats: PlayerStats, value: number) => void) {
this._tempStats.changeStat(value, statFun)
this.statsChanged()
}
increaseLevel() {
this.status.increaseLevel()
this._baseStats.increaseLevel()
this._weapons.forEach(weapon => {
weapon.increaseLevel()
})
this.statsChanged()
}
level() {
return this.status.level
}
isHurt() {
return this.health < this._effectiveStats.health
}
tick(seconds: number, tick: number) {
this._healTick += 1;
if((this._healTick % Player.HEAL_TICK_INTERVAL) == 0 && this.isHurt()) {
let healed = this._effectiveStats.healthRegen / seconds
this._toHeal += healed;
if(this._toHeal >= 1) {
let toHealNow = this._toHeal - (this._toHeal % 1);
this._toHeal -= toHealNow;
this.heal(toHealNow);
NumberDisplayParticle.spawnNumberParticle(this._world, toHealNow, this.position, 'green')
}
}
}
}

View File

@@ -0,0 +1,221 @@
import {Enemy} from "./Enemies.ts";
import {Player} from "./Player.ts";
import {Projectile } from "./projectile.ts";
import {Vector} from "./base.ts";
import type {Drop, Particle, Placeable} from "./interfaces.ts";
export class World {
private _enemies: ObjectContainer<Enemy> = new ObjectContainer<Enemy>()
private _projectiles: ObjectContainer<Projectile> = new ObjectContainer<Projectile>();
private _drops: ObjectContainer<Drop> = new ObjectContainer<Drop>();
private _particles: ObjectContainer<Particle> = new ObjectContainer();
private _player: Player;
private readonly _ctx: CanvasRenderingContext2D;
private _size: Vector;
private _tick: number = 0;
private static readonly TICK_INTERVAL = 10;
private timeStamp: Date;
private startTimeStamp: Date;
constructor(player: Player, ctx: CanvasRenderingContext2D, size: Vector) {
this._player = player;
this._ctx = ctx;
this._size = size;
this.timeStamp = new Date();
this.startTimeStamp = new Date();
}
enemiesAct() {
this._enemies.items.forEach(enemy => enemy.act())
this._enemies.clean()
this._projectiles.items.forEach(projectile => projectile.act())
this._projectiles.clean()
this._drops.items.forEach(drop => drop.act())
this._drops.clean()
this._particles.items.forEach(particle => particle.act())
this._particles.clean()
}
draw() {
this._enemies.items.forEach(enemy => enemy.draw(this._ctx))
this._drops.items.forEach(drop => drop.draw(this._ctx))
this._projectiles.items.forEach(projectile => projectile.draw(this._ctx))
this._particles.items.forEach(particle => particle.draw(this._ctx))
this._player.draw(this._ctx);
}
addProjectile(projectile: Projectile) {
this._projectiles.add(projectile)
}
addParticle(particle: Particle) {
this._particles.add(particle)
}
addDrop(drop: Drop) {
this._drops.add(drop)
}
removeDrop(drop: Drop) {
this._drops.scheduleRemoval(drop)
}
removeParticle(particle: Particle) {
this._particles.scheduleRemoval(particle)
}
removeEnemy(enemy: Enemy) {
this._enemies.scheduleRemoval(enemy)
}
removeProjectile(projectile: Projectile) {
this._projectiles.scheduleRemoval(projectile)
}
movePlayer(vector: Vector) {
this._player.position.x += vector.x;
this._player.position.y += vector.y;
this._player.position.x = Math.min(this.size.x - this._player.getSize(), this._player.position.x)
this._player.position.x = Math.max(this._player.getSize(), this._player.position.x)
this._player.position.y = Math.min(this.size.y -this._player.getSize(), this._player.position.y)
this._player.position.y = Math.max(this._player.getSize(), this._player.position.y)
}
maxValue() {
return Math.max(this.size.x, this.size.y)
}
get size(): Vector {
return this._size;
}
get tick(): number {
return this._tick;
}
getSecondsPassed() {
return (this.timeStamp.getTime() - this.startTimeStamp.getTime()) / 1000
}
getEnemyHealthFactor() {
return this.getSecondsPassed() / 30;
}
triggerTick() {
this._tick += 1;
if((this._tick % World.TICK_INTERVAL) == 0) {
let currentTimeStamp = new Date();
let seconds = (currentTimeStamp.getTime() - this.timeStamp.getTime()) / 1000;
this._player.tick(seconds, this._tick);
this._particles.items.forEach(particle => particle.tick(seconds, this._tick))
this.timeStamp = currentTimeStamp;
}
}
outside(position: Vector): boolean {
return position.x > this.size.x || position.y > this.size.y || position.x < 0 || position.y < 0
}
addEnemy(enemy: Enemy) {
this._enemies.add(enemy)
}
randomPlace(): Vector {
return new Vector(this.size.x * Math.random(), this.size.y * Math.random())
}
getClosestTargetTo(point: Vector, range?: number): [number, Placeable | undefined] | undefined {
return this.getClosestTargetToButNot(point, undefined, range)
}
getFarthestTargetButWithin(point: Vector, range?: number): [number, Placeable | undefined] | undefined {
let currentTarget;
let currentDistance = Number.MAX_SAFE_INTEGER;
this._enemies.items.forEach(enemy => {
let distance = point.distanceTo(enemy.getPosition());
if(range && distance > range) {
return;
}
if((range - distance) < currentDistance) {
currentDistance = distance;
currentTarget = enemy
}
})
if(currentTarget) {
return [currentDistance, currentTarget];
}
}
getClosestTargetToButNot(point: Vector, placeable?: Placeable, range?: number): [number, Placeable | undefined] | undefined {
return this.getClosestTargetToButNotArray(point, [placeable], range)
}
getClosestTargetToButNotArray(point: Vector, placeAbles?: [Placeable | undefined], range?: number): [number, Placeable | undefined] | undefined {
let currentTarget;
let currentDistance = Number.MAX_SAFE_INTEGER;
this._enemies.items.forEach(enemy => {
if(placeAbles && placeAbles.indexOf(enemy) !== -1) {
return;
}
let distance = point.distanceTo(enemy.getPosition());
if(range && distance > range) {
return;
}
if(distance < currentDistance) {
currentDistance = distance;
currentTarget = enemy
}
})
if(currentTarget) {
return [currentDistance, currentTarget];
}
}
getAllInRange(point: Vector, range: number): Enemy[] {
let found = [];
this._enemies.items.forEach(enemy => {
let distance = point.distanceTo(enemy.getPosition());
if(range && distance < range) {
found.push(enemy)
}
});
return found;
}
get player(): Player {
return this._player;
}
}
class ObjectContainer<T> {
private _items: T[] = [];
private _itemsToRemove: T[] = [];
constructor() {
this._items = []
this._itemsToRemove = []
}
scheduleRemoval(item: T) {
this._itemsToRemove.push(item)
}
clean() {
this._itemsToRemove.forEach(value => this.remove(value))
this._itemsToRemove = []
}
private remove(itemToRemove: T) {
this._items = this._items.filter(item => item !== itemToRemove)
}
add(item: T) {
this._items.push(item)
}
get items(): T[] {
return this._items;
}
}

View File

@@ -0,0 +1,167 @@
import type {Healthy, Placeable} from "./interfaces.ts";
export class Vector {
constructor(private _x: number, private _y: number) {
}
static createVector(tip: Vector, shaft: Vector): Vector {
return new Vector(tip.x - shaft.x, tip.y - shaft.y);
}
static zero() {
return new Vector(0, 0)
}
normalize(): Vector {
let length = this.vecLength();
return new Vector(this.x / length, this.y / length)
}
vecLength(): number {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
clone() {
return new Vector(this.x, this.y)
}
distanceTo(point: Vector): number {
return Math.sqrt(Math.pow(this.x - point.x, 2) + Math.pow(this.y - point.y, 2));
}
add(vec: Vector): Vector {
return new Vector(this._x + vec._x, this._y + vec._y)
}
minus(vec: Vector): Vector {
return new Vector(this.x - vec._x, this.y - vec.y)
}
multiply(number: number): Vector {
return new Vector(this.x * number, this.y * number)
}
multiplyVec(vec: Vector): Vector {
return new Vector(this.x * vec._x, this.y * vec.y)
}
negate(): Vector {
return this.multiply(-1)
}
dotProduct(vector: Vector): number {
return (this._x * vector._x + this._y * vector._y) / Math.pow(vector.vecLength(), 2);
}
dotProduct2(vector: Vector): number {
return (this._x * vector._x + this._y * vector._y)
}
angleTo(vector: Vector): number {
return Math.acos(this.dotProduct2(vector) / (this.vecLength() * vector.vecLength()))
}
rotate(angle: number) {
let c = Math.cos(angle);
let s = Math.sin(angle)
let x = this._x * c - this._y * s
let y = this._x * s + this._y * c
return new Vector(x, y)
}
get x(): number {
return this._x;
}
set x(value: number) {
this._x = value;
}
get y(): number {
return this._y;
}
set y(value: number) {
this._y = value;
}
}
export class Cooldown {
private _currentValue;
private _totalValue;
constructor(totalValue) {
this._totalValue = totalValue;
this._currentValue = 0
}
cooledDown(): boolean {
return this.currentValue <= 0;
}
get currentValue(): number {
return this._currentValue;
}
decreaseCooldown() {
this._currentValue -= 1;
}
resetCooldown() {
this._currentValue = this._totalValue;
}
}
export class Point implements Placeable {
private position: Vector;
constructor(position: Vector) {
this.position = position;
}
getPosition(): Vector {
return this.position;
}
getSize() {
}
move(any?: any) {
}
}
export class DeadPoint implements Placeable, Healthy {
private position: Vector;
constructor(position: Vector) {
this.position = position;
}
getPosition(): Vector {
return this.position;
}
getSize() {
}
move(any?: any) {
}
dead() {
return true;
}
die() {
}
takeDamage(damage: number) {
}
}

View File

@@ -0,0 +1,160 @@
import type {Drop, Item} from "./interfaces.ts";
import {World} from "./World.ts";
import {fillDot, moveInDirectionOf} from "./utils.ts";
import {Vector} from "./base.ts";
import {rarityColor} from "./items.ts";
export abstract class BasicDrop implements Drop {
protected world: World;
protected _position: Vector;
protected _color: string;
protected size: number;
constructor(world: World, position: Vector) {
this.world = world;
this._position = position.clone();
}
getPosition(): Vector {
return this._position;
}
move() {
}
pickup() {
}
act() {
let distanceToPlayer = this._position.distanceTo(this.world.player.position);
if(distanceToPlayer < (this.world.player.effectiveStats.size + this.size)) {
this.pickup()
this.world.removeDrop(this)
} else if(distanceToPlayer < this.world.player.effectiveStats.pullRange) {
let speedFactor = 125 / distanceToPlayer;
this._position = moveInDirectionOf(this._position, this.world.player.position, speedFactor)
}
}
abstract draw(ctx: CanvasRenderingContext2D);
getSize() {
return this.size
}
tick(seconds: number, tick: number) {
}
}
export class MoneyDrop extends BasicDrop {
private worth: number;
draw(ctx: CanvasRenderingContext2D) {
fillDot(this._position, this.size, this._color, ctx)
}
pickup() {
this.world.player.status.wealth += this.worth
}
static spawnMoneyDrop(world: World, position?: Vector) {
world.addDrop(this.createMoneyDrop(world, position))
}
static createMoneyDrop(world: World, position?: Vector): MoneyDrop {
if(!position) {
position = world.randomPlace()
}
let drop = new MoneyDrop(world, position)
drop.worth = 1;
drop.size = 1;
drop._color = 'orange';
return drop;
}
}
export class HealthPack extends BasicDrop {
private healAmount: number;
draw(ctx: CanvasRenderingContext2D) {
fillDot(this._position, this.size, this._color, ctx)
}
pickup() {
this.world.player.heal(this.healAmount)
}
static spawnHealthPack(world: World, position?: Vector) {
world.addDrop(this.createHealthPack(world, position))
}
static createHealthPack(world: World, position?: Vector) {
if(!position) {
position = world.randomPlace()
}
let drop = new HealthPack(world, position)
drop.healAmount = 5;
drop.size = 2;
drop._color = 'green';
return drop;
}
}
export class LevelDrop extends BasicDrop {
draw(ctx: CanvasRenderingContext2D) {
fillDot(this._position, this.size, this._color, ctx)
}
pickup() {
this.world.player.increaseLevel()
}
static spawnLevelDrop(world: World, position?: Vector) {
world.addDrop(this.createLevelDrop(world, position))
}
static createLevelDrop(world: World, position?: Vector): LevelDrop {
if(!position) {
position = world.randomPlace()
}
let drop = new LevelDrop(world, position)
drop.size = 5;
drop._color = 'blue';
return drop;
}
}
export class ItemDrop extends BasicDrop {
private item: Item;
constructor(world: World, position: Vector, item: Item) {
super(world, position);
this.item = item;
}
pickup() {
this.item.pickup(this.world.player, this.world)
}
draw(ctx: CanvasRenderingContext2D) {
ctx.fillStyle = rarityColor(this.item.getRarity())
ctx.fillText(this.item.name() + '', this._position.x, this._position.y)
}
static spawnItemDrop(world: World, item: Item, position?: Vector) {
world.addDrop(this.createItemDrop(world, item, position))
}
static createItemDrop(world: World, item: Item, position?: Vector) {
if(!position) {
position = world.randomPlace()
}
let drop = new ItemDrop(world, position, item)
drop.size = 3
return drop
}
}

View File

@@ -0,0 +1,7 @@
import type {Healthy} from "./interfaces.ts";
export class InstanceOfUtils {
static instanceOfHealthy(object: any): object is Healthy {
return 'takeDamage' in object;
}
}

View File

@@ -0,0 +1,80 @@
import {Vector} from "./base.ts";
import type {Player} from "./Player.ts";
import type {Rarity} from "./items.ts";
import type {World} from "./World.ts";
export interface Acting {
act()
tick(seconds: number, tick: number)
}
export interface Healthy {
takeDamage(damage: number);
die();
dead();
}
export interface Leveling {
increaseLevel();
level()
}
export interface Item {
pickup(player: Player, world: World);
name(): string
getRarity(): Rarity;
}
export interface ChanceEntry {
chance: number;
creationMethod: (any: any) => any;
}
export interface Drop extends Drawable, Acting {
pickup()
}
export interface Particle extends Drawable, Placeable, Acting {
}
export interface Placeable {
move(any?: any)
getSize();
getPosition(): Vector;
}
export interface MouseInteracting {
hit(pos: Vector): boolean
clickAction(pos: Vector): void
}
export interface MouseInterActingContainer {
mouseDown(pos: Vector)
}
export interface Projectile extends Drawable {
}
export interface Equipment {
getOffset(): Vector;
setOffset(vec: Vector);
}
export interface Weapon extends Drawable, Equipment, Leveling {
act()
}
export interface Shooting {
createProjectile(): Projectile;
removeProjectile(projectile: Projectile)
}
export interface Drawable extends Placeable {
draw(ctx: CanvasRenderingContext2D);
}
export interface DrawContainer {
draw(ctx: CanvasRenderingContext2D);
}

View File

@@ -0,0 +1,271 @@
import type {ChanceEntry, Item} from "./interfaces.ts";
import {Player} from "./Player.ts";
import {randomItem} from "./utils.ts";
import {ChainBall, HomingPistol, Pistol, Spear, SpreadWeapon} from "./weapons.ts";
import type {World} from "./World.ts";
import {PlayerStats} from "./stats.ts";
export enum Rarity {
GARBAGE= 'GARBAGE',
COMMON = 'COMMON',
UNCOMMON = 'UNCOMMON',
RARE = 'RARE',
EPIC = 'EPIC',
LEGENDARY = 'LEGENDARY',
GODLY = 'GODLY'
}
export class ItemManagement {
private static ITEMS: Item[] = []
static addItem(item: Item) {
this.ITEMS.push(item)
}
static getItemsWithRarityFactor(): ChanceEntry[] {
let items: ChanceEntry[] = []
this.ITEMS.forEach((item) => {
items.push({chance: rarityWeight(item.getRarity()), creationMethod: () => item})
})
return items;
}
static getRandomItem() {
return randomItem(this.ITEMS)
}
static initializeItems() {
this.ITEMS.push(new SpeedUp())
this.ITEMS.push(new HealthUp())
this.ITEMS.push(new HomingPistolItem())
this.ITEMS.push(new PistolItem())
this.ITEMS.push(new SpreadWeaponItem())
this.ITEMS.push(new ChainBallWeaponItem())
this.ITEMS.push(new PullRangeUp())
this.ITEMS.push(new SpearWeaponItem())
this.ITEMS.push(new DamageUpFactor())
this.ITEMS.push(new DamageFactorUp())
this.ITEMS.push(new DamageFactorUpFactor())
this.ITEMS.push(new DamageUp())
}
}
export function rarityWeight(rarity: Rarity): number {
switch (rarity) {
case Rarity.GARBAGE: return 80;
case Rarity.COMMON: return 65;
case Rarity.UNCOMMON: return 50;
case Rarity.RARE: return 30;
case Rarity.EPIC: return 15;
case Rarity.LEGENDARY: return 5;
case Rarity.GODLY: return 1;
}
}
export function rarityColor(rarity: Rarity): string {
switch (rarity) {
case Rarity.GARBAGE: return 'white';
case Rarity.COMMON: return 'gray';
case Rarity.UNCOMMON: return 'blue';
case Rarity.RARE: return 'green';
case Rarity.EPIC: return 'orange';
case Rarity.LEGENDARY: return 'violett';
case Rarity.GODLY: return 'red';
}
}
export abstract class BaseItem implements Item {
constructor() {
}
pickup(player: Player, world: World) {
player.addItem(this)
}
abstract name();
abstract getRarity(): Rarity;
}
export class SpeedUp extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1, PlayerStats.increaseSpeed)
super.pickup(player, world)
}
name() {
return 'speed'
}
getRarity(): Rarity {
return Rarity.LEGENDARY;
}
}
export class PullRangeUp extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1.1, PlayerStats.factorPullRange)
super.pickup(player, world)
}
name() {
return 'pull range'
}
getRarity(): Rarity {
return Rarity.COMMON;
}
}
export class HealthUp extends BaseItem {
pickup(player: Player, world: World) {
let healthAmount = 1;
player.changeBaseStat(healthAmount, PlayerStats.increaseHealth)
player.heal(healthAmount)
super.pickup(player, world)
}
name() {
return 'health'
}
getRarity(): Rarity {
return Rarity.COMMON;
}
}
export class HomingPistolItem extends BaseItem {
pickup(player: Player, world: World) {
player.addWeapon(HomingPistol.generateHomingPistol(world))
super.pickup(player, world)
}
name() {
return 'homingp'
}
getRarity(): Rarity {
return Rarity.RARE;
}
}
export class PistolItem extends BaseItem {
pickup(player: Player, world: World) {
player.addWeapon(Pistol.generatePistol(world))
super.pickup(player, world)
}
name() {
return 'pistol'
}
getRarity(): Rarity {
return Rarity.RARE;
}
}
export class SpreadWeaponItem extends BaseItem {
pickup(player: Player, world: World) {
player.addWeapon(SpreadWeapon.generateSpreadWeapon(world))
super.pickup(player, world)
}
name() {
return 'spreadp'
}
getRarity(): Rarity {
return Rarity.EPIC;
}
}
export class ChainBallWeaponItem extends BaseItem {
pickup(player: Player, world: World) {
player.addWeapon(ChainBall.createChainBall(world))
super.pickup(player, world)
}
name() {
return 'chain ball'
}
getRarity(): Rarity {
return Rarity.EPIC;
}
}
export class SpearWeaponItem extends BaseItem {
pickup(player: Player, world: World) {
player.addWeapon(Spear.createSpear(world))
super.pickup(player, world)
}
name() {
return 'spear'
}
getRarity(): Rarity {
return Rarity.EPIC;
}
}
export class DamageFactorUpFactor extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1, PlayerStats.factorDamageFactor)
super.pickup(player, world)
}
name() {
return 'damageFactorUpFactor'
}
getRarity(): Rarity {
return Rarity.LEGENDARY;
}
}
export class DamageUp extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1, PlayerStats.increaseDamage)
super.pickup(player, world)
}
name() {
return 'damageUp'
}
getRarity(): Rarity {
return Rarity.COMMON;
}
}
export class DamageUpFactor extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1, PlayerStats.factorDamage)
super.pickup(player, world)
}
name() {
return 'damageUpFactor'
}
getRarity(): Rarity {
return Rarity.LEGENDARY;
}
}
export class DamageFactorUp extends BaseItem {
pickup(player: Player, world: World) {
player.changeBaseStat(1, PlayerStats.increaseDamageFactor)
super.pickup(player, world)
}
name() {
return 'damageFactorUp'
}
getRarity(): Rarity {
return Rarity.UNCOMMON;
}
}

View File

@@ -0,0 +1,175 @@
import './style.css'
import {docReady} from "canvas-common";
import {World} from "./World.ts";
import {Player} from "./Player.ts";
import {Vector} from "./base.ts";
import {BasicEnemy, ContainerEnemy, HealthEnemy, ShootingEnemy} from "./Enemies.ts";
import {HUD} from "./ui.ts";
import {Pistol} from "./weapons.ts";
import {ItemManagement} from "./items.ts";
let hud: HUD;
let world: World;
let config: Config;
let state: WorldState;
let ctx: CanvasRenderingContext2D;
let canvas;
export class Config {
private _fps: number = 60;
get fps(): number {
return this._fps;
}
}
export class WorldState {
private _ended: boolean = false;
get ended(): boolean {
return this._ended;
}
}
function updateCanvas() {
ctx.clearRect(0, 0, world.size.x, world.size.y);
hud.draw(ctx)
if(!state.ended) {
world.triggerTick()
world.enemiesAct()
world.player.act()
world.draw()
for(let key in keys) {
if(keys[key].state) {
keys[key].fun(keys[key].intensity)
}
}
} else {
ctx.fillText('End', 15, 15)
}
setTimeout(function () {
requestAnimationFrame(updateCanvas);
}, 1000 / config.fps)
}
function makeKey(char, fun) {
keys[char] = {
state: false,
fun: fun
}
}
let keys = {};
makeKey('w', function (intensity: number) {
world.movePlayer(new Vector(0, -world.player.effectiveStats.speed * intensity))
})
makeKey('s', function (intensity: number) {
world.movePlayer(new Vector(0, world.player.effectiveStats.speed * intensity))
})
makeKey('a', function (intensity: number) {
world.movePlayer(new Vector(-world.player.effectiveStats.speed * intensity, 0))
})
makeKey('d', function (intensity: number) {
world.movePlayer(new Vector(world.player.effectiveStats.speed * intensity, 0))
})
function keyUp(event) {
if(event.key in keys) {
keys[event.key].state = false;
keys[event.key].intensity = 0;
}
}
function keyDown(event) {
if(event.key in keys) {
keys[event.key].state = true;
keys[event.key].intensity = 1;
}
}
document.onkeyup = keyUp;
document.onkeydown = keyDown;
docReady(function () {
canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
config = new Config();
let player = Player.generatePlayer(new Vector(window.innerWidth /2, window.innerHeight / 2));
world = new World(player, ctx, new Vector(window.innerWidth, window.innerHeight));
player.world = world; // not sure if this is great design
state = new WorldState();
setInterval(() => {
BasicEnemy.spawnBasicEnemy(world)
}, 1_000)
setInterval(() => {
ShootingEnemy.spawnShootingEnemy(world)
}, 3_000)
setInterval(() => {
HealthEnemy.spawnHealthEnemy(world)
}, 15_000)
setInterval(() => {
ContainerEnemy.spawnContainerEnemy(world)
}, 10_000)
player.addWeapon(Pistol.generatePistol(world))
hud = new HUD(world, keys);
canvas.onmousedown = (event) => {
event.preventDefault()
let pos = new Vector(event.x, event.y)
hud.mouseDown(pos)
}
canvas.onmouseup = (event) => {
event.preventDefault()
let pos = new Vector(event.x, event.y)
hud.mouseUp(pos)
}
canvas.onmousemove = (event) => {
event.preventDefault()
let pos = new Vector(event.x, event.y)
hud.mouseMove(pos)
}
canvas.addEventListener("touchstart", (event) => {
event.preventDefault()
let touch = event.touches[0]
let pos = new Vector(touch.clientX, touch.clientY)
hud.mouseDown(pos)
});
canvas.addEventListener("touchend", (event) => {
event.preventDefault()
let pos = new Vector(event.clientX, event.clientY)
hud.mouseUp(pos)
});
canvas.addEventListener("touchmove", (event) => {
event.preventDefault()
let touch = event.touches[0]
let pos = new Vector(touch.clientX, touch.clientY)
hud.mouseMove(pos)
});
ItemManagement.initializeItems()
requestAnimationFrame(updateCanvas);
})

View File

@@ -0,0 +1,75 @@
import type {Particle} from "./interfaces.ts";
import {Vector} from "./base.ts";
import {World} from "./World.ts";
abstract class BaseParticle implements Particle {
protected _position: Vector;
protected world: World;
constructor(position: Vector, world: World) {
this._position = position.clone();
this.world = world;
}
getPosition(): Vector {
return this._position;
}
getSize() {
}
move(any?: any) {
this._position = this._position.add(new Vector(0, -0.5))
}
draw(ctx: CanvasRenderingContext2D) {
}
act() {
this.move()
}
tick(seconds: number, tick: number) {
}
}
export class NumberDisplayParticle extends BaseParticle {
private number: number;
private secondsToDisplay: number = 2;
private alreadyDisplayed: number = 0;
private color: string;
constructor(position: Vector, world: World, healthAmount: number, color: string) {
super(position, world);
this.number = healthAmount;
this.color = color
}
draw(ctx: CanvasRenderingContext2D) {
ctx.fillStyle = this.color;
ctx.fillText(this.number + '', this._position.x, this._position.y);
}
tick(seconds: number, tick: number) {
this.alreadyDisplayed += seconds;
if(this.alreadyDisplayed > this.secondsToDisplay) {
this.world.removeParticle(this)
}
}
static spawnNumberParticle(world: World, health: number, position: Vector, color?: string) {
if(!color) {
color = 'red'
}
world.addParticle(this.createNumberParticle(world, health, position, color))
}
static createNumberParticle(world: World, health: number, position: Vector, color?: string) {
if(!color) {
color = 'red'
}
let particle = new NumberDisplayParticle(position, world, health, color)
return particle
}
}

View File

@@ -0,0 +1,326 @@
import type {Acting, Placeable, Healthy } from "./interfaces.ts";
import type {Vector} from "./base.ts";
import {World} from "./World.ts";
import {DeadPoint, Vector} from "./base.ts";
import {
circleLineCollision,
fillDot,
getCoordinatesSplit,
moveInDirectionOf,
pointOnLineWithinLine,
straightMove,
toRad
} from "./utils.ts";
import {InstanceOfUtils} from "./instance.ts";
import {ChainBall, MeleeWeapon} from "./weapons.ts";
import type {Enemy} from "./Enemies.ts";
import {ProjectileStats} from "./stats.ts";
import {ProjectileStatus} from "./status.ts";
export abstract class Projectile implements Acting, Placeable {
protected position: Vector;
protected speedVec: Vector;
protected world: World;
protected parent: any;
protected color: string
protected stats: ProjectileStats;
protected status: ProjectileStatus;
protected lastPosition: Vector;
protected secondToLastPosition?: Vector
protected lastColliding?: Placeable;
constructor(position: Vector, speedVec: Vector, stats: ProjectileStats, world: World, parent: any) {
this.position = position.clone();
this.speedVec = speedVec.clone();
this.world = world;
this.parent = parent;
this.stats = stats;
this.status = new ProjectileStatus(stats.piercings)
}
die() {
this.world.removeProjectile(this)
}
act() {
this.move()
if(this.parent !== this.world.player) {
if(this.position.distanceTo(this.world.player.position) < (this.stats.size + this.world.player.effectiveStats.size) && this.status.collisionCooldown.cooledDown()) {
this.impactPlayer()
this.status.collisionCooldown.resetCooldown()
}
} else if(this.parent === this.world.player) {
let closestTargetTo = this.world.getClosestTargetToButNot(this.position, this.lastColliding);
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
let target: Placeable = closestTargetTo[1]!;
if(target.getPosition().distanceTo(this.position) < (this.stats.size + target.getSize())
|| circleLineCollision(target.getPosition(), target.getSize(), this.position, this.lastPosition)) {
if(target !== this.lastColliding) {
if(InstanceOfUtils.instanceOfHealthy(target)) {
let healthy = target as Healthy;
healthy.takeDamage(this.stats.damage)
if(!this.status.hasPiercingLeft()) {
this.die()
}
this.status.decreasePiercings()
} else {
this.die()
}
}
this.lastColliding = target;
} else {
this.lastColliding = undefined;
}
} else {
this.lastColliding = undefined;
}
}
this.status.collisionCooldown.decreaseCooldown()
this.checkWorldBorder()
}
checkWorldBorder() {
if(this.world.outside(this.position)) {
this.die()
}
}
impactPlayer() {
this.world.player.takeDamage(this.stats.damage)
this.die()
};
draw(ctx: CanvasRenderingContext2D) {
fillDot(this.position, this.stats.size, this.color, ctx)
}
move() {
this.secondToLastPosition = this.lastPosition;
this.lastPosition = this.position.clone()
}
getPosition(): Vector {
return this.position;
}
getSize() {
return this.stats.size
}
}
export class StraightProjectile extends Projectile {
constructor(position: Vector, dirVector: Vector, stats: ProjectileStats, world: World, parent: any) {
super(position, dirVector, stats, world, parent);
}
move() {
super.move()
this.position = straightMove(this.position, this.speedVec)
}
static createStraightProjectile(world: World, start: Vector, targetPosition: Vector, parent: any, stats: ProjectileStats, color?: string) {
let dirVector = Vector.createVector(targetPosition, start).normalize().multiply(stats.speed);
let projectile = new StraightProjectile(start, dirVector, stats, world, parent)
projectile.color = color === undefined ? 'red' : color!;
world.addProjectile(projectile)
return projectile;
}
}
export class ChainBallProjectile extends Projectile {
private weapon: ChainBall;
private movingBack: boolean = false;
private target: Vector;
private lastHit: Enemy[] = []
constructor(position: Vector, speedVec: Vector, stats: ProjectileStats, world: World, parent: any, weapon: ChainBall, target: Vector) {
super(position, speedVec, stats, world, parent);
this.weapon = weapon;
this.target = target.clone()
}
draw(ctx: CanvasRenderingContext2D) {
fillDot(this.position, this.stats.size, 'pink', ctx) // todo render the weapon instead
}
act() {
this.move()
// TODO It seems that the projectile doesnt _quite_ hit the enemy, but is just getting close to it
let hitEnemies = this.world.getAllInRange(this.position, this.stats.size * 2);
hitEnemies.forEach(value => {
if(this.lastHit.indexOf(value) !== -1) {
if(InstanceOfUtils.instanceOfHealthy(value)) {
let healthy = value as Healthy;
healthy.takeDamage(this.stats.damage)
}
}
})
this.lastHit = hitEnemies;
}
move() {
super.move()
if(!this.movingBack) {
this.position = straightMove(this.position, this.speedVec)
if(this.position.distanceTo(this.target) < this.stats.size) {
this.movingBack = true;
this.speedVec = this.speedVec.multiply(3)
}
} else {
this.position = moveInDirectionOf(this.position, this.world.player.position, this.speedVec.vecLength())
}
if(this.movingBack && this.position.distanceTo(this.world.player.position) < (this.stats.size + this.world.player.effectiveStats.size)) {
this.weapon.reset();
this.die()
}
}
static createChainBallProjectile(world: World, start: Vector, targetPosition: Vector, parent: any, stats: ProjectileStats, weapon: MeleeWeapon, color?: string) {
let dirVector = Vector.createVector(targetPosition, start).normalize().multiply(stats.speed);
let projectile = new ChainBallProjectile(start, dirVector, stats, world, parent, weapon, targetPosition)
projectile.color = color === undefined ? 'red' : color!;
world.addProjectile(projectile)
return projectile;
}
}
export class StraightMeleeWeaponProjectile extends Projectile {
private weapon: MeleeWeapon;
private movingBack: boolean = false;
private target: Vector;
private lastHit: Enemy[] = []
constructor(position: Vector, speedVec: Vector, stats: ProjectileStats, world: World, parent: any, weapon: ChainBall, target: Vector) {
super(position, speedVec, stats, world, parent);
this.weapon = weapon;
this.target = target.clone()
}
draw(ctx: CanvasRenderingContext2D) {
let position = this.getRealPosition();
fillDot(position, this.stats.size, 'brown', ctx) // todo render the weapon instead
}
getRealPosition(): Vector {
return this.world.player.getPosition().add(this.position)
}
act() {
this.move()
let hitEnemies = this.world.getAllInRange(this.getRealPosition(), this.stats.size * 2);
hitEnemies.forEach(value => {
if(this.lastHit.indexOf(value) !== -1) {
if(InstanceOfUtils.instanceOfHealthy(value)) {
let healthy = value as Healthy;
healthy.takeDamage(this.stats.damage)
}
}
})
this.lastHit = hitEnemies;
}
move() {
super.move()
if(!this.movingBack) {
this.position = straightMove(this.position, this.speedVec)
if(this.position.distanceTo(this.target) < this.stats.size) {
this.movingBack = true;
this.speedVec = this.speedVec.multiply(3)
}
} else {
this.position = moveInDirectionOf(this.position, new Vector(0, 0), this.speedVec.vecLength())
}
if(this.movingBack && this.position.distanceTo(new Vector(0, 0)) < (this.stats.size + this.world.player.effectiveStats.size)) {
this.weapon.reset();
this.die()
}
}
static createStraightMeleeProjectile(world: World, start: Vector, targetPosition: Vector, parent: any, stats: ProjectileStats, weapon: MeleeWeapon, color?: string) {
let dirVector = Vector.createVector(targetPosition, start).normalize().multiply(stats.speed);
let projectile = new StraightMeleeWeaponProjectile(start, dirVector, stats, world, parent, weapon, targetPosition)
projectile.color = color === undefined ? 'red' : color!;
world.addProjectile(projectile)
return projectile;
}
}
export class HomingProjectile extends Projectile {
private target: Placeable;
constructor(position: Vector, speedVec: Vector, stats: ProjectileStats, world: World, parent: any, target: Placeable) {
super(position, speedVec, stats, world, parent);
this.target = target;
}
move() {
super.move()
this.position = moveInDirectionOf(this.position, this.target.getPosition(), this.speedVec.vecLength())
if(InstanceOfUtils.instanceOfHealthy(this.target)) {
let target = this.target as Healthy
if(target.dead()) {
let closestTargetTo = this.world.getClosestTargetTo(this.position)
let oldTargetDirection = Vector.createVector(this.target.getPosition(), this.position)
let justMovedDirection = Vector.createVector(this.position, this.lastPosition).normalize()
let olderMovedDirection: Vector;
if(this.secondToLastPosition !== undefined) {
olderMovedDirection = Vector.createVector(this.lastPosition, this.secondToLastPosition).normalize();
} else {
olderMovedDirection = new Vector(0, 1)
}
if (closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
let newTargetPosition = closestTargetTo[1]!.getPosition();
let newDir = Vector.createVector(newTargetPosition, this.position)
let newDirAngle = newDir.angleTo(olderMovedDirection);
if(Math.abs(newDirAngle) <= toRad(30)) {
this.target = closestTargetTo[1]!;
} else {
if(pointOnLineWithinLine(this.target.getPosition(), this.lastPosition, this.position)) {
justMovedDirection = olderMovedDirection
}
this.target = new DeadPoint(this.position.add(justMovedDirection.multiply(this.world.maxValue())))
}
} else {
if(pointOnLineWithinLine(this.target.getPosition(), this.lastPosition, this.position)) {
justMovedDirection = olderMovedDirection
}
this.target = new DeadPoint(this.position.add(justMovedDirection.multiply(Math.max(this.world.size.x, this.world.size.y))))
}
}
}
this.checkWorldBorder()
}
static createHomingProjectile(world: World, start: Vector, parent: any, target: Placeable, stats: ProjectileStats, color?: string) {
let projectile = new HomingProjectile(start, new Vector(0, stats.speed), stats, world, parent, target)
projectile.color = color === undefined ? 'red' : color!;
world.addProjectile(projectile)
return projectile;
}
die() {
if(this.stats.deathSplit && this.stats.deathSplit > 0 && Math.random() > this.stats.deathSplitChance) {
let splits = this.stats.deathSplit;
let directionalVectors = getCoordinatesSplit(splits);
let stats = new ProjectileStats()
.withSize(this.stats.size / 2)
.withDamage(this.stats.damage / 2)
.withSpeed(this.stats.speed / 2)
directionalVectors.forEach(value => {
let target = this.position.add(value.multiply(this.world.maxValue()))
StraightProjectile.createStraightProjectile(this.world, this.position, target, this.parent, stats, 'white')
})
}
super.die();
}
}

View File

@@ -0,0 +1,235 @@
export class PlayerStats {
private _speed: number;
private _size: number;
private _health: number;
private _pullRange: number;
private _weaponRange: number;
private _weaponRangeFactor: number;
private _healthRegen: number;
private _damage: number;
private _damageFactor: number;
constructor() {
this._speed = 3;
this._size = 5;
this._health = 10;
this._pullRange = 150;
this._weaponRange = 250;
this._weaponRangeFactor = 1;
this._healthRegen = 0.001;
this._damage = 0;
this._damageFactor = 0;
}
resetToBasic() {
this._speed = 0;
this._health = 0;
this._pullRange = 0;
this._weaponRange = 0;
this._weaponRangeFactor = 1;
this._healthRegen = 0.001;
this._damage = 0;
this._damageFactor = 0;
}
increaseLevel() {
this._speed *= 1.1;
this._health += 1
this._pullRange *= 1.1;
this._weaponRange *= 1.25
this._weaponRangeFactor += 0.1
this._healthRegen += 0.1
this._damage += 1;
this._damageFactor += 0.1;
}
mergeStats(otherStats: PlayerStats) {
this._speed += otherStats._speed;
this._health += otherStats._health;
this._pullRange += otherStats._pullRange;
this._weaponRange += otherStats._weaponRange
this._weaponRangeFactor += otherStats._weaponRangeFactor;
this._healthRegen += otherStats._healthRegen;
this._damage += otherStats._damage;
this._damageFactor += otherStats._damageFactor
}
clone() {
let newStats = new PlayerStats();
newStats.mergeStats(this)
return newStats;
}
changeStat(value: number, statFun: (stats: PlayerStats, value: number) => void) {
statFun(this, value)
}
static increaseSpeed(stats: PlayerStats, value: number) {
stats._speed += value
}
static factorSpeed(stats: PlayerStats, value: number) {
stats._speed *= value
}
static factorDamage(stats: PlayerStats, value: number) {
stats._damage *= value
}
static increaseDamage(stats: PlayerStats, value: number) {
stats._damage += value
}
static factorDamageFactor(stats: PlayerStats, value: number) {
stats._damageFactor *= value
}
static increaseDamageFactor(stats: PlayerStats, value: number) {
stats._damageFactor += value
}
static increasePullRange(stats: PlayerStats, value: number) {
stats._pullRange += value
}
static factorPullRange(stats: PlayerStats, value: number) {
stats._pullRange *= value
}
static increaseHealth(stats: PlayerStats, value: number) {
stats._health += value
}
get speed(): number {
return this._speed;
}
get size(): number {
return this._size;
}
get pullRange(): number {
return this._pullRange;
}
get health(): number {
return this._health;
}
get weaponRange(): number {
return this._weaponRange
}
get healthRegen(): number {
return this._healthRegen;
}
get effectiveWeaponRange(): number {
return this._weaponRange * this._weaponRangeFactor;
}
get effectiveDamage(): number {
return this._damage * this._damageFactor;
}
public static defaultPlayerStats(): PlayerStats {
return new PlayerStats();
}
}
export class ProjectileStats {
private _piercings: number;
private _size: number;
private _damage: number;
private _speed: number;
private _deathSplit: number;
private _deathSplitChance: number;
constructor() {
this._size = 1
}
withPiercings(value: number) {
this._piercings = value;
return this;
}
withSize(value: number) {
this._size = Math.max(value, 1);
return this;
}
withDamage(value: number) {
this._damage = value;
return this;
}
withSpeed(value: number) {
this._speed = value;
return this;
}
withDeathSplit(value: number) {
this._deathSplit = value;
return this;
}
withDeathSplitChance(value: number) {
this._deathSplitChance = value;
return this;
}
get piercings(): number {
return this._piercings;
}
get size(): number {
return this._size;
}
get speed(): number {
return this._speed;
}
get damage(): number {
return this._damage;
}
get deathSplitChance(): number {
return this._deathSplitChance;
}
get deathSplit(): number {
return this._deathSplit;
}
}
export class EnemyStats {
private _healthFactor: number;
private _baseHealth: number;
constructor() {
this._healthFactor = 1;
this._baseHealth = 10
}
withHealthFactor(healthFactor: number) {
this._healthFactor = healthFactor;
return this
}
withBaseHealth(baseHealth: number) {
this._baseHealth = baseHealth;
return this
}
getEffectiveHealth() {
return this._baseHealth * this._healthFactor
}
}

View File

@@ -0,0 +1,88 @@
import {Cooldown} from "./base.ts";
export class PlayerStatus {
constructor(private _health: number,
private _wealth: number,
private _level: number) {
}
get level(): number {
return this._level;
}
set level(value: number) {
this._level = value;
}
get health(): number {
return this._health;
}
set health(value: number) {
this._health = value;
}
get dead(): boolean {
return this._health <= 0
}
get wealth(): number {
return this._wealth;
}
set wealth(value: number) {
this._wealth = value;
}
increaseLevel() {
this._level += 1
}
}
export class EnemyStatus {
constructor(private _health: number) {
}
get health(): number {
return this._health;
}
get dead(): boolean {
return this._health <= 0;
}
set health(value: number) {
this._health = value;
}
}
export class ProjectileStatus {
private _piercingsLeft: number;
private _collisionCooldown: Cooldown;
constructor(piercingsLeft: number) {
this._piercingsLeft = piercingsLeft;
this._collisionCooldown = new Cooldown(10)
}
get piercingsLeft(): number {
return this._piercingsLeft;
}
hasPiercingLeft(): boolean {
return this.piercingsLeft > 0;
}
get collisionCooldown(): Cooldown {
return this._collisionCooldown;
}
decreasePiercings() {
this._piercingsLeft -= 1;
}
}

View File

@@ -0,0 +1,4 @@
html, body, div, canvas {
margin: 0;
padding: 0;
}

295
absurd-survivors/src/ui.ts Normal file
View File

@@ -0,0 +1,295 @@
import type {Drawable, DrawContainer, MouseInteracting, MouseInterActingContainer} from "./interfaces.ts";
import {World} from "./World.ts";
import {Vector} from "./base.ts";
import {drawDot, fillDot, isMobile, rectPointIntersection} from "./utils.ts";
export class HUD implements DrawContainer {
private health: PlayerInfo;
private controls: Controls | undefined;
private world: World;
constructor(world: World, keys: any) {
this.world = world;
this.health = new PlayerInfo(world);
if (isMobile()) {
this.controls = new Controls(world, keys);
}
}
draw(ctx: CanvasRenderingContext2D) {
this.health.draw(ctx)
if(this.controls) {
this.controls.draw(ctx)
}
}
mouseDown(pos: Vector) {
if(this.controls) {
this.controls.mouseDown(pos)
}
}
mouseUp(pos: Vector) {
if(this.controls) {
this.controls.mouseUp(pos)
}
}
mouseMove(pos: Vector) {
if(this.controls) {
this.controls.mouseMove(pos)
}
}
}
export class Controls implements DrawContainer, MouseInterActingContainer {
private world: World;
private centerPosition: Vector | undefined;
private keys: any;
private size: number;
constructor(world: World, keys: any) {
this.world = world;
this.keys = keys;
this.size = 60;
}
draw(ctx: CanvasRenderingContext2D) {
if(this.isPressed()) {
drawDot(this.centerPosition!, this.size, 'white', ctx)
}
}
isPressed() {
return this.centerPosition !== undefined;
}
mouseDown(pos: Vector) {
this.centerPosition = pos
}
mouseUp(pos: Vector) {
this.centerPosition = undefined;
let keys = ['a', 's', 'd', 'w']
keys.forEach(key => {
this.keys[key].state = false;
this.keys[key].intensity = 0;
})
this.keys['a'].state = false
}
setKeyTo(key: string, state: boolean, value: number) {
this.keys[key].state = state;
this.keys[key].intensity = value;
}
mouseMove(pos: Vector) {
if(this.isPressed()) {
let diff = Vector.createVector(pos, this.centerPosition!);
if(isNaN(diff.x) || isNaN(diff.y)) {
return;
}
diff = diff.normalize();
if(diff.x > 0) {
this.setKeyTo('a', false, 0)
this.setKeyTo('d', true, Math.abs(diff.x))
} else if(diff.x < 0){
this.setKeyTo('d', false, 0)
this.setKeyTo('a', true, Math.abs(diff.x))
}
if(diff.y > 0) {
this.setKeyTo('w', false, 0)
this.setKeyTo('s', true, Math.abs(diff.y))
} else if(diff.y < 0) {
this.setKeyTo('s', false, 0)
this.setKeyTo('w', true, Math.abs(diff.y))
}
}
}
}
export class PlayerInfo implements DrawContainer {
private bar: InfoBar;
private statLabels: StatLabel[] = []
private world: World;
private statLabelBase: Vector;
private static readonly STAT_LABEL_HEIGHT_OFFSET: number = 4;
constructor(world: World) {
this.world = world;
this.bar = new InfoBar(new Vector(0, 50), 50, 150, () => 'Health', () => this.world.player.status.health, () => this.world.player.effectiveStats.health)
this.statLabelBase = new Vector(0, 150)
this.statLabels = [
new StatLabel(() => 'Money', () => this.world.player.status.wealth),
new StatLabel(() => 'Level', () => this.world.player.status.level),
new StatLabel(() => 'Speed', () => Math.floor(this.world.player.effectiveStats.speed)),
new StatLabel(() => 'Pull range', () => Math.floor(this.world.player.effectiveStats.pullRange)),
new StatLabel(() => 'Weapon range', () => Math.floor(this.world.player.effectiveStats.effectiveWeaponRange)),
new StatLabel(() => 'Damage', () => Math.floor(this.world.player.effectiveStats.effectiveDamage))
]
}
draw(ctx: CanvasRenderingContext2D) {
this.bar.draw(ctx)
let metrics = ctx.measureText('I')
let upperCaseCharacterSize = new Vector(0, metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent).add(new Vector(0, PlayerInfo.STAT_LABEL_HEIGHT_OFFSET))
for (let i = 0; i < this.statLabels.length; i++){
const label = this.statLabels[i];
label.move(this.statLabelBase.add(upperCaseCharacterSize.multiply(i)))
label.draw(ctx);
}
}
}
export class RectButton implements Drawable, MouseInteracting {
protected position: Vector;
protected size: Vector;
protected borderColor: string = 'light-gray';
protected contentColor: string = 'gray';
protected label: string;
protected action: () => void;
protected releaseLambda?: () => void;
constructor(position: Vector, size: Vector, label: string, actionLambda: () => void, releaseLambda?: () => void) {
this.position = position;
this.size = size;
this.label = label;
this.action = actionLambda;
this.releaseLambda = releaseLambda;
}
draw(ctx: CanvasRenderingContext2D) {
ctx.beginPath()
ctx.fillStyle = this.contentColor;
ctx.strokeStyle = this.borderColor;
ctx.rect(this.position.x, this.position.y, this.size.x, this.size.y)
ctx.fill()
ctx.stroke();
ctx.fillStyle = 'black'
ctx.fillText(this.label, this.position.x + this.size.x / 2, this.position.y + this.size.y / 2)
ctx.closePath()
}
getPosition(): Vector {
return this.position;
}
getSize() {
}
move(any?: any) {
}
clickAction(pos: Vector) {
this.action()
}
releaseAction(pos: Vector) {
if(this.releaseLambda) {
this.releaseLambda()
}
}
hit(pos: Vector): boolean {
return rectPointIntersection(this.position, this.size, pos)
}
}
export class KeyboardButton extends RectButton {
constructor(position: Vector, size: Vector, key: string, keys: any) {
super(position, size, key.toUpperCase(),
() => { keys[key.toLowerCase()].state = true},
() => { keys[key.toLowerCase()].state = false}
)
}
}
export class StatLabel implements Drawable {
private position: Vector;
private borderColor: string = 'white';
private textLambda: () => string;
private valueLambda: () => number;
constructor(textLambda: () => string, valueLambda: () => number);
constructor(textLambda: () => string, valueLambda: () => number, position?: Vector) {
if(!position) {
position = new Vector(0, 0)
}
this.position = position;
this.textLambda = textLambda;
this.valueLambda = valueLambda;
}
draw(ctx: CanvasRenderingContext2D) {
ctx.beginPath();
ctx.strokeStyle = this.borderColor
let value = this.valueLambda();
let text = this.textLambda();
ctx.fillText(`${text}: ${value}`, this.position.x, this.position.y)
ctx.fill()
}
getPosition(): Vector {
return this.position;
}
move(position: Vector) {
this.position = position
}
getSize() {
}
}
export class InfoBar implements Drawable {
private position: Vector;
private height: number;
private width: number;
private fillColor: string = 'green';
private borderColor: string = 'white';
private textLambda: () => string;
private valueLambda: () => number;
private totalValueLambda: () => number;
constructor(position: Vector, height: number, width: number, textLambda: () => string, valueLambda: () => number, totalValueLambda: () => number) {
this.position = position;
this.height = height;
this.width = width;
this.textLambda = textLambda;
this.valueLambda = valueLambda;
this.totalValueLambda = totalValueLambda;
}
draw(ctx: CanvasRenderingContext2D) {
ctx.beginPath();
ctx.strokeStyle = this.borderColor
ctx.strokeRect(this.position.x, this.position.y, this.width, this.height)
ctx.fillStyle = this.fillColor;
let value = this.valueLambda();
let totalValue = this.totalValueLambda();
let usedWidth = (value / totalValue) * this.width;
ctx.fillRect(this.position.x, this.position.y, usedWidth, this.height)
ctx.fillStyle = this.borderColor
ctx.fillText(`${value}/${totalValue}`, this.position.x + this.width / 2, this.position.y + this.height / 2)
ctx.fill()
}
getPosition(): Vector {
return this.position;
}
move(any?: any) {
}
getSize() {
}
}

View File

@@ -0,0 +1,109 @@
import {Vector} from "./base.ts";
export function fillDot(position: Vector, size: number, color: string, ctx: CanvasRenderingContext2D) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(position.x, position.y, size, 0, 2 * Math.PI);
ctx.fill();
}
export function isMobile() {
// https://stackoverflow.com/questions/11381673/detecting-a-mobile-browser
let check = false;
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor);
return check;
}
export function drawDot(position: Vector, size: number, color: string, ctx: CanvasRenderingContext2D) {
ctx.beginPath();
ctx.strokeStyle = color;
ctx.arc(position.x, position.y, size, 0, 2 * Math.PI);
ctx.stroke();
}
export function moveInDirectionOf(position: Vector, target: Vector, speedFactor: number): Vector {
let playerVector = Vector.createVector(target, position);
let direction = playerVector.normalize()
return position.add(direction.multiply(speedFactor))
}
export function straightMove(position: Vector, speed: Vector): Vector {
return position.add(speed)
}
export function toRad(angle) {
return angle / 180 * Math.PI;
}
export function toDegrees(angle) {
return angle * 180 / Math.PI
}
export function pointInsideCircle(circleCenter: Vector, radius: number, point: Vector) {
return circleCenter.distanceTo(point) < radius;
}
export function linePointCollision(point: Vector, lineStart: Vector, lineEnd: Vector) {
let lineLength = Vector.createVector(lineEnd, lineStart).vecLength();
let distanceStart = Vector.createVector(lineStart, point).vecLength()
let distanceEnd = Vector.createVector(lineEnd, point).vecLength();
let buffer = 0.001
if((distanceStart + distanceEnd) >= (lineLength - buffer) && (distanceEnd + distanceStart) <= (lineLength + buffer)) {
return true;
}
return false;
}
export function pointOnLineWithinLine(circleCenter: Vector, lineStart: Vector, lineEnd: Vector) {
let lineVector = Vector.createVector(lineEnd, lineStart)
let vectorCenterLine = Vector.createVector(circleCenter, lineStart)
let dot = vectorCenterLine.dotProduct(lineVector)
let closestX = lineStart.x + (dot * (lineVector.x))
let closestY = lineStart.y + (dot * (lineVector.y))
let closestPoint = new Vector(closestX, closestY);
return linePointCollision(closestPoint, lineStart, lineEnd);
}
export function circleLineCollision(circleCenter: Vector, radius: number, lineStart: Vector, lineEnd: Vector) {
if(pointInsideCircle(circleCenter, radius, lineStart) || pointInsideCircle(circleCenter, radius, lineEnd)) {
return true;
}
let lineVector = Vector.createVector(lineEnd, lineStart)
let vectorCenterLine = Vector.createVector(circleCenter, lineStart)
let dot = vectorCenterLine.dotProduct(lineVector)
let closestX = lineStart.x + (dot * (lineVector.x))
let closestY = lineStart.y + (dot * (lineVector.y))
let closestPoint = new Vector(closestX, closestY);
let onSegment = linePointCollision(closestPoint, lineStart, lineEnd)
if(!onSegment) {
return false;
}
return pointInsideCircle(circleCenter, radius, closestPoint);
}
export function getCoordinatesSplit(amount: number) {
let angle = 2 * Math.PI / amount;
let points: Vector[] = [];
for (let i = 0; i < amount; i++) {
let x = Math.cos(angle * i)
let y = Math.sin(angle * i)
points.push(new Vector(x, y))
}
return points;
}
export function randomItem(items: any[]) {
return items[Math.floor(Math.random() * items.length)]
}
export function rectPointIntersection(topLeft: Vector, size: Vector, point: Vector) {
if (topLeft.x + size.x >= point.x &&
topLeft.x <= point.x &&
topLeft.y + size.y >= point.y &&
topLeft.y <= point.y) {
return true
} else {
return false;
}
}

1
absurd-survivors/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@@ -0,0 +1,406 @@
import type {Weapon} from "./interfaces.ts";
import {fillDot, toRad} from "./utils.ts";
import {Player} from "./Player.ts";
import {
HomingProjectile,
Projectile,
StraightProjectile,
ChainBallProjectile,
StraightMeleeWeaponProjectile
} from "./projectile.ts";
import {World} from "./World.ts";
import {Vector} from "./base.ts";
import {ProjectileStats} from "./stats.ts";
export abstract class BasicWeapon implements Weapon {
protected offset: Vector;
protected readonly player: Player
protected readonly world: World;
protected color: string;
protected size: number;
protected stats: WeaponStats;
protected _level: number;
constructor(world: World, stats: WeaponStats) {
this.player = world.player;
this.world = world;
this.stats = stats;
this._level = 1;
}
act() {
}
increaseLevel() {
this._level += 1;
this.stats.increase()
}
draw(ctx: CanvasRenderingContext2D) {
}
getPosition(): Vector {
return this.player.position.add(this.offset);
}
move(any?: any) {
}
getSize() {
return this.size;
}
getDamage() {
return this.stats.damage + this.player.effectiveStats.effectiveDamage;
}
getOffset(): Vector {
return this.offset;
}
setOffset(vec: Vector) {
this.offset = vec;
}
level() {
return this._level
}
}
export abstract class RangeWeapon extends BasicWeapon {
protected projectiles: [Projectile] = []
protected shootCooldown: number = 0;
abstract createProjectile(): boolean;
act() {
if(this.shootCooldown <= 0) {
if(this.createProjectile()) {
this.shootCooldown = this.stats.shootInterval;
}
}
this.shootCooldown -= 1;
}
calculateRange(): number {
return this.world.player.effectiveStats.effectiveWeaponRange + this.stats.effectiveWeaponRange;
}
}
export abstract class MeleeWeapon extends RangeWeapon {
protected launched: boolean;
act() {
if(this.shootCooldown <= 0) {
if(this.createProjectile()) {
this.launched = true;
this.shootCooldown = 1;
}
}
if(!this.launched) {
this.shootCooldown -= 1;
}
}
reset() {
this.shootCooldown = this.stats.shootInterval
this.launched = false;
}
calculateRange(): number {
return this.stats.effectiveWeaponRange;
}
}
export class Spear extends MeleeWeapon {
protected launched: boolean;
createProjectile(): boolean {
let range = this.calculateRange()
let closestTargetTo = this.world.getFarthestTargetButWithin(this.world.player.position, range);
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
let stats = new ProjectileStats()
.withPiercings(1000)
.withSize(3)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let offsetVector = Vector.createVector(closestTargetTo[1]!.getPosition(), this.player.position).multiply(1.1);
if(offsetVector.vecLength() < 15) {
offsetVector = offsetVector.normalize().multiply(40)
}
let projectile = StraightMeleeWeaponProjectile.createStraightMeleeProjectile(this.world, new Vector(0, 0), offsetVector, this.player, stats, this)
this.projectiles.push(projectile)
return true
} else {
return false;
}
}
static createSpear(world: World, offset?: Vector) {
if(!offset) {
offset = new Vector(5, 5)
}
let stats = new WeaponStats()
.withProjectileSpeed(5)
.withDamage(15)
.withWeaponRange(150)
.withShootInterval(50)
let pistol = new Spear(world, stats)
pistol.offset = offset;
pistol.size = 7;
pistol.color = 'brown';
return pistol;
}
}
export class ChainBall extends MeleeWeapon {
createProjectile(): boolean {
let range = this.calculateRange()
let closestTargetTo = this.world.getFarthestTargetButWithin(this.world.player.position, range);
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
let stats = new ProjectileStats()
.withPiercings(1000)
.withSize(3)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let projectile = ChainBallProjectile.createChainBallProjectile(this.world, this.getPosition(), closestTargetTo[1]!.getPosition(), this.player, stats, this)
this.projectiles.push(projectile)
return true
} else {
return false;
}
}
static createChainBall(world: World, offset?: Vector) {
if(!offset) {
offset = new Vector(5, 5)
}
let stats = new WeaponStats()
.withProjectileSpeed(3)
.withDamage(15)
.withWeaponRange(150)
.withShootInterval(20)
let pistol = new ChainBall(world, stats)
pistol.offset = offset;
pistol.size = 4;
pistol.color = 'gray';
return pistol;
}
}
export class HomingPistol extends RangeWeapon {
draw(ctx: CanvasRenderingContext2D) {
fillDot(this.getPosition(), 1, this.color, ctx)
}
createProjectile(): boolean {
let range = this.calculateRange()
let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position, range);
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
let stats = new ProjectileStats()
.withPiercings(this.stats.projectilePiercings)
.withSize(1)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let projectile = HomingProjectile.createHomingProjectile(this.world, this.getPosition(), this.player, closestTargetTo[1]!, stats, 'yellow')
this.projectiles.push(projectile)
return true
} else {
return false;
}
}
static generateHomingPistol(world: World, offset?: Vector) {
if(!offset) {
offset = new Vector(5, 5)
}
let stats = new WeaponStats()
.withProjectileSpeed(3)
.withDamage(3)
.withShootInterval(50)
let pistol = new HomingPistol(world, stats)
pistol.offset = offset;
pistol.size = 5;
pistol.color = 'yellow';
return pistol;
}
}
export class Pistol extends RangeWeapon {
draw(ctx: CanvasRenderingContext2D) {
fillDot(this.getPosition(), 1, this.color, ctx)
}
private createProjectile(): boolean {
let range = this.calculateRange()
let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position, range);
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
let stats = new ProjectileStats()
.withPiercings(this.stats.projectilePiercings)
.withSize(1)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let projectile = StraightProjectile.createStraightProjectile(this.world, this.getPosition(), closestTargetTo[1]!.getPosition(), this.player, stats, 'pink')
this.projectiles.push(projectile)
return true
} else {
return false;
}
}
static generatePistol(world: World, offset?: Vector) {
if(!offset) {
offset = new Vector(5, 5)
}
let stats = new WeaponStats()
.withProjectileSpeed(4)
.withDamage(4)
.withShootInterval(25)
let pistol = new Pistol(world, stats)
pistol.offset = offset;
pistol.size = 5;
pistol.color = 'brown';
return pistol;
}
}
export class SpreadWeapon extends RangeWeapon {
draw(ctx: CanvasRenderingContext2D) {
fillDot(this.getPosition(), 1, this.color, ctx)
}
private createProjectile(): boolean {
let range = this.calculateRange()
let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position, range);
if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
let stats = new ProjectileStats()
.withPiercings(this.stats.projectilePiercings)
.withSize(1)
.withDamage(this.getDamage())
.withSpeed(this.stats.projectileSpeed);
let targetPosition = closestTargetTo[1]!.getPosition();
let weaponPosition = this.getPosition();
let mainVector = Vector.createVector(targetPosition, weaponPosition)
let mainProjectile = StraightProjectile.createStraightProjectile(this.world, weaponPosition, targetPosition, this.player, stats, 'gray')
this.projectiles.push(mainProjectile)
let upperVector = mainVector.rotate(toRad(-30))
let upperTarget = weaponPosition.add(upperVector.multiply(this.world.maxValue()))
let upperProjectile = StraightProjectile.createStraightProjectile(this.world, weaponPosition, upperTarget, this.player, stats, 'gray')
this.projectiles.push(upperProjectile)
let lowerVector = mainVector.rotate(toRad(30))
let lowerTarget = weaponPosition.add(lowerVector.multiply(this.world.maxValue()))
let lowerProjectile = StraightProjectile.createStraightProjectile(this.world, weaponPosition, lowerTarget, this.player, stats, 'gray')
this.projectiles.push(lowerProjectile)
return true
} else {
return false;
}
}
static generateSpreadWeapon(world: World, offset?: Vector) {
if(!offset) {
offset = new Vector(5, 5)
}
let stats = new WeaponStats()
.withProjectileSpeed(3)
.withDamage(3)
.withShootInterval(40)
let pistol = new SpreadWeapon(world, stats)
pistol.offset = offset;
pistol.size = 5;
pistol.color = 'gray';
return pistol;
}
}
export class WeaponStats {
private _weaponRange: number;
private _weaponRangeFactor: number;
private _projectileSpeed: number;
private _projectilePiercings: number;
private _damage: number;
private _shootInterval: number;
constructor() {
this._weaponRangeFactor = 1
this._weaponRange = 50
this._projectilePiercings = 0
this._projectileSpeed = 100
this._damage = 1
this._shootInterval = 50
}
increase() {
this._weaponRange *= 1.1;
this._weaponRangeFactor += 0.05;
this._damage *= 1.25;
this._shootInterval *= 0.9
}
withWeaponRange(value: number) {
this._weaponRange = value;
return this;
}
withWeaponRangeFactor(value: number) {
this._weaponRangeFactor = value;
return this;
}
withProjectileSpeed(value: number) {
this._projectileSpeed = value;
return this;
}
withShootInterval(value: number) {
this._shootInterval = value;
return this;
}
withProjectilePiercings(value: number) {
this._projectilePiercings = value;
return this;
}
withDamage(value: number) {
this._damage = value;
return this;
}
get weaponRange(): number {
return this._weaponRange;
}
get weaponRangeFactor(): number {
return this._weaponRangeFactor
}
get projectilePiercings(): number {
return this._projectilePiercings;
}
get damage(): number {
return this._damage;
}
get projectileSpeed(): number {
return this._projectileSpeed;
}
get shootInterval(): number {
return this._shootInterval;
}
get effectiveWeaponRange(): number {
return this._weaponRange * this._weaponRangeFactor
}
}

View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

View File

@@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
export default defineConfig({
base: './',
build: {
outDir: '../dist/survivors'
},
})

View File

@@ -28,7 +28,7 @@ var config = {
resetTapsDecreaseInterval: 2
},
general: {
fps: 30
fps: 60
}
};

872
bubbles/package-lock.json generated Normal file
View File

@@ -0,0 +1,872 @@
{
"name": "bubbles",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bubbles",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
},
"../canvas-common": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
"integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
"integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
"integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
"integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
"integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
"integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
"integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
"integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
"integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
"integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
"integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
"integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
"integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
"integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
"integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
"integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
"integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
"integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
"integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
"integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true
},
"node_modules/canvas-common": {
"resolved": "../canvas-common",
"link": true
},
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rollup": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
"dev": true,
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.46.2",
"@rollup/rollup-android-arm64": "4.46.2",
"@rollup/rollup-darwin-arm64": "4.46.2",
"@rollup/rollup-darwin-x64": "4.46.2",
"@rollup/rollup-freebsd-arm64": "4.46.2",
"@rollup/rollup-freebsd-x64": "4.46.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
"@rollup/rollup-linux-arm-musleabihf": "4.46.2",
"@rollup/rollup-linux-arm64-gnu": "4.46.2",
"@rollup/rollup-linux-arm64-musl": "4.46.2",
"@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
"@rollup/rollup-linux-ppc64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-musl": "4.46.2",
"@rollup/rollup-linux-s390x-gnu": "4.46.2",
"@rollup/rollup-linux-x64-gnu": "4.46.2",
"@rollup/rollup-linux-x64-musl": "4.46.2",
"@rollup/rollup-win32-arm64-msvc": "4.46.2",
"@rollup/rollup-win32-ia32-msvc": "4.46.2",
"@rollup/rollup-win32-x64-msvc": "4.46.2",
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/vite": {
"version": "5.4.19",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"dev": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
}
}

19
bubbles/package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "bubbles",
"version": "1.0.0",
"description": "",
"private": true,
"type": "module",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
}

22
bubbles/src/index.html Normal file
View File

@@ -0,0 +1,22 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>bubbles</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body, div, canvas {
margin: 0;
padding: 0;
}
html, body { width:100%; height:100%; }
canvas { display:block; }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="module" src="js/main.js"></script>
</body>
</html>

137
bubbles/src/js/main.js Normal file
View File

@@ -0,0 +1,137 @@
import {
createRainbowColors, docReady, pointDistance, randomInteger
} from "canvas-common";
var imageData = {};
var ctx = {};
var canvas = {};
var config = {
size: {
width: window.innerWidth,
height: window.innerHeight
},
buBbles: {
tileSize: 100,
growFactorDivider: 5,
radiusFactor: 1,
bubbleChance: 0.5
}
};
var rainbow = createRainbowColors(1/16);
// max distance from top left corner
var max = Math.sqrt(Math.pow(config.size.width + config.buBbles.tileSize, 2) + Math.pow(config.size.height + config.buBbles.tileSize, 2));
var startOffset = (rainbow.length * Math.random()) << 0;
function getRandomPoint(x, y) {
var distance = Math.sqrt(x * x + y * y);
var index = ((startOffset + distance / max * 100) << 0) % rainbow.length;
var color = rainbow[index];
color.a = 255;
return {
x: x + Math.random() * config.buBbles.tileSize,
y: y + Math.random() * config.buBbles.tileSize,
factor: Math.random() / config.buBbles.growFactorDivider,
radius: Math.random() * config.buBbles.radiusFactor,
color: color
};
}
function getTilePoints(){
var points = [];
for(var x = 0; x < config.size.width; x += config.buBbles.tileSize){
for(var y = 0; y < config.size.height; y += config.buBbles.tileSize){
points.push(getRandomPoint(x, y));
}
}
return points;
}
docReady(function () {
canvas = document.getElementById('canvas')
ctx = canvas.getContext("2d");
canvas.width = config.size.width;
canvas.height = config.size.height;
var pointsOfInterest = getTilePoints();
removeIntersecting(pointsOfInterest);
updateCanvas(pointsOfInterest);
});
function grow(points){
for(var i = 0; i < points.length; i++){
var currentPoint = points[i];
if(!intersectsAnotherPoint(currentPoint, points)){
currentPoint.radius += currentPoint.factor;
} else if(!currentPoint.spawnedAnother){
currentPoint.spawnedAnother = true;
currentPoint.radius += currentPoint.factor;
addAnotherRandomPoint(points);
}
}
if(Math.random() < config.buBbles.bubbleChance){
addAnotherRandomPoint(points);
}
}
function addAnotherRandomPoint(points){
var probablePoint = getRandomPoint(randomInteger(config.size.width), randomInteger(config.size.height));
if(!intersectsAnotherPoint(probablePoint, points)){
points.push(probablePoint);
}
}
function intersectsAnotherPoint(point, points){
var intersects = false;
points.forEach(function(pointToLookAt){
if(intersects) return;
var pointDist = pointDistance(point, pointToLookAt);
if(pointDist > 0 && pointDist < (point.radius + pointToLookAt.radius)){
intersects = true;
}
});
return intersects;
}
function updateCanvas(points){
ctx.clearRect(0, 0, config.size.width, config.size.height);
grow(points);
paintPoints(points, function(){
requestAnimationFrame(function(){
updateCanvas(points);
});
});
}
function removeIntersecting(points){
for(var i = 0; i < points.length; ){
if(intersectsAnotherPoint(points[i], points)){
points.splice(i, 1);
} else {
i++;
}
}
}
function paintPoints(points, cb){
for(var i = 0; i < points.length; i++){
var point = points[i];
if(point.radius < 0) continue;
ctx.beginPath();
ctx.fillStyle = point.color.styleRGB;
ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI);
ctx.fill();
}
cb();
}

24
bubbles/src/js/plugins.js Normal file
View File

@@ -0,0 +1,24 @@
// Avoid `console` errors in browsers that lack a console.
(function() {
var method;
var noop = function () {};
var methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
];
var length = methods.length;
var console = (window.console = window.console || {});
while (length--) {
method = methods[length];
// Only stub undefined methods.
if (!console[method]) {
console[method] = noop;
}
}
}());
// Place any jQuery/helper plugins in here.

9
bubbles/vite.config.js Normal file
View File

@@ -0,0 +1,9 @@
import { defineConfig } from 'vite'
export default defineConfig({
base: './',
root: 'src',
build: {
outDir: '../../dist/bubbles'
}
})

View File

@@ -74,6 +74,19 @@ export function downloadCanvas(name, canvas_obj, downloadBtn) {
downloadBtn.href = URL.createObjectURL(blob);
}
export function downloadCanvasWithoutButton(name, canvas_obj) {
var downloadBtn = document.getElementById('download');
downloadBtn.download = name + '_' + new Date().toISOString() + '.png';
var imageData = canvas_obj.toDataURL({
format: 'png',
multiplier: 4
});
var blob = dataURLtoBlob(imageData);
downloadBtn.href = URL.createObjectURL(blob);
}
export function convertColorToRgbaWithAlphaPlaceholderStyle(color) {
color.styleRGBA = 'rgba(%red, %green, %blue, %alpha)'
.replace('%red', color.r)
@@ -121,6 +134,13 @@ export function randomNumberButAtLeast(range, min) {
return (rand < min) ? min : rand;
}
export function randomNumber(n, cur) {
var rand = (Math.random() * n) - n / 2;
if (cur + rand < 0) return 0;
return rand;
}
export function getIndexForCoordinate(config, x, y) {
return (y * config.size.width + x) * 4;
}
@@ -142,3 +162,15 @@ export function angleBetweenTwoVectors(vectorA, vectorB){
export function dotProduct(vector1, vector2) {
return vector1.x * vector2.x + vector1.y * vector2.y;
}
export function randomInteger(n){
return (Math.random() * n) << 0
}
export function getCoordinates(config, index) {
return {x: index / 4 % config.size.width, y: Math.floor((index / 4 / config.size.width))}
}
export function formatInterval(date1, date2, message) {
console.log(message + ((date2 - date1) / 1000));
}

872
circleBs/package-lock.json generated Normal file
View File

@@ -0,0 +1,872 @@
{
"name": "bubbles",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bubbles",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
},
"../canvas-common": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
"integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
"integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
"integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
"integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
"integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
"integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
"integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
"integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
"integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
"integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
"integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
"integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
"integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
"integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
"integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
"integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
"integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
"integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
"integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
"integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true
},
"node_modules/canvas-common": {
"resolved": "../canvas-common",
"link": true
},
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rollup": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
"dev": true,
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.46.2",
"@rollup/rollup-android-arm64": "4.46.2",
"@rollup/rollup-darwin-arm64": "4.46.2",
"@rollup/rollup-darwin-x64": "4.46.2",
"@rollup/rollup-freebsd-arm64": "4.46.2",
"@rollup/rollup-freebsd-x64": "4.46.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
"@rollup/rollup-linux-arm-musleabihf": "4.46.2",
"@rollup/rollup-linux-arm64-gnu": "4.46.2",
"@rollup/rollup-linux-arm64-musl": "4.46.2",
"@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
"@rollup/rollup-linux-ppc64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-musl": "4.46.2",
"@rollup/rollup-linux-s390x-gnu": "4.46.2",
"@rollup/rollup-linux-x64-gnu": "4.46.2",
"@rollup/rollup-linux-x64-musl": "4.46.2",
"@rollup/rollup-win32-arm64-msvc": "4.46.2",
"@rollup/rollup-win32-ia32-msvc": "4.46.2",
"@rollup/rollup-win32-x64-msvc": "4.46.2",
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/vite": {
"version": "5.4.19",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"dev": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
}
}

19
circleBs/package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "circleBs",
"version": "1.0.0",
"description": "",
"private": true,
"type": "module",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
}

36
circleBs/src/index.html Normal file
View File

@@ -0,0 +1,36 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>circleBs</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body, div, canvas {
margin: 0;
padding: 0;
}
html, body { width:100%; height:100%; }
canvas { display:block; }
#clearBtn { left: 0px; }
#download { left: 60px; top: 3px; }
.controls {
position: absolute;
top: 0px;
right: 10px
}
</style>
</head>
<body>
<div id="content">
<canvas id="canvas" ></canvas>
<div class="controls">
<input class="top" id="clearBtn" type="button" onclick="reset()" value="clear" />
<a id="download" class="top" onclick="downloadCanvasCommon('circleBs', canvas)">Download</a>
</div>
</div>
<script type="module" src="js/main.js"></script>
</body>
</html>

85
circleBs/src/js/main.js Normal file
View File

@@ -0,0 +1,85 @@
import {
docReady, randomNumber
} from "canvas-common";
var canvas;
var ctx;
var config = {
size: {
height: window.innerHeight,
width: window.innerWidth
},
circleBs: {
maxAge: 1000
}
};
var objects = [];
function update(obj) {
obj.deltaX += randomNumber(2);
obj.deltaY += randomNumber(2);
obj.x += obj.deltaX;
obj.y += obj.deltaY;
obj.radius += randomNumber(2, obj.radius);
ctx.beginPath();
ctx.arc(obj.x, obj.y, obj.radius, 0, 2 * Math.PI);
ctx.strokeStyle = obj.col;
ctx.stroke();
if (obj.deltaX > obj.radius) obj.deltaX -= Math.random() * 2;
if (obj.deltaY > obj.radius) obj.deltaY -= Math.random() * 2;
if ((obj.x + obj.radius) > config.size.width || (obj.x - obj.radius) < 0) {
obj.deltaX *= -1;
}
if ((obj.y + obj.radius) > config.size.height || (obj.y - obj.radius) < 0) {
obj.deltaY *= -1;
}
obj.age += 1;
if (obj.age < config.circleBs.maxAge) {
requestAnimationFrame(function(){
update(obj);
});
//setTimeout(function () {
// update(obj);
//}, 10)
}
}
window.reset = reset;
function reset() {
objects.forEach(function (obj) {
obj.age = config.circleBs.maxAge;
});
objects = [];
ctx.clearRect(0, 0, config.size.width, config.size.height);
createNewCircle();
}
function createNewCircle() {
var obj = {
x: Math.random() * config.size.width,
y: Math.random() * config.size.height,
radius: Math.random() * 20,
deltaX: 0,
deltaY: 0,
age: 0,
col: '#' + (~~(Math.random() * 255)).toString(16) + (~~(Math.random() * 255)).toString(16) + (~~(Math.random() * 255)).toString(16)
};
objects.push(obj);
requestAnimationFrame(function(){
update(obj);
});
}
docReady(function () {
canvas = document.getElementById('canvas')
canvas.width = config.size.width;
canvas.height = config.size.height;
ctx = canvas.getContext("2d");
reset();
setInterval(createNewCircle, 5000)
});

View File

@@ -0,0 +1,24 @@
// Avoid `console` errors in browsers that lack a console.
(function() {
var method;
var noop = function () {};
var methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
];
var length = methods.length;
var console = (window.console = window.console || {});
while (length--) {
method = methods[length];
// Only stub undefined methods.
if (!console[method]) {
console[method] = noop;
}
}
}());
// Place any jQuery/helper plugins in here.

9
circleBs/vite.config.js Normal file
View File

@@ -0,0 +1,9 @@
import { defineConfig } from 'vite'
export default defineConfig({
base: './',
root: 'src',
build: {
outDir: '../../dist/circleBs'
}
})

272
clusterFilter/package-lock.json generated Normal file
View File

@@ -0,0 +1,272 @@
{
"name": "clusterFilter",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "clusterFilter",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
},
"../canvas-common": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/@esbuild/linux-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
"integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
"integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true
},
"node_modules/canvas-common": {
"resolved": "../canvas-common",
"link": true
},
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rollup": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
"dev": true,
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.46.2",
"@rollup/rollup-android-arm64": "4.46.2",
"@rollup/rollup-darwin-arm64": "4.46.2",
"@rollup/rollup-darwin-x64": "4.46.2",
"@rollup/rollup-freebsd-arm64": "4.46.2",
"@rollup/rollup-freebsd-x64": "4.46.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
"@rollup/rollup-linux-arm-musleabihf": "4.46.2",
"@rollup/rollup-linux-arm64-gnu": "4.46.2",
"@rollup/rollup-linux-arm64-musl": "4.46.2",
"@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
"@rollup/rollup-linux-ppc64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-musl": "4.46.2",
"@rollup/rollup-linux-s390x-gnu": "4.46.2",
"@rollup/rollup-linux-x64-gnu": "4.46.2",
"@rollup/rollup-linux-x64-musl": "4.46.2",
"@rollup/rollup-win32-arm64-msvc": "4.46.2",
"@rollup/rollup-win32-ia32-msvc": "4.46.2",
"@rollup/rollup-win32-x64-msvc": "4.46.2",
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/vite": {
"version": "5.4.19",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"dev": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
{
"name": "clusterFilter",
"version": "1.0.0",
"description": "",
"private": true,
"type": "module",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

View File

@@ -0,0 +1,54 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>clusterFilter</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body, div, canvas {
margin: 0;
padding: 0;
}
html, body { width:100%; height:100%; }
canvas { display:block; }
#clearBtn { left: 0px; }
#download { left: 60px; top: 3px; }
.controls {
position: absolute;
top: 0px;
right: 10px
}
</style>
</head>
<body>
<div class="controls">
A cluster is a point of color in which the pixels might be concentrated in. <br>
<label for="equidistant_cluster">Equally distributed colors (random colors if unchecked)</label>
<input type="checkbox" checked="checked" id="equidistant_cluster" onchange="setNewClusterFunction()">
<input type="button" onclick="generateClusters()" value="Regenerate image">
<br>
<select id="fun_select" onchange="setNewFunction()">
<option value="1">Every pixel moves to half the distance between it and the cluster</option>
<option value="2">Sets the pixels to exactly the cluster color (only the cluster colors)</option>
<option value="3">Move the pixels in the direction to the cluster (and maybe over it)</option>
</select>
<span id="dist_wrapper">
<label for="dist_in">Distance to move the pixels</label>
<input type="number" id="dist_in" onblur="updateConfig()" min="0">
</span>
<label for="cluster_in">Amount of clusters (essentially amount of colors, bigger number might crash)</label>
<input type="number" id="cluster_in" onblur="updateConfig()" min="0">
<br>
<label for=img_upload>Change image: (bigger images cause slowdown)</label>
<input type="file" onchange="previewFile()" id="img_upload"> <a id="download" onclick="downloadCanvas()">Download</a>
<br>
</div>
<img src="example.jpg" id="src_image">
<canvas id="src_canvas" style="display: none"></canvas>
<canvas id="final_canvas"></canvas>
<script type="module" src="js/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,273 @@
import {
docReady, getCoordinates, formatInterval, getIndexForCoordinate, downloadCanvasWithoutButton
} from "canvas-common";
var src_canvas;
var final_canvas;
var ctx;
var final_ctx;
var final_imageData;
var final_data;
var inputFields = {};
var input_data;
var config = {
size: {
width: 640,
height: 923
},
clusterFilter: {
dist: 120,
fun: halfWayDistance,
clusterFun: getEquidistantPoints,
clusterAmount: 8,
cubeSize: 255
}
};
var imageInfo;
var clusterCenter;
function renderImageAndGetData(img, targetContext) {
targetContext.drawImage(img, 0, 0);
var imgData = targetContext.getImageData(0, 0, config.size.width, config.size.height);
return imgData.data;
}
function resetImageInfo(data, copyClusterData) {
for (var i = 0; i < data.length; i += 4) {
var coor = getCoordinates(config, i);
var x = coor.x;
var y = coor.y;
imageInfo[y][x] = {
red: data[i],
green: data[i + 1],
blue: data[i + 2],
alpha: data[i + 3],
cluster: copyClusterData ? imageInfo[y][x].cluster : undefined
};
}
}
function createImageInfo() {
var img = document.getElementById('src_image');
config.size.width = img.clientWidth;
config.size.height = img.clientHeight;
if (config.size.width == 0) {
config.size.width = 500;
}
if (config.size.height == 0) {
config.size.height = 500;
}
src_canvas.width = config.size.width;
src_canvas.height = config.size.height;
final_canvas.width = config.size.width;
final_canvas.height = config.size.height;
imageInfo = new Array(config.size.height);
for (var i = 0; i < config.size.height; i++) {
imageInfo[i] = new Array(config.size.width);
}
// draw the image here, because resizing causes the context to loose its imageData (it seems)
input_data = renderImageAndGetData(img, ctx);
resetImageInfo(input_data, false);
}
function recreateClusterAndClusterDistance() {
var timeBefore = new Date();
clusterCenter = config.clusterFilter.clusterFun();
createImageInfo();
for (var y = 0; y < config.size.height; y++) {
for (var x = 0; x < config.size.width; x++) {
var colorInfoAtThisPos = imageInfo[y][x];
var minDist = Number.MAX_VALUE;
var foundCluster = -1;
for (var clusterIndex = 0; clusterIndex < config.clusterFilter.clusterAmount; clusterIndex++) {
var clusterPoint = clusterCenter[clusterIndex];
var dist = Math.sqrt(Math.pow(colorInfoAtThisPos.red - clusterPoint.x, 2) +
Math.pow(colorInfoAtThisPos.green - clusterPoint.y, 2) +
Math.pow(colorInfoAtThisPos.blue - clusterPoint.z, 2));
if (dist < minDist) {
minDist = dist;
foundCluster = clusterIndex;
}
}
colorInfoAtThisPos.cluster = {clusterIndex: foundCluster, dist: minDist};
}
}
var timeAfter = new Date();
formatInterval(timeBefore, timeAfter, "cluster distance and imageInfo creation took ...");
}
function halfWayDistance(colorInfoAtThisPos, unused) {
colorInfoAtThisPos.red = (colorInfoAtThisPos.red + clusterCenter[colorInfoAtThisPos.cluster.clusterIndex].x) / 2;
colorInfoAtThisPos.green = (colorInfoAtThisPos.green + clusterCenter[colorInfoAtThisPos.cluster.clusterIndex].y) / 2;
colorInfoAtThisPos.blue = (colorInfoAtThisPos.blue + clusterCenter[colorInfoAtThisPos.cluster.clusterIndex].z) / 2;
}
function exactlyCluster(colorInfoAtThisPos, unused) {
colorInfoAtThisPos.red = clusterCenter[colorInfoAtThisPos.cluster.clusterIndex].x;
colorInfoAtThisPos.green = clusterCenter[colorInfoAtThisPos.cluster.clusterIndex].y;
colorInfoAtThisPos.blue = clusterCenter[colorInfoAtThisPos.cluster.clusterIndex].z;
}
function addDistanceToPoint(colorInfoAtThisPos, dist) {
var vect = {
x: colorInfoAtThisPos.red - clusterCenter[colorInfoAtThisPos.cluster.clusterIndex].x,
y: colorInfoAtThisPos.green - clusterCenter[colorInfoAtThisPos.cluster.clusterIndex].y,
z: colorInfoAtThisPos.blue - clusterCenter[colorInfoAtThisPos.cluster.clusterIndex].z
};
vect.x /= dist;
vect.y /= dist;
vect.z /= dist;
colorInfoAtThisPos.red += vect.x * config.clusterFilter.dist;
colorInfoAtThisPos.green += vect.y * config.clusterFilter.dist;
colorInfoAtThisPos.blue += vect.z * config.clusterFilter.dist;
}
function getEquidistantPoints() {
var clusterCenter = [];
var increment = config.clusterFilter.cubeSize / Math.cbrt(config.clusterFilter.clusterAmount);
for (var x = 0; x < config.clusterFilter.cubeSize; x += increment) {
for (var y = 0; y < config.clusterFilter.cubeSize; y += increment) {
for (var z = 0; z < config.clusterFilter.cubeSize; z += increment) {
clusterCenter.push({x: x, y: y, z: z});
}
}
}
return clusterCenter;
}
function getRandomClusters() {
var clusterCenter = [];
for (var i = 0; i < config.clusterFilter.clusterAmount; i++) {
clusterCenter.push({
x: Math.random() * config.clusterFilter.cubeSize,
y: Math.random() * config.clusterFilter.cubeSize,
z: Math.random() * config.clusterFilter.cubeSize
});
}
return clusterCenter;
}
function applyClusterFilter() {
var timeBefore = new Date();
resetImageInfo(input_data, true);
final_imageData = final_ctx.createImageData(config.size.width, config.size.height);
final_data = final_imageData.data;
for (var y = 0; y < config.size.height; y++) {
for (var x = 0; x < config.size.width; x++) {
var colorInfoAtThisPos = imageInfo[y][x];
config.clusterFilter.fun(colorInfoAtThisPos, colorInfoAtThisPos.cluster.dist);
}
}
for (var y = 0; y < config.size.height; y++) {
for (var x = 0; x < config.size.width; x++) {
var colorInfoAtThisPos = imageInfo[y][x];
var index = getIndexForCoordinate(config, x, y);
final_data[index] = colorInfoAtThisPos.red;
final_data[index + 1] = colorInfoAtThisPos.green;
final_data[index + 2] = colorInfoAtThisPos.blue;
final_data[index + 3] = colorInfoAtThisPos.alpha;
}
}
final_ctx.putImageData(final_imageData, 0, 0);
var timeAfter = new Date();
formatInterval(timeBefore, timeAfter, "rendering took...");
}
docReady(function () {
src_canvas = document.getElementById('src_canvas');
final_canvas = document.getElementById('final_canvas');
ctx = src_canvas.getContext("2d");
final_ctx = final_canvas.getContext("2d");
inputFields['clusterAmount'] = document.getElementById('cluster_in');
inputFields['clusterAmount'].value = config.clusterFilter.clusterAmount;
inputFields['distance'] = document.getElementById('dist_in');
inputFields['distance'].value = config.clusterFilter.dist;
inputFields['distanceWrapper'] = document.getElementById('dist_wrapper');
inputFields['distanceWrapper'].display = 'none';
var newImg = new Image();
newImg.src = "example.jpg";
newImg.id = 'src_image';
newImg.onload = function () {
createImageInfo();
recreateClusterAndClusterDistance();
applyClusterFilter();
};
});
window.setNewFunction = setNewFunction;
window.setNewClusterFunction = setNewClusterFunction;
window.downloadCanvas = downloadCanvasClusterFilter;
window.previewFile = previewFile;
window.updateConfig = updateConfig;
window.generateClusters = generateClusters;
function downloadCanvasClusterFilter() {
downloadCanvasWithoutButton('clusterFilter', final_canvas);
}
function setNewFunction() {
var selectedWAy = document.getElementById('fun_select').value;
if (selectedWAy == 1) {
config.clusterFilter.fun = halfWayDistance;
inputFields['distanceWrapper'].display = 'none';
} else if (selectedWAy == 2) {
config.clusterFilter.fun = exactlyCluster;
inputFields['distanceWrapper'].display = 'none';
} else {
config.clusterFilter.fun = addDistanceToPoint;
inputFields['distanceWrapper'].display = 'none';
}
applyClusterFilter();
}
function setNewClusterFunction() {
if (document.getElementById('equidistant_cluster').checked) {
config.clusterFilter.clusterFun = getEquidistantPoints;
} else {
config.clusterFilter.clusterFun = getRandomClusters;
}
recreateClusterAndClusterDistance();
applyClusterFilter();
}
function generateClusters() {
recreateClusterAndClusterDistance();
applyClusterFilter();
}
function updateConfig() {
config.clusterFilter.dist = inputFields['distance'].value;
var newClusterAmount = inputFields['clusterAmount'].value;
if (config.clusterFilter.clusterAmount != newClusterAmount) {
config.clusterFilter.clusterAmount = newClusterAmount;
recreateClusterAndClusterDistance();
}
applyClusterFilter();
}
function previewFile() {
var preview = document.getElementById('src_image');
var file = document.getElementById('img_upload').files[0]; //sames as here
var reader = new FileReader();
reader.onloadend = function () {
preview.src = reader.result;
recreateClusterAndClusterDistance();
applyClusterFilter();
};
if (file) {
reader.readAsDataURL(file);
} else {
preview.src = "";
}
}

View File

@@ -0,0 +1,24 @@
// Avoid `console` errors in browsers that lack a console.
(function() {
var method;
var noop = function () {};
var methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
];
var length = methods.length;
var console = (window.console = window.console || {});
while (length--) {
method = methods[length];
// Only stub undefined methods.
if (!console[method]) {
console[method] = noop;
}
}
}());
// Place any jQuery/helper plugins in here.

View File

@@ -0,0 +1,9 @@
import { defineConfig } from 'vite'
export default defineConfig({
base: './',
root: 'src',
build: {
outDir: '../../dist/clusterFilter'
}
})

872
collatzConjecture/package-lock.json generated Normal file
View File

@@ -0,0 +1,872 @@
{
"name": "collatzConjecture",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "collatzConjecture",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
},
"../canvas-common": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
"integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
"integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
"integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
"integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
"integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
"integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
"integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
"integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
"integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
"integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
"integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
"integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
"integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
"integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
"integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
"integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
"integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
"integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
"integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
"integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true
},
"node_modules/canvas-common": {
"resolved": "../canvas-common",
"link": true
},
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rollup": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
"dev": true,
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.46.2",
"@rollup/rollup-android-arm64": "4.46.2",
"@rollup/rollup-darwin-arm64": "4.46.2",
"@rollup/rollup-darwin-x64": "4.46.2",
"@rollup/rollup-freebsd-arm64": "4.46.2",
"@rollup/rollup-freebsd-x64": "4.46.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
"@rollup/rollup-linux-arm-musleabihf": "4.46.2",
"@rollup/rollup-linux-arm64-gnu": "4.46.2",
"@rollup/rollup-linux-arm64-musl": "4.46.2",
"@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
"@rollup/rollup-linux-ppc64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-musl": "4.46.2",
"@rollup/rollup-linux-s390x-gnu": "4.46.2",
"@rollup/rollup-linux-x64-gnu": "4.46.2",
"@rollup/rollup-linux-x64-musl": "4.46.2",
"@rollup/rollup-win32-arm64-msvc": "4.46.2",
"@rollup/rollup-win32-ia32-msvc": "4.46.2",
"@rollup/rollup-win32-x64-msvc": "4.46.2",
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/vite": {
"version": "5.4.19",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"dev": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
{
"name": "collatzConjecture",
"version": "1.0.0",
"description": "",
"private": true,
"type": "module",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
}

View File

@@ -0,0 +1,60 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>collatzConjecture</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body, div, canvas {
margin: 0;
padding: 0;
}
html, body { width:100%; height:100%; }
canvas { display:block; }
/*.controls {
position: absolute;
top: 0px;
right: 10px
}*/
</style>
</head>
<body>
<label for="heightChange">Size of tree</label>
<input type="range" value="25" min="1" max="50" id="heightChange" oninput="setNewHeightChange(this.value)">
<label for="spread">Spread</label>
<input type="range" value="5" min="1" id="spread" oninput="setNewSpread(this.value)" max="30">
<label for="firstNumber">First number</label>
<input type="range" value="2" min="1" max="50" id="firstNumber" oninput="setNewFirstNumber(this.value)">
<label for="secondNumber">Second number</label>
<input type="range" value="3" min="1" id="secondNumber" oninput="setNewSecondNumber(this.value)" max="50">
<label for="size">Canvas Size:</label>
<input type="number" id="size" max="10000" onblur="updateCanvasConfig(this.value)">
<input type="button" value="reinitialize colors" onclick="generateColors(); updateCanvas()">
<br>
Explanation of first and second number:
The typical conjecture is in the form of: 3n + 1 with the property of dividing by two if odd etc.... (Explanation <a href="https://en.wikipedia.org/wiki/Collatz_conjecture">here</a>). <br> The 'firstNumber' is the 2 in the formula and the 'secondNumber' the 3.
Beware of ... interesting outcomes as this can cause the created tree to become very large... often too much for the browser.
<br>
<span>
Height:
<span id="heightValue"></span>
Spread:
<span id="spreadValue"></span>
Firstnumber:
<span id="firstValue"></span>
Secondnumber:
<span id="secondValue"></span>
</span>
<br>
<a id="download" onclick="downloadCanvasCommon()">Download</a>
<br>
<canvas id="canvas"></canvas>
<script type="module" src="js/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,242 @@
import {
docReady, d2h, toRad, downloadCanvasWithoutButton
} from "canvas-common";
var ctx = {};
var canvas = {};
var used = {};
var colors = [];
var collatzTree;
var heightChange;
var sizeInput;
var spreadInput;
var heightValue;
var spreadValue;
var firstValue;
var secondValue;
var startCol = {r: 0, g: 0, b: 0};
var finalCol = {r: 0, g: 0, b: 0};
var colorPoints = [];
function setToPinkishColors() {
colorPoints.push({r: 38, g: 17, b: 43});
colorPoints.push({r: 86, g: 37, b: 81});
colorPoints.push({r: 132, g: 60, b: 107});
colorPoints.push({r: 171, g: 88, b: 126});
colorPoints.push({r: 202, g: 122, b: 142});
colorPoints.push({r: 224, g: 159, b: 161});
colorPoints.push({r: 238, g: 198, b: 189});
colorPoints.push({r: 249, g: 237, b: 229});
finalCol = colorPoints[colorPoints.length - 1];
}
var config = {
size: {
size: 1500
},
conjectureConfig: {
firstNumber: 2,
secondNumber: 3,
maxDepth: 30,
heightChange: 25,
dirChange: 10
}
};
function buildStructure(input, depth) {
if (depth > config.conjectureConfig.maxDepth) return undefined;
if (input in used) {
return undefined
}
depth++;
used[input] = 1;
var element = {};
element.num = input;
var child1 = buildStructure(input * config.conjectureConfig.firstNumber, depth);
var child2 = undefined;
var inputSmaller = input - 1;
var probableNumber = (inputSmaller) / config.conjectureConfig.secondNumber;
if ((inputSmaller) % config.conjectureConfig.secondNumber == 0 && (probableNumber % config.conjectureConfig.firstNumber != 0)) {
child2 = buildStructure(probableNumber, depth);
}
element.child1 = child1;
element.child2 = child2;
return element;
}
function setNewHeightChange(newValue) {
config.conjectureConfig.heightChange = parseInt(newValue);
heightValue.innerText = config.conjectureConfig.heightChange;
updateCanvas();
}
function setNewSpread(newValue) {
config.conjectureConfig.dirChange = parseInt(newValue);
spreadValue.innerText = config.conjectureConfig.dirChange;
updateCanvas();
}
// TODO fix with different amount of colors
function drawElement(input, dir, parentPos, depth) {
if (depth >= config.conjectureConfig.maxDepth || input in used) return;
//console.log('__________________')
ctx.beginPath();
ctx.moveTo(parentPos.x, parentPos.y);
var numberOfStepsEachColor = Math.round(config.conjectureConfig.maxDepth / colorPoints.length);
// often... this returns a higher color, but the ratio is still in above 0.5, so the color changes drastically
var whereToMoveColorWise = (~~((depth) / config.conjectureConfig.maxDepth * 0.995 * colorPoints.length) + 1);
var ratio = (depth % (numberOfStepsEachColor)) / ((numberOfStepsEachColor));
// sometimes a color to high is chosen with a high ratio, this causes the color change to be wrong (and very drastic)
// we need to go a step back
if (ratio >= 1) {
whereToMoveColorWise--;
}
else if (ratio > 0.5) {
whereToMoveColorWise = (~~((depth - 1) / config.conjectureConfig.maxDepth * 0.995 * colorPoints.length) + 1);
}
var targetColor = colorPoints[whereToMoveColorWise];
if (whereToMoveColorWise == colorPoints.length) {
targetColor = finalCol;
}
var whereWeComeColorWise = whereToMoveColorWise - 1;
//console.log(whereWeComeColorWise + '_' + whereToMoveColorWise)
//console.log(depth + '_' + numberOfStepsEachColor)
//console.log(ratio + '_' + depth / config.conjectureConfig.maxDepth)
var baseColor = colorPoints[whereWeComeColorWise];
var newColor = {
r: targetColor.r * ratio + baseColor.r * (1 - ratio),
g: targetColor.g * ratio + baseColor.g * (1 - ratio),
b: targetColor.b * ratio + baseColor.b * (1 - ratio)
};
ctx.strokeStyle = '#' + d2h(~~newColor.r) + d2h(~~newColor.g) + d2h(~~newColor.b);
var newXOffset = config.conjectureConfig.heightChange * Math.cos(toRad(dir));
//console.log(ctx.strokeStyle)
//console.log(newColor)
//console.log(baseColor)
//console.log(targetColor)
var newYOffset = config.conjectureConfig.heightChange * Math.sin(toRad(dir));
//ctx.fillText((ctx.strokeStyle) + ' ', parentPos.x, parentPos.y);
ctx.lineTo(parentPos.x + newXOffset, parentPos.y + newYOffset);
ctx.stroke();
// experiment to use two parallel lines represented in the video, doesn't work that well, because the line can cross the other line and then... stuff happens
// also lines overlay each other
//ctx.strokeStyle = colors[depth + 1];
//ctx.moveTo(parentPos.x - Math.cos(toRad(90 - dir)) * config.width / 2, parentPos.y - Math.sin(toRad(90 - dir)) * config.width / 2);
//ctx.lineTo(parentPos.x + config.heightChange * Math.cos(toRad(dir)) - Math.cos(toRad(90 - dir)) * config.width / 2,
// parentPos.y + config.heightChange * Math.sin(toRad(dir)) - Math.sin(toRad(90 - dir)) * config.width / 2);
//
//ctx.moveTo(parentPos.x + Math.cos(toRad(90 - dir)) * config.width / 2, parentPos.y + Math.sin(toRad(90 - dir)) * config.width / 2);
//ctx.lineTo(parentPos.x + config.heightChange * Math.cos(toRad(dir)) + Math.cos(toRad(90 - dir)) * config.width / 2,
// parentPos.y + config.heightChange * Math.sin(toRad(dir)) + Math.sin(toRad(90 - dir)) * config.width / 2);
//ctx.stroke();
var newParentPos = {
x: parentPos.x + newXOffset,
y: parentPos.y + newYOffset
};
depth++;
used[input] = 1;
drawElement(input * config.conjectureConfig.firstNumber, dir + config.conjectureConfig.dirChange, newParentPos, depth);
var inputSmaller = input - 1;
var probableNumber = (inputSmaller) / config.conjectureConfig.secondNumber;
if ((inputSmaller) % config.conjectureConfig.secondNumber == 0 && (probableNumber % config.conjectureConfig.firstNumber != 0)) {
drawElement(probableNumber, dir - config.conjectureConfig.dirChange, newParentPos, depth);
}
else {
ctx.stroke();
}
}
function generateColors() {
colors = [];
colorPoints = [];
colorPoints.push(startCol);
var maxInc = (255 / config.conjectureConfig.maxDepth);
for (var i = 0; i < 3; i++) {
var red = ~~(Math.random() * 255);
var green = ~~(Math.random() * 255);
var blue = ~~(Math.random() * 255);
//colors.push('#' + (red).toString(16) + (green).toString(16) + (blue).toString(16));
colorPoints.push({r: red, g: green, b: blue});
}
}
function updateCanvasConfig(newValue) {
config.size.size = parseInt(newValue);
initCanvas();
updateCanvas();
}
function updateCanvas() {
used = {};
ctx.clearRect(0, 0, config.size.size, config.size.size);
drawElement(1, -180, config.size, 0);
}
function setNewFirstNumber(newVal) {
config.conjectureConfig.firstNumber = parseInt(newVal);
firstValue.innerText = config.conjectureConfig.firstNumber;
updateCanvas();
}
function setNewSecondNumber(newVal) {
config.conjectureConfig.secondNumber = parseInt(newVal);
secondValue.innerText = config.conjectureConfig.secondNumber;
updateCanvas();
}
function initCanvas() {
ctx = canvas.getContext("2d");
canvas.height = config.size.size;
canvas.width = config.size.size;
config.size.x = config.size.size / 2;
config.size.y = config.size.size / 2;
}
docReady(function () {
canvas = document.getElementById('canvas');
initCanvas();
ctx.font = "8px Verdana";
ctx.lineWidth = 1;
sizeInput = document.getElementById('size');
heightChange = document.getElementById('heightChange');
spreadInput = document.getElementById('spread');
heightValue = document.getElementById('heightValue');
heightValue.innerText = config.conjectureConfig.heightChange;
spreadValue = document.getElementById('spreadValue');
spreadValue.innerText = config.conjectureConfig.dirChange;
firstValue = document.getElementById('firstValue');
firstValue.innerText = config.conjectureConfig.firstNumber;
secondValue = document.getElementById('secondValue');
secondValue.innerText = config.conjectureConfig.secondNumber;
sizeInput.value = config.size.size;
spreadInput.value =config.conjectureConfig.dirChange;
heightChange.value = config.conjectureConfig.heightChange;
setToPinkishColors();
//generateColors();
drawElement(1, -180, config.size, 0);
//initStructure();
});
function downloadCanvasCollatz() {
downloadCanvasWithoutButton('collatzConjecture', canvas);
}
window.setNewHeightChange = setNewHeightChange;
window.setNewSpread = setNewSpread;
window.setNewFirstNumber = setNewFirstNumber;
window.setNewSecondNumber = setNewSecondNumber;
window.updateCanvasConfig = updateCanvasConfig;
window.generateColors = generateColors;
window.updateCanvas = updateCanvas;
window.downloadCanvasCommon = downloadCanvasCollatz;

View File

@@ -0,0 +1,24 @@
// Avoid `console` errors in browsers that lack a console.
(function() {
var method;
var noop = function () {};
var methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
];
var length = methods.length;
var console = (window.console = window.console || {});
while (length--) {
method = methods[length];
// Only stub undefined methods.
if (!console[method]) {
console[method] = noop;
}
}
}());
// Place any jQuery/helper plugins in here.

View File

@@ -0,0 +1,9 @@
import { defineConfig } from 'vite'
export default defineConfig({
base: './',
root: 'src',
build: {
outDir: '../../dist/collatzConjecture'
}
})

872
dotLines/package-lock.json generated Normal file
View File

@@ -0,0 +1,872 @@
{
"name": "dotLines",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dotLines",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
},
"../canvas-common": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
"integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
"integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
"integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
"integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
"integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
"integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
"integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
"integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
"integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
"integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
"integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
"integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
"integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
"integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
"integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
"integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
"integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
"integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
"integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
"integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true
},
"node_modules/canvas-common": {
"resolved": "../canvas-common",
"link": true
},
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rollup": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
"dev": true,
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.46.2",
"@rollup/rollup-android-arm64": "4.46.2",
"@rollup/rollup-darwin-arm64": "4.46.2",
"@rollup/rollup-darwin-x64": "4.46.2",
"@rollup/rollup-freebsd-arm64": "4.46.2",
"@rollup/rollup-freebsd-x64": "4.46.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
"@rollup/rollup-linux-arm-musleabihf": "4.46.2",
"@rollup/rollup-linux-arm64-gnu": "4.46.2",
"@rollup/rollup-linux-arm64-musl": "4.46.2",
"@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
"@rollup/rollup-linux-ppc64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-gnu": "4.46.2",
"@rollup/rollup-linux-riscv64-musl": "4.46.2",
"@rollup/rollup-linux-s390x-gnu": "4.46.2",
"@rollup/rollup-linux-x64-gnu": "4.46.2",
"@rollup/rollup-linux-x64-musl": "4.46.2",
"@rollup/rollup-win32-arm64-msvc": "4.46.2",
"@rollup/rollup-win32-ia32-msvc": "4.46.2",
"@rollup/rollup-win32-x64-msvc": "4.46.2",
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/vite": {
"version": "5.4.19",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"dev": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
}
}

19
dotLines/package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "dotLines",
"version": "1.0.0",
"description": "",
"private": true,
"type": "module",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"dependencies": {
"canvas-common": "file:../canvas-common"
},
"devDependencies": {
"vite": "^5.1.5"
}
}

47
dotLines/src/index.html Normal file
View File

@@ -0,0 +1,47 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>dotLines</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body, div, canvas {
margin: 0;
padding: 0;
}
html, body { width:100%; height:100%; }
canvas { display:block; }
#pauseBtn {
left: 0px;
}
#showAllBtn {
left: 60px;
}
#download {
left: 150px;
top: 3px;
}
.controls {
position: absolute;
top: 0px;
right: 10px
}
</style>
</head>
<body>
<div id="content">
<canvas id="canvas" ></canvas>
<div class="controls">
<input type="button" class="top" id="pauseBtn" value="pause" onclick="pause()" />
<input type="button" class="top" id="showAllBtn" value="show all" onclick="showAll()" />
<a id="download" class="top" onclick="downloadCanvasCommon()">Download</a> <br>
</div>
</div>
<script type="module" src="js/main.js"></script>
</body>
</html>

176
dotLines/src/js/main.js Normal file
View File

@@ -0,0 +1,176 @@
import {
docReady, downloadCanvasWithoutButton, getMousePos, pointDistance, roundedRandom
} from "canvas-common";
var canvas;
var ctx;
var drawn = {};
var mousePos = {};
var animationId;
var config = {
size: {
height: window.innerHeight,
width: window.innerWidth
},
dotLines: {
dotAmount: (window.innerHeight * window.innerWidth) / 4000,
boxWidth: 50,
boxHeight: 50,
maxLinks: 10,
mouseRange: 100,
numberOfIncrements: 50,
paused: false,
showAll: false,
minOpacity: 0.1,
fps: 30,
linkRange: 100
}
};
var currentNum = 0;
var dots = [];
function createDots() {
for (var i = 0; i < config.dotLines.dotAmount; i++) {
var dot = {};
dot.x = roundedRandom(config.size.width);
dot.y = roundedRandom(config.size.height);
dot.origin = {x: dot.x, y: dot.y};
createTarget(dot);
startDotMovement(dot);
dot.num = currentNum++;
dot.links = [];
dots.push(dot);
}
}
function createLines() {
dots.forEach(function (linkingDot) {
dots.forEach(function (linkedDot) {
if (linkingDot.links.length >= config.dotLines.maxLinks || linkedDot.links.length >= config.dotLines.maxLinks) return;
var dist = pointDistance(linkingDot, linkedDot);
if (dist < config.dotLines.linkRange && linkedDot.links.indexOf(linkingDot) == -1) {
linkingDot.links.push(linkedDot);
}
})
})
}
function renderDot(dotToRender) {
if (dotToRender.num in drawn) return;
var mouseDistance = pointDistance(dotToRender, mousePos);
if ((mouseDistance > config.dotLines.mouseRange || mouseDistance == undefined || isNaN(parseInt(mouseDistance))) && !config.dotLines.showAll) return;
drawn[dotToRender.num] = 1;
ctx.beginPath();
ctx.moveTo(dotToRender.x, dotToRender.y);
var alpha = Math.max(config.dotLines.minOpacity, 1 - (mouseDistance / config.dotLines.mouseRange));
// in case we want to show everything with full opacity
//var alpha = Math.max(!config.dotLines.showAll ? config.dotLines.minOpacity : 1, 1 - (mouseDistance / config.dotLines.mouseRange));
ctx.fillStyle = 'rgba(255, 0, 0, ' + alpha + ')';
ctx.arc(dotToRender.x, dotToRender.y, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.strokeStyle = 'rgba(255, 0, 0, ' + alpha + ')';
dotToRender.links.forEach(function (tar) {
ctx.moveTo(dotToRender.x, dotToRender.y);
ctx.lineTo(tar.x, tar.y);
});
ctx.stroke();
dotToRender.links.forEach(renderDot)
}
function createTarget(dot) {
dot.target = {
x: dot.origin.x - config.dotLines.boxWidth / 2 + roundedRandom(config.dotLines.boxWidth),
y: dot.origin.y - config.dotLines.boxHeight / 2 + roundedRandom(config.dotLines.boxHeight)
};
}
function startDotMovement(dot) {
dot.targetReached = false;
createTarget(dot);
var dist = pointDistance(dot, dot.target);
var vect = {
x: dot.target.x - dot.x,
y: dot.target.y - dot.y
};
vect.x /= dist;
vect.y /= dist;
dot.distRemaining = dist;
dot.vect = vect;
}
function act(dot) {
if (dot.targetReached) {
startDotMovement(dot);
dot.x += dot.vect.x * (dot.distRemaining / config.dotLines.numberOfIncrements);
dot.y += dot.vect.y * (dot.distRemaining / config.dotLines.numberOfIncrements);
} else {
dot.x += dot.vect.x * (dot.distRemaining / config.dotLines.numberOfIncrements);
dot.y += dot.vect.y * (dot.distRemaining / config.dotLines.numberOfIncrements);
if (pointDistance(dot, dot.target) < 10) {
dot.targetReached = true;
}
}
}
function renderDots() {
drawn = {};
ctx.clearRect(0, 0, config.size.width, config.size.height);
dots.forEach(renderDot);
dots.forEach(act);
setTimeout(function () {
if(!config.dotLines.paused) {
animationId = requestAnimationFrame(renderDots);
}
}, 1000 / config.dotLines.fps)
}
function setMousePos(e) {
mousePos = getMousePos(canvas, e);
}
function pause() {
config.dotLines.paused = !config.dotLines.paused;
if (!config.dotLines.paused) {
animationId = requestAnimationFrame(renderDots);
} else {
cancelAnimationFrame(animationId);
}
}
function showAll() {
config.dotLines.showAll = !config.dotLines.showAll;
}
docReady(function () {
canvas = document.getElementById('canvas');
canvas.width = config.size.width;
canvas.height = config.size.height;
canvas.onmousemove = setMousePos;
ctx = canvas.getContext("2d");
createDots();
createLines();
requestAnimationFrame(renderDots);
});
function downloadCanvasDotlines() {
downloadCanvasWithoutButton('dotLines', canvas);
}
window.downloadCanvasCommon = downloadCanvasDotlines;
window.pause = pause;
window.showAll = showAll;

View File

@@ -0,0 +1,24 @@
// Avoid `console` errors in browsers that lack a console.
(function() {
var method;
var noop = function () {};
var methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
];
var length = methods.length;
var console = (window.console = window.console || {});
while (length--) {
method = methods[length];
// Only stub undefined methods.
if (!console[method]) {
console[method] = noop;
}
}
}());
// Place any jQuery/helper plugins in here.

9
dotLines/vite.config.js Normal file
View File

@@ -0,0 +1,9 @@
import { defineConfig } from 'vite'
export default defineConfig({
base: './',
root: 'src',
build: {
outDir: '../../dist/dotLines'
}
})

BIN
img/bubbles.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
img/circleBs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
img/clusterFilter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
img/collatzConjecture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
img/dotLines.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
img/survivors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -13,5 +13,11 @@
<a href="/recBubbles"><img src="img/recBubbles.png" alt="Recursive bubbles" class="preview" title="Recursive bubbles"></a> <br>
<a href="/fireWorks"><img src="img/fireWorks.png" alt="Fireworks" class="preview" title="Fireworks"></a>
<a href="/balls"><img src="img/balls.png" alt="Balls" class="preview" title="Balls"></a>
<a href="/bubbles"><img src="img/bubbles.png" alt="Bubbles" class="preview" title="Bubbles"></a>
<a href="/circleBs"><img src="img/circleBs.png" alt="circleBs" class="preview" title="circleBs"></a>
<a href="/clusterFilter"><img src="img/clusterFilter.png" alt="clusterFilter" class="preview" title="clusterFilter"></a>
<a href="/collatzConjecture"><img src="img/collatzConjecture.png" alt="collatzConjecture" class="preview" title="collatzConjecture"></a>
<a href="/dotLines"><img src="img/dotLines.png" alt="dotLines" class="preview" title="dotLines"></a>
<a href="/survivors"><img src="img/survivors.png" alt="survivors" class="preview" title="survivors"></a>
</body>
</html>