Merge branch 'main' of https://github.com/Bubobubobubobubo/Topos
3
.github/workflows/deploy.yml
vendored
@ -47,9 +47,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: "main"
|
path: "main"
|
||||||
|
|
||||||
- name: Copy favicon folder
|
|
||||||
run: cp -r main/favicon ./dist/
|
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
53
README.md
@ -19,52 +19,53 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Topos is a web based live coding environment. Topos is capable of many things:
|
Topos is a web based live coding environment designed to be installation-free, independant and fun. Topos is loosely based on the [Monome Teletype](https://monome.org/docs/teletype/). The application follows the same operating principle, but adapts it to the rich multimedia context offered by web browsers. Topos is capable of many things:
|
||||||
|
|
||||||
- it is a music sequencer made for improvisation and composition alike
|
- it is a generative/algorithmic music sequencer made for **improvisation** and **composition** alike
|
||||||
- it is a synthesizer capable of additive, substractive, FM and wavetable
|
- it is a synthesizer capable of _additive_, _substractive_, _FM_ and _wavetable
|
||||||
synthesis, backed up by a [powerful web based audio engine](https://www.npmjs.com/package/superdough)
|
synthesis_, backed up by a [powerful web based audio engine](https://www.npmjs.com/package/superdough)
|
||||||
- it can also generate video thanks to [Hydra](https://hydra.ojack.xyz/) and
|
- it can also generate video thanks to [Hydra](https://hydra.ojack.xyz/),
|
||||||
custom oscilloscopes, frequency visualizers and image sequencing capabilities
|
oscilloscopes, frequency visualizers and image/canvas sequencing capabilities
|
||||||
- it can be used to sequence other MIDI devices (and soon.. OSC!)
|
- it can be used to sequence other MIDI and OSC devices (the latter using a **NodeJS** script)
|
||||||
- it is made to be used without the need of installing anything, always ready at
|
- it is made to be used without the need of installing anything, always ready at
|
||||||
[https://topos.live](https://topos.live)
|
[https://topos.live](https://topos.live)
|
||||||
- Topos is also an emulation and personal extension of the [Monome Teletype](https://monome.org/docs/teletype/)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
**Topos** is still a young project developed by two hobbyists :) Contributions are welcome! We wish to be as inclusive and welcoming as possible to your ideas and suggestions! The software is working quite well and we are continuously striving to improve it.
|
**Topos** is still a young and experimental project developed by two hobbyists :) Contributions are welcome! We wish to be as inclusive and welcoming as possible to your ideas and suggestions! The software is working quite well and we are continuously striving to improve it. Note that most features are rather experimental and that we don't really have any classical training in web development.
|
||||||
|
|
||||||
## Installation (for devs and contributors)
|
## Local Installation (for devs and contributors)
|
||||||
|
|
||||||
To run the application, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
|
To run the application, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
|
||||||
|
|
||||||
- `yarn install`
|
- `yarn install`
|
||||||
- `yarn run dev`
|
- `yarn run dev`
|
||||||
|
|
||||||
To build the application for production, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
|
You are good to go. The application will update itself automatically with every change to the codebase. To test the production version of the applicationn, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
|
||||||
|
|
||||||
- `yarn run build`
|
- `yarn run build`
|
||||||
- `yarn run start`
|
- `yarn run start`
|
||||||
|
|
||||||
Always run a build before committing to check for compiler errors. The automatic deployment on the `main` branch will not accept compiler errors!
|
If the build passes, you can be sure that it will also pass our **CI** pipeline that deploys the application to [https://topos.live](https://topos.live). Always run a build before committing to check for compiler errors. The automatic deployment on the `main` branch will not accept compiler errors!
|
||||||
|
|
||||||
To build a standalone browser application using [Tauri](https://tauri.app/), you will need to have [Node.js](https://nodejs.org/en/), [Yarn](https://yarnpkg.com/en/) and [Rust](https://www.rust-lang.org/) installed. Then, clone the repository and run:
|
## Tauri version
|
||||||
|
|
||||||
|
Topos can also be compiled as a standalone application using [Tauri](https://tauri.app/). You will need [Node.js](https://nodejs.org/en/), [Yarn](https://yarnpkg.com/en/) and [Rust](https://www.rust-lang.org/) to be installed on your computer. Then, clone the repository and run:
|
||||||
|
|
||||||
- `yarn tauri build`
|
- `yarn tauri build`
|
||||||
- `yarn tauri dev`
|
- `yarn tauri dev`
|
||||||
|
|
||||||
The `tauri` version is only here to quickstart future developments but nothing has been done yet.
|
The `tauri` version has never been fleshed out. It's a template for later developments if Topos ever wants to escape from the web :)
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
### Run the application
|
To run the **Docker** version, run the following command:
|
||||||
|
|
||||||
`docker run -p 8001:80 yassinsiouda/topos:latest`
|
`docker run -p 8001:80 bubobubobubo/topos:latest`
|
||||||
|
|
||||||
### Build and run the prod image
|
### Build and run the prod image
|
||||||
|
|
||||||
@ -72,8 +73,7 @@ The `tauri` version is only here to quickstart future developments but nothing
|
|||||||
|
|
||||||
### Build and run the dev image
|
### Build and run the dev image
|
||||||
|
|
||||||
**First installation**
|
First you need to map `node_modules` to your local machine for your IDE IntelliSense to work properly :
|
||||||
First you need to map node_modules to your local machine for your ide intellisense to work properly
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose --profile dev up -d
|
docker compose --profile dev up -d
|
||||||
@ -81,8 +81,21 @@ docker cp topos-dev:/app/node_modules .
|
|||||||
docker compose --profile dev down
|
docker compose --profile dev down
|
||||||
```
|
```
|
||||||
|
|
||||||
**Then**
|
then run the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose --profile dev up
|
docker compose --profile dev up
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that a Docker version of Topos is automatically generated everytime a commit is done on the `main` branch.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- Felix Roos for the [SuperDough](https://www.npmjs.com/package/superdough) audio engine.
|
||||||
|
- Frank Force for the [ZzFX](https://github.com/KilledByAPixel/ZzFX) synthesizer.
|
||||||
|
- Kristoffer Ekstrand for the [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) waveforms.
|
||||||
|
- Ryan Kirkbride for some of the audio samples in the [Dough-Fox](https://github.com/Bubobubobubobubo/Dough-Fox) sample pack, taken from [here](https://github.com/Qirky/FoxDot/tree/master/FoxDot/snd).
|
||||||
|
- Adel Faure for the [JGS](https://adelfaure.net/https://adelfaure.net/) font.
|
||||||
|
- Raphaël Bastide for the [Steps Mono](https://github.com/raphaelbastide/steps-mono/) font.
|
||||||
|
|
||||||
|
Many thanks to all the contributors and folks who tried the software already :)
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "",
|
|
||||||
"short_name": "",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/android-chrome-192x192.png",
|
|
||||||
"sizes": "192x192",
|
|
||||||
"type": "image/png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "/android-chrome-512x512.png",
|
|
||||||
"sizes": "512x512",
|
|
||||||
"type": "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"theme_color": "#ffffff",
|
|
||||||
"background_color": "#ffffff",
|
|
||||||
"display": "standalone"
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 590 KiB |
11
index.html
@ -1,13 +1,14 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="description" content="Topos is a live coding environment inspired by the Monome Teletype.">
|
|
||||||
<title>Topos</title>
|
<title>Topos</title>
|
||||||
|
<meta name="description" content="Topos is a live coding environment inspired by the Monome Teletype.">
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="favicon/apple-touch-icon.png">
|
<link rel="icon" href="/favicon/favicon.ico" sizes="48x48" ><!-- REVISED (Aug 11, 2023)! -->
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="favicon/favicon-32x32.png">
|
<link rel="icon" href="/favicon/favicon.svg" sizes="any" type="image/svg+xml"><!-- REVISED (Aug 11, 2023)! -->
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="favicon/favicon-16x16.png">
|
<link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png"/>
|
||||||
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="mask-icon" href="favicon/safari-pinned-tab.svg" color="#5bbad5">
|
<link rel="mask-icon" href="favicon/safari-pinned-tab.svg" color="#5bbad5">
|
||||||
<meta name="msapplication-TileColor" content="#da532c">
|
<meta name="msapplication-TileColor" content="#da532c">
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Topos",
|
|
||||||
"short_name": "Topos",
|
|
||||||
"description": "Live coding environment",
|
|
||||||
"theme_color": "#ffffff",
|
|
||||||
"background_color": "#ffffff",
|
|
||||||
"display": "standalone",
|
|
||||||
"scope": "/",
|
|
||||||
"start_url": "/",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "./favicon/android-chrome-192x192.png",
|
|
||||||
"sizes": "192x192",
|
|
||||||
"type": "image/png",
|
|
||||||
"purpose": "any maskable"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./favicon/android-chrome-512x512.png",
|
|
||||||
"sizes": "512x512",
|
|
||||||
"type": "image/png",
|
|
||||||
"purpose": "any maskable"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.4.5",
|
"vite": "^4.4.5",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-pwa": "^0.16.7"
|
"vite-plugin-pwa": "^0.17.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-javascript": "^6.1.9",
|
"@codemirror/lang-javascript": "^6.1.9",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
46
public/favicon/favicon.svg
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="48.000000pt" height="48.000000pt" viewBox="0 0 48.000000 48.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
|
||||||
|
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M163 470 c-13 -5 -23 -12 -23 -15 0 -3 46 -5 102 -5 80 0 99 3 90 12
|
||||||
|
-15 15 -139 21 -169 8z"/>
|
||||||
|
<path d="M91 391 c-16 -16 -19 -32 -18 -84 1 -53 -2 -69 -18 -87 -15 -16 -20
|
||||||
|
-38 -22 -83 0 -33 2 -56 5 -50 9 13 44 13 39 -1 -2 -6 -18 -11 -34 -11 -39 0
|
||||||
|
-50 -10 -33 -30 8 -10 30 -15 60 -15 26 0 64 -7 83 -15 43 -18 125 -20 164 -3
|
||||||
|
15 6 57 14 93 17 73 7 87 24 51 60 -12 12 -21 17 -21 13 0 -4 5 -13 12 -20 8
|
||||||
|
-8 8 -12 1 -12 -18 0 -25 34 -9 45 10 7 12 16 6 25 -5 8 -7 24 -4 35 3 13 -2
|
||||||
|
28 -15 39 -12 11 -21 27 -21 36 0 9 -5 21 -11 27 -8 8 -8 17 0 31 17 32 13 56
|
||||||
|
-12 80 -31 29 -63 28 -91 -3 -16 -17 -34 -25 -56 -25 -22 0 -40 8 -56 25 -28
|
||||||
|
30 -67 32 -93 6z m89 -16 c19 -23 5 -29 -24 -10 -16 10 -33 14 -47 10 -14 -5
|
||||||
|
-19 -4 -15 4 10 16 72 13 86 -4z m211 -7 c12 -22 11 -22 -8 -5 -25 21 -41 22
|
||||||
|
-65 0 -22 -19 -36 -10 -18 12 20 24 77 19 91 -7z m-264 -30 c-3 -7 -5 -2 -5
|
||||||
|
12 0 14 2 19 5 13 2 -7 2 -19 0 -25z m229 -5 c-11 -11 -19 6 -11 24 8 17 8 17
|
||||||
|
12 0 3 -10 2 -21 -1 -24z m-80 -8 c4 -8 10 -12 15 -9 5 3 9 0 9 -6 0 -14 60
|
||||||
|
-40 77 -33 7 3 13 -2 13 -11 0 -13 -6 -15 -27 -9 -36 9 -210 9 -245 0 -22 -6
|
||||||
|
-28 -4 -28 9 0 8 6 14 13 11 6 -2 25 2 40 10 15 8 32 11 37 8 6 -4 7 1 3 11
|
||||||
|
-4 12 -3 15 5 10 7 -4 15 -1 18 8 8 21 63 21 70 1z m83 -91 c10 -9 -37 -33
|
||||||
|
-77 -39 -55 -8 -117 2 -155 26 l-30 18 54 4 c56 4 201 -2 208 -9z m-286 -30
|
||||||
|
c-15 -7 21 -44 42 -44 8 0 15 -4 15 -10 0 -16 -33 -11 -59 9 -25 19 -24 52 1
|
||||||
|
50 9 0 10 -2 1 -5z m355 -21 c2 -14 -4 -23 -17 -28 -12 -3 -21 -13 -21 -21 0
|
||||||
|
-19 -16 -18 -24 1 -5 15 -31 16 -53 2 -8 -5 -13 -4 -13 2 0 6 5 12 10 12 6 1
|
||||||
|
15 3 20 4 6 1 18 3 28 4 25 1 66 37 52 44 -6 3 -5 4 2 3 7 -1 14 -12 16 -23z
|
||||||
|
m-255 -37 c4 -10 1 -13 -9 -9 -7 3 -14 9 -14 14 0 14 17 10 23 -5z m42 -6 c3
|
||||||
|
-5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m35 0 c0
|
||||||
|
-5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m-170
|
||||||
|
-10 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z
|
||||||
|
m215 -39 c-6 -5 -25 10 -25 20 0 5 6 4 14 -3 8 -7 12 -15 11 -17z m45 8 c0
|
||||||
|
-14 -16 -11 -29 5 -10 12 -8 13 8 9 12 -3 21 -9 21 -14z m-220 1 c0 -5 -5 -10
|
||||||
|
-11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m40 0 c0 -5 -4 -10
|
||||||
|
-10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m215 0 c3 -5 2
|
||||||
|
-10 -4 -10 -5 0 -13 5 -16 10 -3 6 -2 10 4 10 5 0 13 -4 16 -10z m43 -10 c7
|
||||||
|
-11 10 -20 6 -20 -7 0 -34 27 -34 34 0 13 16 5 28 -14z m-160 -17 c-10 -2 -26
|
||||||
|
-2 -35 0 -10 3 -2 5 17 5 19 0 27 -2 18 -5z m99 1 c-3 -3 -12 -4 -19 -1 -8 3
|
||||||
|
-5 6 6 6 11 1 17 -2 13 -5z m40 0 c-3 -3 -12 -4 -19 -1 -8 3 -5 6 6 6 11 1 17
|
||||||
|
-2 13 -5z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 603 B |
BIN
public/favicon/screenshot_miniature.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/favicon/topos_code.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
37
public/manifest.webmanifest
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"name": "Topos",
|
||||||
|
"short_name": "Topos",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon/android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "favicon/android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"display": "standalone",
|
||||||
|
"start_url": "/",
|
||||||
|
"scope": "/",
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"description": "Topos is a web based live coding platform",
|
||||||
|
"screenshots": [
|
||||||
|
{
|
||||||
|
"src": "favicon/screenshot_miniature.png",
|
||||||
|
"sizes": "640x320",
|
||||||
|
"type": "image/gif",
|
||||||
|
"form_factor": "wide",
|
||||||
|
"label": "Topos application"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "favicon/topos_code.png",
|
||||||
|
"sizes": "1280x768",
|
||||||
|
"type": "image/gif",
|
||||||
|
"label": "Topos code"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
329
src/API.ts
@ -121,7 +121,7 @@ export class UserAPI {
|
|||||||
public currentSeed: string | undefined = undefined;
|
public currentSeed: string | undefined = undefined;
|
||||||
public localSeeds = new Map<string, Function>();
|
public localSeeds = new Map<string, Function>();
|
||||||
public patternCache = new LRUCache({ max: 10000, ttl: 10000 * 60 * 5 });
|
public patternCache = new LRUCache({ max: 10000, ttl: 10000 * 60 * 5 });
|
||||||
public invalidPatterns: {[key: string]: boolean} = {};
|
public invalidPatterns: { [key: string]: boolean } = {};
|
||||||
public cueTimes: { [key: string]: number } = {};
|
public cueTimes: { [key: string]: number } = {};
|
||||||
private errorTimeoutID: number = 0;
|
private errorTimeoutID: number = 0;
|
||||||
private printTimeoutID: number = 0;
|
private printTimeoutID: number = 0;
|
||||||
@ -757,15 +757,15 @@ export class UserAPI {
|
|||||||
this.patternCache.delete(id);
|
this.patternCache.delete(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
maybeToNumber = (something: any): number|any => {
|
maybeToNumber = (something: any): number | any => {
|
||||||
// If something is BigInt
|
// If something is BigInt
|
||||||
if(typeof something === "bigint") {
|
if (typeof something === "bigint") {
|
||||||
return Number(something);
|
return Number(something);
|
||||||
} else {
|
} else {
|
||||||
return something;
|
return something;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cache = (key: string, value: any) => {
|
cache = (key: string, value: any) => {
|
||||||
/**
|
/**
|
||||||
* Gets or sets a value in the cache.
|
* Gets or sets a value in the cache.
|
||||||
@ -774,40 +774,40 @@ export class UserAPI {
|
|||||||
* @param value - The value to set
|
* @param value - The value to set
|
||||||
* @returns The value of the key
|
* @returns The value of the key
|
||||||
*/
|
*/
|
||||||
if(value !== undefined) {
|
if (value !== undefined) {
|
||||||
if(isGenerator(value)) {
|
if (isGenerator(value)) {
|
||||||
if(this.patternCache.has(key)) {
|
if (this.patternCache.has(key)) {
|
||||||
const cachedValue = (this.patternCache.get(key) as Generator<any>).next().value
|
const cachedValue = (this.patternCache.get(key) as Generator<any>).next().value
|
||||||
if(cachedValue!==0 && !cachedValue) {
|
if (cachedValue !== 0 && !cachedValue) {
|
||||||
const generator = value as unknown as Generator<any>
|
|
||||||
this.patternCache.set(key, generator);
|
|
||||||
return this.maybeToNumber(generator.next().value);
|
|
||||||
}
|
|
||||||
return this.maybeToNumber(cachedValue);
|
|
||||||
} else {
|
|
||||||
const generator = value as unknown as Generator<any>
|
const generator = value as unknown as Generator<any>
|
||||||
this.patternCache.set(key, generator);
|
this.patternCache.set(key, generator);
|
||||||
return this.maybeToNumber(generator.next().value);
|
return this.maybeToNumber(generator.next().value);
|
||||||
}
|
}
|
||||||
} else if(isGeneratorFunction(value)) {
|
return this.maybeToNumber(cachedValue);
|
||||||
if(this.patternCache.has(key)) {
|
} else {
|
||||||
const cachedValue = (this.patternCache.get(key) as Generator<any>).next().value;
|
const generator = value as unknown as Generator<any>
|
||||||
if(cachedValue || cachedValue===0 || cachedValue===0n) {
|
this.patternCache.set(key, generator);
|
||||||
return this.maybeToNumber(cachedValue);
|
return this.maybeToNumber(generator.next().value);
|
||||||
} else {
|
}
|
||||||
const generator = value();
|
} else if (isGeneratorFunction(value)) {
|
||||||
this.patternCache.set(key, generator);
|
if (this.patternCache.has(key)) {
|
||||||
return this.maybeToNumber(generator.next().value);
|
const cachedValue = (this.patternCache.get(key) as Generator<any>).next().value;
|
||||||
}
|
if (cachedValue || cachedValue === 0 || cachedValue === 0n) {
|
||||||
|
return this.maybeToNumber(cachedValue);
|
||||||
} else {
|
} else {
|
||||||
const generator = value();
|
const generator = value();
|
||||||
this.patternCache.set(key, generator);
|
this.patternCache.set(key, generator);
|
||||||
return this.maybeToNumber(generator.next().value);
|
return this.maybeToNumber(generator.next().value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.patternCache.set(key, value);
|
const generator = value();
|
||||||
return this.maybeToNumber(value);
|
this.patternCache.set(key, generator);
|
||||||
|
return this.maybeToNumber(generator.next().value);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.patternCache.set(key, value);
|
||||||
|
return this.maybeToNumber(value);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return this.maybeToNumber(this.patternCache.get(key));
|
return this.maybeToNumber(this.patternCache.get(key));
|
||||||
}
|
}
|
||||||
@ -833,27 +833,27 @@ export class UserAPI {
|
|||||||
if (this.app.api.patternCache.has(key)) {
|
if (this.app.api.patternCache.has(key)) {
|
||||||
player = this.app.api.patternCache.get(key) as Player;
|
player = this.app.api.patternCache.get(key) as Player;
|
||||||
|
|
||||||
if (typeof input === "string" &&
|
if (typeof input === "string" &&
|
||||||
player.input !== input &&
|
player.input !== input &&
|
||||||
player.atTheBeginning()) {
|
player.atTheBeginning()) {
|
||||||
replace = true;
|
replace = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((typeof input !== "string" || validSyntax) && (!player || replace)) {
|
if ((typeof input !== "string" || validSyntax) && (!player || replace)) {
|
||||||
const newPlayer = new Player(input, options, this.app, zid);
|
const newPlayer = new Player(input, options, this.app, zid);
|
||||||
if(newPlayer.isValid()) {
|
if (newPlayer.isValid()) {
|
||||||
player = newPlayer
|
player = newPlayer
|
||||||
this.patternCache.set(key, player);
|
this.patternCache.set(key, player);
|
||||||
} else if(typeof input === "string") {
|
} else if (typeof input === "string") {
|
||||||
this.invalidPatterns[input] = true;
|
this.invalidPatterns[input] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(player) {
|
if (player) {
|
||||||
|
|
||||||
if(player.atTheBeginning()) {
|
if (player.atTheBeginning()) {
|
||||||
if(typeof input === "string" && !validSyntax) this.app.api.log(`Invalid syntax: ${input}`);
|
if (typeof input === "string" && !validSyntax) this.app.api.log(`Invalid syntax: ${input}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.ziffers.generator && player.ziffers.generatorDone) {
|
if (player.ziffers.generator && player.ziffers.generatorDone) {
|
||||||
@ -920,7 +920,7 @@ export class UserAPI {
|
|||||||
*
|
*
|
||||||
* @returns True if the code is being evaluated for the first time
|
* @returns True if the code is being evaluated for the first time
|
||||||
*/
|
*/
|
||||||
const firstTime = this.app.api.onceEvaluator;
|
const firstTime = this.app.api.onceEvaluator;
|
||||||
this.app.api.onceEvaluator = false;
|
this.app.api.onceEvaluator = false;
|
||||||
|
|
||||||
return firstTime;
|
return firstTime;
|
||||||
@ -1395,7 +1395,7 @@ export class UserAPI {
|
|||||||
/**
|
/**
|
||||||
* Returns the number of pulses in a given bar
|
* Returns the number of pulses in a given bar
|
||||||
*/
|
*/
|
||||||
return (this.tempo()*this.ppqn()*this.nominator())/60;
|
return (this.tempo() * this.ppqn() * this.nominator()) / 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================
|
// =============================================================
|
||||||
@ -1444,7 +1444,7 @@ export class UserAPI {
|
|||||||
const results: boolean[] = nArray.map(
|
const results: boolean[] = nArray.map(
|
||||||
(value) =>
|
(value) =>
|
||||||
(this.app.clock.pulses_since_origin - Math.floor(nudge * this.ppqn())) %
|
(this.app.clock.pulses_since_origin - Math.floor(nudge * this.ppqn())) %
|
||||||
Math.floor(value * this.ppqn()) ===
|
Math.floor(value * this.ppqn()) ===
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
return results.some((value) => value === true);
|
return results.some((value) => value === true);
|
||||||
@ -1464,7 +1464,7 @@ export class UserAPI {
|
|||||||
const results: boolean[] = nArray.map(
|
const results: boolean[] = nArray.map(
|
||||||
(value) =>
|
(value) =>
|
||||||
(this.app.clock.pulses_since_origin - nudgeInPulses) %
|
(this.app.clock.pulses_since_origin - nudgeInPulses) %
|
||||||
Math.floor(value * barLength) ===
|
Math.floor(value * barLength) ===
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
return results.some((value) => value === true);
|
return results.some((value) => value === true);
|
||||||
@ -2071,7 +2071,7 @@ export class UserAPI {
|
|||||||
// =============================================================
|
// =============================================================
|
||||||
|
|
||||||
register = (name: string, operation: EventOperation<AbstractEvent>): void => {
|
register = (name: string, operation: EventOperation<AbstractEvent>): void => {
|
||||||
AbstractEvent.prototype[name] = function (
|
AbstractEvent.prototype[name] = function(
|
||||||
this: AbstractEvent,
|
this: AbstractEvent,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
) {
|
) {
|
||||||
@ -2259,7 +2259,7 @@ export class UserAPI {
|
|||||||
* Returns the current pulse location in the current bar.
|
* Returns the current pulse location in the current bar.
|
||||||
* @returns The current pulse location in the current bar
|
* @returns The current pulse location in the current bar
|
||||||
*/
|
*/
|
||||||
return ((this.epulse() / this.pulsesForBar())*this.w())%this.w()
|
return ((this.epulse() / this.pulsesForBar()) * this.w()) % this.w()
|
||||||
}
|
}
|
||||||
|
|
||||||
public clear = (): boolean => {
|
public clear = (): boolean => {
|
||||||
@ -2268,9 +2268,9 @@ export class UserAPI {
|
|||||||
* @param timeout - The timeout in seconds
|
* @param timeout - The timeout in seconds
|
||||||
*/
|
*/
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public w = (): number => {
|
public w = (): number => {
|
||||||
@ -2307,21 +2307,21 @@ export class UserAPI {
|
|||||||
return this.w() / 2;
|
return this.w() / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public background = (color: string|number, ...gb:number[]): boolean => {
|
public background = (color: string | number, ...gb: number[]): boolean => {
|
||||||
/**
|
/**
|
||||||
* Set background color of the canvas.
|
* Set background color of the canvas.
|
||||||
* @param color - The color to set. String or 3 numbers representing RGB values.
|
* @param color - The color to set. String or 3 numbers representing RGB values.
|
||||||
*/
|
*/
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
if(typeof color === "number") color = `rgb(${color},${gb[0]},${gb[1]})`;
|
if (typeof color === "number") color = `rgb(${color},${gb[0]},${gb[1]})`;
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bg = this.background;
|
bg = this.background;
|
||||||
|
|
||||||
public linearGradient = (x1: number, y1: number, x2: number, y2: number, ...stops: (number|string)[]) => {
|
public linearGradient = (x1: number, y1: number, x2: number, y2: number, ...stops: (number | string)[]) => {
|
||||||
/**
|
/**
|
||||||
* Set linear gradient on the canvas.
|
* Set linear gradient on the canvas.
|
||||||
* @param x1 - The x-coordinate of the start point
|
* @param x1 - The x-coordinate of the start point
|
||||||
@ -2334,15 +2334,15 @@ export class UserAPI {
|
|||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
const gradient = ctx.createLinearGradient(x1, y1, x2, y2);
|
const gradient = ctx.createLinearGradient(x1, y1, x2, y2);
|
||||||
// Parse pairs of values from stops
|
// Parse pairs of values from stops
|
||||||
for(let i=0; i<stops.length; i+=2) {
|
for (let i = 0; i < stops.length; i += 2) {
|
||||||
let color = stops[i+1];
|
let color = stops[i + 1];
|
||||||
if(typeof color === "number") color = `rgb(${color},${stops[i+2]},${stops[i+3]})`;
|
if (typeof color === "number") color = `rgb(${color},${stops[i + 2]},${stops[i + 3]})`;
|
||||||
gradient.addColorStop((stops[i] as number), color);
|
gradient.addColorStop((stops[i] as number), color);
|
||||||
}
|
}
|
||||||
return gradient;
|
return gradient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public radialGradient = (x1: number, y1: number, r1: number, x2: number, y2: number, r2: number, ...stops: (number|string)[]) => {
|
public radialGradient = (x1: number, y1: number, r1: number, x2: number, y2: number, r2: number, ...stops: (number | string)[]) => {
|
||||||
/**
|
/**
|
||||||
* Set radial gradient on the canvas.
|
* Set radial gradient on the canvas.
|
||||||
* @param x1 - The x-coordinate of the start circle
|
* @param x1 - The x-coordinate of the start circle
|
||||||
@ -2356,15 +2356,15 @@ export class UserAPI {
|
|||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
const gradient = ctx.createRadialGradient(x1, y1, r1, x2, y2, r2);
|
const gradient = ctx.createRadialGradient(x1, y1, r1, x2, y2, r2);
|
||||||
for(let i=0; i<stops.length; i+=2) {
|
for (let i = 0; i < stops.length; i += 2) {
|
||||||
let color = stops[i+1];
|
let color = stops[i + 1];
|
||||||
if(typeof color === "number") color = `rgb(${color},${stops[i+2]},${stops[i+3]})`;
|
if (typeof color === "number") color = `rgb(${color},${stops[i + 2]},${stops[i + 3]})`;
|
||||||
gradient.addColorStop((stops[i] as number), color);
|
gradient.addColorStop((stops[i] as number), color);
|
||||||
}
|
}
|
||||||
return gradient;
|
return gradient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public conicGradient = (x: number, y: number, angle: number, ...stops: (number|string)[]) => {
|
public conicGradient = (x: number, y: number, angle: number, ...stops: (number | string)[]) => {
|
||||||
/**
|
/**
|
||||||
* Set conic gradient on the canvas.
|
* Set conic gradient on the canvas.
|
||||||
* @param x - The x-coordinate of the center of the gradient
|
* @param x - The x-coordinate of the center of the gradient
|
||||||
@ -2375,9 +2375,9 @@ export class UserAPI {
|
|||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
const gradient = ctx.createConicGradient(x, y, angle);
|
const gradient = ctx.createConicGradient(x, y, angle);
|
||||||
for(let i=0; i<stops.length; i+=2) {
|
for (let i = 0; i < stops.length; i += 2) {
|
||||||
let color = stops[i+1];
|
let color = stops[i + 1];
|
||||||
if(typeof color === "number") color = `rgb(${color},${stops[i+2]},${stops[i+3]})`;
|
if (typeof color === "number") color = `rgb(${color},${stops[i + 2]},${stops[i + 3]})`;
|
||||||
gradient.addColorStop((stops[i] as number), color);
|
gradient.addColorStop((stops[i] as number), color);
|
||||||
}
|
}
|
||||||
return gradient;
|
return gradient;
|
||||||
@ -2388,8 +2388,8 @@ export class UserAPI {
|
|||||||
* Draws on the canvas.
|
* Draws on the canvas.
|
||||||
* @param func - The function to execute
|
* @param func - The function to execute
|
||||||
*/
|
*/
|
||||||
if(typeof func === "string") {
|
if (typeof func === "string") {
|
||||||
this.drawText (func);
|
this.drawText(func);
|
||||||
} else {
|
} else {
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
@ -2399,29 +2399,29 @@ export class UserAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public balloid = (
|
public balloid = (
|
||||||
curves: number|ShapeObject = 6,
|
curves: number | ShapeObject = 6,
|
||||||
radius: number = this.hc()/2,
|
radius: number = this.hc() / 2,
|
||||||
curve: number = 1.5,
|
curve: number = 1.5,
|
||||||
fillStyle: string = "white",
|
fillStyle: string = "white",
|
||||||
secondary: string = "black",
|
secondary: string = "black",
|
||||||
x: number = this.wc(),
|
x: number = this.wc(),
|
||||||
y: number = this.hc(),
|
y: number = this.hc(),
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof curves === "object") {
|
if (typeof curves === "object") {
|
||||||
fillStyle = curves.fillStyle || "white";
|
fillStyle = curves.fillStyle || "white";
|
||||||
x = curves.x || this.wc();
|
x = curves.x || this.wc();
|
||||||
y = curves.y || this.hc();
|
y = curves.y || this.hc();
|
||||||
curve = curves.curve || 1.5;
|
curve = curves.curve || 1.5;
|
||||||
radius = curves.radius || this.hc()/2;
|
radius = curves.radius || this.hc() / 2;
|
||||||
curves = curves.curves || 6;
|
curves = curves.curves || 6;
|
||||||
}
|
}
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
|
|
||||||
// Draw the shape using quadratic Bézier curves
|
// Draw the shape using quadratic Bézier curves
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.fillStyle = fillStyle;
|
ctx.fillStyle = fillStyle;
|
||||||
|
|
||||||
if (curves === 0) {
|
if (curves === 0) {
|
||||||
// Draw a circle if curves = 0
|
// Draw a circle if curves = 0
|
||||||
ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
||||||
@ -2429,7 +2429,7 @@ export class UserAPI {
|
|||||||
ctx.fill();
|
ctx.fill();
|
||||||
} else if (curves === 1) {
|
} else if (curves === 1) {
|
||||||
// Draw a single curve (ellipse) if curves = 1
|
// Draw a single curve (ellipse) if curves = 1
|
||||||
ctx.ellipse(x, y, radius*0.8, (radius* curve)*0.7, 0, 0, 2 * Math.PI);
|
ctx.ellipse(x, y, radius * 0.8, (radius * curve) * 0.7, 0, 0, 2 * Math.PI);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
} else if (curves === 2) {
|
} else if (curves === 2) {
|
||||||
@ -2438,20 +2438,20 @@ export class UserAPI {
|
|||||||
|
|
||||||
// First curve
|
// First curve
|
||||||
ctx.quadraticCurveTo(x + radius * curve, y, x, y + radius);
|
ctx.quadraticCurveTo(x + radius * curve, y, x, y + radius);
|
||||||
|
|
||||||
// Second symmetric curve
|
// Second symmetric curve
|
||||||
ctx.quadraticCurveTo(x - radius * curve, y, x, y - radius);
|
ctx.quadraticCurveTo(x - radius * curve, y, x, y - radius);
|
||||||
|
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
} else {
|
} else {
|
||||||
// Draw the curved shape with the specified number of curves
|
// Draw the curved shape with the specified number of curves
|
||||||
ctx.moveTo(x, y - radius);
|
ctx.moveTo(x, y - radius);
|
||||||
let points = [];
|
let points = [];
|
||||||
for (let i = 0; i < curves; i++) {
|
for (let i = 0; i < curves; i++) {
|
||||||
const startAngle = (i / curves) * 2 * Math.PI;
|
const startAngle = (i / curves) * 2 * Math.PI;
|
||||||
const endAngle = startAngle + (2 * Math.PI) / curves;
|
const endAngle = startAngle + (2 * Math.PI) / curves;
|
||||||
|
|
||||||
const controlX = x + radius * curve * Math.cos(startAngle + Math.PI / curves);
|
const controlX = x + radius * curve * Math.cos(startAngle + Math.PI / curves);
|
||||||
const controlY = y + radius * curve * Math.sin(startAngle + Math.PI / curves);
|
const controlY = y + radius * curve * Math.sin(startAngle + Math.PI / curves);
|
||||||
points.push([x + radius * Math.cos(startAngle), y + radius * Math.sin(startAngle)]);
|
points.push([x + radius * Math.cos(startAngle), y + radius * Math.sin(startAngle)]);
|
||||||
@ -2466,28 +2466,28 @@ export class UserAPI {
|
|||||||
ctx.fillStyle = secondary;
|
ctx.fillStyle = secondary;
|
||||||
// Form the shape from points with straight lines and fill it
|
// Form the shape from points with straight lines and fill it
|
||||||
ctx.moveTo(points[0][0], points[0][1]);
|
ctx.moveTo(points[0][0], points[0][1]);
|
||||||
for(let point of points) ctx.lineTo(point[0], point[1]);
|
for (let point of points) ctx.lineTo(point[0], point[1]);
|
||||||
// Close and fill
|
// Close and fill
|
||||||
|
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
public equilateral = (
|
public equilateral = (
|
||||||
radius: number|ShapeObject = this.hc()/3,
|
radius: number | ShapeObject = this.hc() / 3,
|
||||||
fillStyle: string = "white",
|
fillStyle: string = "white",
|
||||||
rotation: number = 0,
|
rotation: number = 0,
|
||||||
x: number = this.wc(),
|
x: number = this.wc(),
|
||||||
y: number = this.hc(),
|
y: number = this.hc(),
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof radius === "object") {
|
if (typeof radius === "object") {
|
||||||
fillStyle = radius.fillStyle || "white";
|
fillStyle = radius.fillStyle || "white";
|
||||||
x = radius.x || this.wc();
|
x = radius.x || this.wc();
|
||||||
y = radius.y || this.hc();
|
y = radius.y || this.hc();
|
||||||
rotation = radius.rotation || 0;
|
rotation = radius.rotation || 0;
|
||||||
radius = radius.radius || this.hc()/3;
|
radius = radius.radius || this.hc() / 3;
|
||||||
}
|
}
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
@ -2506,20 +2506,20 @@ export class UserAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public triangular = (
|
public triangular = (
|
||||||
width: number|ShapeObject = this.hc()/3,
|
width: number | ShapeObject = this.hc() / 3,
|
||||||
height: number = this.hc()/3,
|
height: number = this.hc() / 3,
|
||||||
fillStyle: string = "white",
|
fillStyle: string = "white",
|
||||||
rotation: number = 0,
|
rotation: number = 0,
|
||||||
x: number = this.wc(),
|
x: number = this.wc(),
|
||||||
y: number = this.hc(),
|
y: number = this.hc(),
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof width === "object") {
|
if (typeof width === "object") {
|
||||||
fillStyle = width.fillStyle || "white";
|
fillStyle = width.fillStyle || "white";
|
||||||
x = width.x || this.wc();
|
x = width.x || this.wc();
|
||||||
y = width.y || this.hc();
|
y = width.y || this.hc();
|
||||||
rotation = width.rotation || 0;
|
rotation = width.rotation || 0;
|
||||||
height = width.height || this.hc()/3;
|
height = width.height || this.hc() / 3;
|
||||||
width = width.width || this.hc()/3;
|
width = width.width || this.hc() / 3;
|
||||||
}
|
}
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
@ -2539,16 +2539,16 @@ export class UserAPI {
|
|||||||
pointy = this.triangular;
|
pointy = this.triangular;
|
||||||
|
|
||||||
public ball = (
|
public ball = (
|
||||||
radius: number|ShapeObject = this.hc()/3,
|
radius: number | ShapeObject = this.hc() / 3,
|
||||||
fillStyle: string = "white",
|
fillStyle: string = "white",
|
||||||
x: number = this.wc(),
|
x: number = this.wc(),
|
||||||
y: number = this.hc(),
|
y: number = this.hc(),
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof radius === "object") {
|
if (typeof radius === "object") {
|
||||||
fillStyle = radius.fillStyle || "white";
|
fillStyle = radius.fillStyle || "white";
|
||||||
x = radius.x || this.wc();
|
x = radius.x || this.wc();
|
||||||
y = radius.y || this.hc();
|
y = radius.y || this.hc();
|
||||||
radius = radius.radius || this.hc()/3;
|
radius = radius.radius || this.hc() / 3;
|
||||||
}
|
}
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
@ -2585,26 +2585,26 @@ export class UserAPI {
|
|||||||
stroke = slices.stroke || "black";
|
stroke = slices.stroke || "black";
|
||||||
slices = slices.slices || 3;
|
slices = slices.slices || 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.translate(x, y);
|
ctx.translate(x, y);
|
||||||
ctx.rotate((rotation * Math.PI) / 180);
|
ctx.rotate((rotation * Math.PI) / 180);
|
||||||
|
|
||||||
if(slices<2) {
|
if (slices < 2) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
|
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fillStyle = slices<1 ? secondary : fillStyle;
|
ctx.fillStyle = slices < 1 ? secondary : fillStyle;
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(0, 0, hole, 0, 2 * Math.PI);
|
ctx.arc(0, 0, hole, 0, 2 * Math.PI);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fillStyle = secondary;
|
ctx.fillStyle = secondary;
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2615,17 +2615,17 @@ export class UserAPI {
|
|||||||
for (let i = 0; i < totalSlices; i++) {
|
for (let i = 0; i < totalSlices; i++) {
|
||||||
const startAngle = i * sliceAngle;
|
const startAngle = i * sliceAngle;
|
||||||
const endAngle = (i + 1) * sliceAngle;
|
const endAngle = (i + 1) * sliceAngle;
|
||||||
|
|
||||||
// Calculate the position of the outer arc
|
// Calculate the position of the outer arc
|
||||||
const outerStartX = hole * Math.cos(startAngle);
|
const outerStartX = hole * Math.cos(startAngle);
|
||||||
const outerStartY = hole * Math.sin(startAngle);
|
const outerStartY = hole * Math.sin(startAngle);
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(outerStartX, outerStartY);
|
ctx.moveTo(outerStartX, outerStartY);
|
||||||
ctx.arc(0, 0, radius, startAngle, endAngle);
|
ctx.arc(0, 0, radius, startAngle, endAngle);
|
||||||
ctx.arc(0, 0, hole, endAngle, startAngle, true);
|
ctx.arc(0, 0, hole, endAngle, startAngle, true);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
|
|
||||||
// Fill and stroke the slices with the specified fill style
|
// Fill and stroke the slices with the specified fill style
|
||||||
if (i < slices - eaten) {
|
if (i < slices - eaten) {
|
||||||
// Regular slices are white
|
// Regular slices are white
|
||||||
@ -2639,14 +2639,14 @@ export class UserAPI {
|
|||||||
ctx.strokeStyle = stroke;
|
ctx.strokeStyle = stroke;
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public pie = (
|
public pie = (
|
||||||
slices: number|ShapeObject = 3,
|
slices: number | ShapeObject = 3,
|
||||||
eaten: number = 0,
|
eaten: number = 0,
|
||||||
radius: number = this.hc() / 3,
|
radius: number = this.hc() / 3,
|
||||||
fillStyle: string = "white",
|
fillStyle: string = "white",
|
||||||
@ -2667,18 +2667,18 @@ export class UserAPI {
|
|||||||
eaten = slices.eaten || 0;
|
eaten = slices.eaten || 0;
|
||||||
slices = slices.slices || 3;
|
slices = slices.slices || 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.translate(x, y);
|
ctx.translate(x, y);
|
||||||
ctx.rotate((rotation * Math.PI) / 180);
|
ctx.rotate((rotation * Math.PI) / 180);
|
||||||
|
|
||||||
if(slices<2) {
|
if (slices < 2) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
|
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fillStyle = slices<1 ? secondary : fillStyle;
|
ctx.fillStyle = slices < 1 ? secondary : fillStyle;
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
return true;
|
return true;
|
||||||
@ -2695,7 +2695,7 @@ export class UserAPI {
|
|||||||
ctx.arc(0, 0, radius, startAngle, endAngle);
|
ctx.arc(0, 0, radius, startAngle, endAngle);
|
||||||
ctx.lineTo(0, 0); // Connect to center
|
ctx.lineTo(0, 0); // Connect to center
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
|
|
||||||
// Fill and stroke the slices with the specified fill style
|
// Fill and stroke the slices with the specified fill style
|
||||||
if (i < slices - eaten) {
|
if (i < slices - eaten) {
|
||||||
// Regular slices are white
|
// Regular slices are white
|
||||||
@ -2709,67 +2709,67 @@ export class UserAPI {
|
|||||||
ctx.strokeStyle = stroke;
|
ctx.strokeStyle = stroke;
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public star = (
|
public star = (
|
||||||
points: number|ShapeObject = 5,
|
points: number | ShapeObject = 5,
|
||||||
radius: number = this.hc()/3,
|
radius: number = this.hc() / 3,
|
||||||
fillStyle: string = "white",
|
fillStyle: string = "white",
|
||||||
rotation: number = 0,
|
rotation: number = 0,
|
||||||
outerRadius: number = radius/100,
|
outerRadius: number = radius / 100,
|
||||||
x: number = this.wc(),
|
x: number = this.wc(),
|
||||||
y: number = this.hc(),
|
y: number = this.hc(),
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof points === "object") {
|
if (typeof points === "object") {
|
||||||
radius = points.radius || this.hc()/3;
|
radius = points.radius || this.hc() / 3;
|
||||||
fillStyle = points.fillStyle || "white";
|
fillStyle = points.fillStyle || "white";
|
||||||
x = points.x || this.wc();
|
x = points.x || this.wc();
|
||||||
y = points.y || this.hc();
|
y = points.y || this.hc();
|
||||||
rotation = points.rotation || 0;
|
rotation = points.rotation || 0;
|
||||||
outerRadius = points.outerRadius || radius/100;
|
outerRadius = points.outerRadius || radius / 100;
|
||||||
points = points.points || 5;
|
points = points.points || 5;
|
||||||
}
|
}
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
if(points<1) return this.ball(radius, fillStyle, x, y);
|
if (points < 1) return this.ball(radius, fillStyle, x, y);
|
||||||
if(points==1) return this.equilateral(radius, fillStyle, 0, x, y);
|
if (points == 1) return this.equilateral(radius, fillStyle, 0, x, y);
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.translate(x, y);
|
ctx.translate(x, y);
|
||||||
ctx.rotate((rotation * Math.PI) / 180);
|
ctx.rotate((rotation * Math.PI) / 180);
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(0, -radius);
|
ctx.moveTo(0, -radius);
|
||||||
for (let i = 0; i < points; i++) {
|
for (let i = 0; i < points; i++) {
|
||||||
ctx.rotate(Math.PI / points);
|
ctx.rotate(Math.PI / points);
|
||||||
ctx.lineTo(0, -(radius * outerRadius));
|
ctx.lineTo(0, -(radius * outerRadius));
|
||||||
ctx.rotate(Math.PI / points);
|
ctx.rotate(Math.PI / points);
|
||||||
ctx.lineTo(0, -radius);
|
ctx.lineTo(0, -radius);
|
||||||
}
|
}
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fillStyle = fillStyle;
|
ctx.fillStyle = fillStyle;
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
public stroke = (
|
public stroke = (
|
||||||
width: number|ShapeObject = 1,
|
width: number | ShapeObject = 1,
|
||||||
strokeStyle: string = "white",
|
strokeStyle: string = "white",
|
||||||
rotation: number = 0,
|
rotation: number = 0,
|
||||||
x1: number = this.wc()-this.wc()/10,
|
x1: number = this.wc() - this.wc() / 10,
|
||||||
y1: number = this.hc(),
|
y1: number = this.hc(),
|
||||||
x2: number = this.wc()+this.wc()/5,
|
x2: number = this.wc() + this.wc() / 5,
|
||||||
y2: number = this.hc(),
|
y2: number = this.hc(),
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof width === "object") {
|
if (typeof width === "object") {
|
||||||
strokeStyle = width.strokeStyle || "white";
|
strokeStyle = width.strokeStyle || "white";
|
||||||
x1 = width.x1 || this.wc()-this.wc()/10;
|
x1 = width.x1 || this.wc() - this.wc() / 10;
|
||||||
y1 = width.y1 || this.hc();
|
y1 = width.y1 || this.hc();
|
||||||
x2 = width.x2 || this.wc()+this.wc()/5;
|
x2 = width.x2 || this.wc() + this.wc() / 5;
|
||||||
y2 = width.y2 || this.hc();
|
y2 = width.y2 || this.hc();
|
||||||
rotation = width.rotation || 0;
|
rotation = width.rotation || 0;
|
||||||
width = width.width || 1;
|
width = width.width || 1;
|
||||||
@ -2781,7 +2781,7 @@ export class UserAPI {
|
|||||||
ctx.rotate((rotation * Math.PI) / 180);
|
ctx.rotate((rotation * Math.PI) / 180);
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(0, 0);
|
ctx.moveTo(0, 0);
|
||||||
ctx.lineTo(x2-x1, y2-y1);
|
ctx.lineTo(x2 - x1, y2 - y1);
|
||||||
ctx.lineWidth = width;
|
ctx.lineWidth = width;
|
||||||
ctx.strokeStyle = strokeStyle;
|
ctx.strokeStyle = strokeStyle;
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
@ -2790,20 +2790,20 @@ export class UserAPI {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public box = (
|
public box = (
|
||||||
width: number|ShapeObject = this.wc()/4,
|
width: number | ShapeObject = this.wc() / 4,
|
||||||
height: number = this.wc()/4,
|
height: number = this.wc() / 4,
|
||||||
fillStyle: string = "white",
|
fillStyle: string = "white",
|
||||||
rotation: number = 0,
|
rotation: number = 0,
|
||||||
x: number = this.wc()-this.wc()/8,
|
x: number = this.wc() - this.wc() / 8,
|
||||||
y: number = this.hc()-this.hc()/8,
|
y: number = this.hc() - this.hc() / 8,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof width === "object") {
|
if (typeof width === "object") {
|
||||||
fillStyle = width.fillStyle || "white";
|
fillStyle = width.fillStyle || "white";
|
||||||
x = width.x || this.wc()-this.wc()/4;
|
x = width.x || this.wc() - this.wc() / 4;
|
||||||
y = width.y || this.hc()-this.hc()/2;
|
y = width.y || this.hc() - this.hc() / 2;
|
||||||
rotation = width.rotation || 0;
|
rotation = width.rotation || 0;
|
||||||
height = width.height || this.wc()/4;
|
height = width.height || this.wc() / 4;
|
||||||
width = width.width || this.wc()/4;
|
width = width.width || this.wc() / 4;
|
||||||
}
|
}
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
@ -2817,27 +2817,27 @@ export class UserAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public smiley = (
|
public smiley = (
|
||||||
happiness: number|ShapeObject = 0,
|
happiness: number | ShapeObject = 0,
|
||||||
radius: number = this.hc()/3,
|
radius: number = this.hc() / 3,
|
||||||
eyeSize: number = 3.0,
|
eyeSize: number = 3.0,
|
||||||
fillStyle: string = "yellow",
|
fillStyle: string = "yellow",
|
||||||
rotation: number = 0,
|
rotation: number = 0,
|
||||||
x: number = this.wc(),
|
x: number = this.wc(),
|
||||||
y: number = this.hc(),
|
y: number = this.hc(),
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof happiness === "object") {
|
if (typeof happiness === "object") {
|
||||||
fillStyle = happiness.fillStyle || "yellow";
|
fillStyle = happiness.fillStyle || "yellow";
|
||||||
x = happiness.x || this.wc();
|
x = happiness.x || this.wc();
|
||||||
y = happiness.y || this.hc();
|
y = happiness.y || this.hc();
|
||||||
rotation = happiness.rotation || 0;
|
rotation = happiness.rotation || 0;
|
||||||
eyeSize = happiness.eyeSize || 3.0;
|
eyeSize = happiness.eyeSize || 3.0;
|
||||||
radius = happiness.radius || this.hc()/3;
|
radius = happiness.radius || this.hc() / 3;
|
||||||
happiness = happiness.happiness || 0;
|
happiness = happiness.happiness || 0;
|
||||||
}
|
}
|
||||||
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
|
||||||
const ctx = canvas.getContext("2d")!;
|
const ctx = canvas.getContext("2d")!;
|
||||||
// Map the rotation value to an angle within the range of -PI to PI
|
// Map the rotation value to an angle within the range of -PI to PI
|
||||||
const rotationAngle = rotation/100 * Math.PI;
|
const rotationAngle = rotation / 100 * Math.PI;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.translate(x, y);
|
ctx.translate(x, y);
|
||||||
ctx.rotate(rotationAngle);
|
ctx.rotate(rotationAngle);
|
||||||
@ -2871,16 +2871,16 @@ export class UserAPI {
|
|||||||
const mouthY = radius / 2;
|
const mouthY = radius / 2;
|
||||||
const mouthLength = radius * 0.9;
|
const mouthLength = radius * 0.9;
|
||||||
const smileFactor = 0.25; // Adjust for the smile curvature
|
const smileFactor = 0.25; // Adjust for the smile curvature
|
||||||
|
|
||||||
let controlPointX = 0;
|
let controlPointX = 0;
|
||||||
let controlPointY = 0;
|
let controlPointY = 0;
|
||||||
|
|
||||||
if (happiness >= 0) {
|
if (happiness >= 0) {
|
||||||
controlPointY = mouthY + happiness * smileFactor * radius / 2;
|
controlPointY = mouthY + happiness * smileFactor * radius / 2;
|
||||||
} else {
|
} else {
|
||||||
controlPointY = mouthY + happiness * smileFactor * radius / 2;
|
controlPointY = mouthY + happiness * smileFactor * radius / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(-mouthLength / 2, mouthY);
|
ctx.moveTo(-mouthLength / 2, mouthY);
|
||||||
ctx.quadraticCurveTo(controlPointX, controlPointY, mouthLength / 2, mouthY);
|
ctx.quadraticCurveTo(controlPointX, controlPointY, mouthLength / 2, mouthY);
|
||||||
@ -2892,7 +2892,7 @@ export class UserAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
drawText = (
|
drawText = (
|
||||||
text: string|ShapeObject,
|
text: string | ShapeObject,
|
||||||
fontSize: number = 24,
|
fontSize: number = 24,
|
||||||
rotation: number = 0,
|
rotation: number = 0,
|
||||||
font: string = "Arial",
|
font: string = "Arial",
|
||||||
@ -2901,7 +2901,7 @@ export class UserAPI {
|
|||||||
fillStyle: string = "white",
|
fillStyle: string = "white",
|
||||||
filter: string = "none",
|
filter: string = "none",
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof text === "object") {
|
if (typeof text === "object") {
|
||||||
fillStyle = text.fillStyle || "white";
|
fillStyle = text.fillStyle || "white";
|
||||||
x = text.x || this.wc();
|
x = text.x || this.wc();
|
||||||
y = text.y || this.hc();
|
y = text.y || this.hc();
|
||||||
@ -2925,16 +2925,16 @@ export class UserAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
image = (
|
image = (
|
||||||
url: string|ShapeObject,
|
url: string | ShapeObject,
|
||||||
width: number = this.wc()/2,
|
width: number = this.wc() / 2,
|
||||||
height: number = this.hc()/2,
|
height: number = this.hc() / 2,
|
||||||
rotation: number = 0,
|
rotation: number = 0,
|
||||||
x: number = this.wc(),
|
x: number = this.wc(),
|
||||||
y: number = this.hc(),
|
y: number = this.hc(),
|
||||||
filter: string = "none",
|
filter: string = "none",
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if(typeof url === "object") {
|
if (typeof url === "object") {
|
||||||
if(!url.url) return true;
|
if (!url.url) return true;
|
||||||
x = url.x || this.wc();
|
x = url.x || this.wc();
|
||||||
y = url.y || this.hc();
|
y = url.y || this.hc();
|
||||||
rotation = url.rotation || 0;
|
rotation = url.rotation || 0;
|
||||||
@ -2951,12 +2951,12 @@ export class UserAPI {
|
|||||||
ctx.filter = filter;
|
ctx.filter = filter;
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.src = url;
|
image.src = url;
|
||||||
ctx.drawImage(image, -width/2, -height/2, width, height);
|
ctx.drawImage(image, -width / 2, -height / 2, width, height);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
randomChar = (length: number= 1, min: number = 0, max: number = 65536): string => {
|
randomChar = (length: number = 1, min: number = 0, max: number = 65536): string => {
|
||||||
return Array.from(
|
return Array.from(
|
||||||
|
|
||||||
{ length }, () => String.fromCodePoint(Math.floor(Math.random() * (max - min) + min))
|
{ length }, () => String.fromCodePoint(Math.floor(Math.random() * (max - min) + min))
|
||||||
@ -2967,7 +2967,7 @@ export class UserAPI {
|
|||||||
const codePoint = Math.floor(Math.random() * (max - min) + min);
|
const codePoint = Math.floor(Math.random() * (max - min) + min);
|
||||||
return String.fromCodePoint(codePoint);
|
return String.fromCodePoint(codePoint);
|
||||||
};
|
};
|
||||||
|
|
||||||
emoji = (n: number = 1): string => {
|
emoji = (n: number = 1): string => {
|
||||||
return this.randomChar(n, 0x1f600, 0x1f64f);
|
return this.randomChar(n, 0x1f600, 0x1f64f);
|
||||||
};
|
};
|
||||||
@ -3068,7 +3068,7 @@ export class UserAPI {
|
|||||||
this.app.clock.time_signature = [numerator, denominator];
|
this.app.clock.time_signature = [numerator, denominator];
|
||||||
};
|
};
|
||||||
|
|
||||||
public cue = (functionName: string|Function): void => {
|
public cue = (functionName: string | Function): void => {
|
||||||
functionName = typeof functionName === "function" ? functionName.name : functionName;
|
functionName = typeof functionName === "function" ? functionName.name : functionName;
|
||||||
this.cueTimes[functionName] = this.app.clock.pulses_since_origin;
|
this.cueTimes[functionName] = this.app.clock.pulses_since_origin;
|
||||||
};
|
};
|
||||||
@ -3086,7 +3086,6 @@ export class UserAPI {
|
|||||||
let theme_names = this.getThemes();
|
let theme_names = this.getThemes();
|
||||||
let selected_theme = theme_names[Math.floor(Math.random() * theme_names.length)];
|
let selected_theme = theme_names[Math.floor(Math.random() * theme_names.length)];
|
||||||
this.app.readTheme(selected_theme);
|
this.app.readTheme(selected_theme);
|
||||||
this.app.api.log(selected_theme);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public nextTheme = (): void => {
|
public nextTheme = (): void => {
|
||||||
|
|||||||
BIN
src/assets/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
src/assets/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
src/assets/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/assets/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
46
src/assets/favicon.svg
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="48.000000pt" height="48.000000pt" viewBox="0 0 48.000000 48.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
|
||||||
|
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M163 470 c-13 -5 -23 -12 -23 -15 0 -3 46 -5 102 -5 80 0 99 3 90 12
|
||||||
|
-15 15 -139 21 -169 8z"/>
|
||||||
|
<path d="M91 391 c-16 -16 -19 -32 -18 -84 1 -53 -2 -69 -18 -87 -15 -16 -20
|
||||||
|
-38 -22 -83 0 -33 2 -56 5 -50 9 13 44 13 39 -1 -2 -6 -18 -11 -34 -11 -39 0
|
||||||
|
-50 -10 -33 -30 8 -10 30 -15 60 -15 26 0 64 -7 83 -15 43 -18 125 -20 164 -3
|
||||||
|
15 6 57 14 93 17 73 7 87 24 51 60 -12 12 -21 17 -21 13 0 -4 5 -13 12 -20 8
|
||||||
|
-8 8 -12 1 -12 -18 0 -25 34 -9 45 10 7 12 16 6 25 -5 8 -7 24 -4 35 3 13 -2
|
||||||
|
28 -15 39 -12 11 -21 27 -21 36 0 9 -5 21 -11 27 -8 8 -8 17 0 31 17 32 13 56
|
||||||
|
-12 80 -31 29 -63 28 -91 -3 -16 -17 -34 -25 -56 -25 -22 0 -40 8 -56 25 -28
|
||||||
|
30 -67 32 -93 6z m89 -16 c19 -23 5 -29 -24 -10 -16 10 -33 14 -47 10 -14 -5
|
||||||
|
-19 -4 -15 4 10 16 72 13 86 -4z m211 -7 c12 -22 11 -22 -8 -5 -25 21 -41 22
|
||||||
|
-65 0 -22 -19 -36 -10 -18 12 20 24 77 19 91 -7z m-264 -30 c-3 -7 -5 -2 -5
|
||||||
|
12 0 14 2 19 5 13 2 -7 2 -19 0 -25z m229 -5 c-11 -11 -19 6 -11 24 8 17 8 17
|
||||||
|
12 0 3 -10 2 -21 -1 -24z m-80 -8 c4 -8 10 -12 15 -9 5 3 9 0 9 -6 0 -14 60
|
||||||
|
-40 77 -33 7 3 13 -2 13 -11 0 -13 -6 -15 -27 -9 -36 9 -210 9 -245 0 -22 -6
|
||||||
|
-28 -4 -28 9 0 8 6 14 13 11 6 -2 25 2 40 10 15 8 32 11 37 8 6 -4 7 1 3 11
|
||||||
|
-4 12 -3 15 5 10 7 -4 15 -1 18 8 8 21 63 21 70 1z m83 -91 c10 -9 -37 -33
|
||||||
|
-77 -39 -55 -8 -117 2 -155 26 l-30 18 54 4 c56 4 201 -2 208 -9z m-286 -30
|
||||||
|
c-15 -7 21 -44 42 -44 8 0 15 -4 15 -10 0 -16 -33 -11 -59 9 -25 19 -24 52 1
|
||||||
|
50 9 0 10 -2 1 -5z m355 -21 c2 -14 -4 -23 -17 -28 -12 -3 -21 -13 -21 -21 0
|
||||||
|
-19 -16 -18 -24 1 -5 15 -31 16 -53 2 -8 -5 -13 -4 -13 2 0 6 5 12 10 12 6 1
|
||||||
|
15 3 20 4 6 1 18 3 28 4 25 1 66 37 52 44 -6 3 -5 4 2 3 7 -1 14 -12 16 -23z
|
||||||
|
m-255 -37 c4 -10 1 -13 -9 -9 -7 3 -14 9 -14 14 0 14 17 10 23 -5z m42 -6 c3
|
||||||
|
-5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m35 0 c0
|
||||||
|
-5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m-170
|
||||||
|
-10 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z
|
||||||
|
m215 -39 c-6 -5 -25 10 -25 20 0 5 6 4 14 -3 8 -7 12 -15 11 -17z m45 8 c0
|
||||||
|
-14 -16 -11 -29 5 -10 12 -8 13 8 9 12 -3 21 -9 21 -14z m-220 1 c0 -5 -5 -10
|
||||||
|
-11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m40 0 c0 -5 -4 -10
|
||||||
|
-10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m215 0 c3 -5 2
|
||||||
|
-10 -4 -10 -5 0 -13 5 -16 10 -3 6 -2 10 4 10 5 0 13 -4 16 -10z m43 -10 c7
|
||||||
|
-11 10 -20 6 -20 -7 0 -34 27 -34 34 0 13 16 5 28 -14z m-160 -17 c-10 -2 -26
|
||||||
|
-2 -35 0 -10 3 -2 5 17 5 19 0 27 -2 18 -5z m99 1 c-3 -3 -12 -4 -19 -1 -8 3
|
||||||
|
-5 6 6 6 11 1 17 -2 13 -5z m40 0 c-3 -3 -12 -4 -19 -1 -8 3 -5 6 6 6 11 1 17
|
||||||
|
-2 13 -5z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/assets/mstile-150x150.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
15
src/assets/safari-pinned-tab.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="850.000000pt" height="850.000000pt" viewBox="0 0 850.000000 850.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,850.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M0 4250 l0 -3770 4250 0 4250 0 0 3770 0 3770 -4250 0 -4250 0 0
|
||||||
|
-3770z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 603 B |
BIN
src/assets/topos_code.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 427 KiB After Width: | Height: | Size: 427 KiB |
BIN
src/assets/topos_gif.gif
Normal file
|
After Width: | Height: | Size: 4.9 MiB |
@ -31,13 +31,12 @@ import { makeStringExtensions } from "./extensions/StringExtensions";
|
|||||||
import { installInterfaceLogic } from "./InterfaceLogic";
|
import { installInterfaceLogic } from "./InterfaceLogic";
|
||||||
import { installWindowBehaviors } from "./WindowBehavior";
|
import { installWindowBehaviors } from "./WindowBehavior";
|
||||||
import { makeNumberExtensions } from "./extensions/NumberExtensions";
|
import { makeNumberExtensions } from "./extensions/NumberExtensions";
|
||||||
// @ts-ignore
|
|
||||||
import { registerSW } from "virtual:pwa-register";
|
|
||||||
import colors from "./colors.json";
|
import colors from "./colors.json";
|
||||||
|
// @ts-ignore
|
||||||
|
const images = import.meta.glob("./assets/*")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ("serviceWorker" in navigator) {
|
|
||||||
registerSW();
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Editor {
|
export class Editor {
|
||||||
// Universes and settings
|
// Universes and settings
|
||||||
|
|||||||
@ -4,15 +4,16 @@ import viteCompression from "vite-plugin-compression";
|
|||||||
|
|
||||||
const vitePWAconfiguration = {
|
const vitePWAconfiguration = {
|
||||||
devOptions: {
|
devOptions: {
|
||||||
enabled: true,
|
enabled: false,
|
||||||
suppressWarnings: true,
|
suppressWarnings: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
workbox: {
|
workbox: {
|
||||||
sourcemap: false,
|
sourcemap: false,
|
||||||
cleanupOutdatedCaches: false,
|
cleanupOutdatedCaches: false,
|
||||||
|
maximumFileSizeToCacheInBytes: 10000000,
|
||||||
globPatterns: [
|
globPatterns: [
|
||||||
"**/*.{js,js.gz,css,html,gif,png,json,woff,woff2,json,ogg,wav,mp3,ico,png,svg}",
|
"**/*.{js,js.gz,css,html,gif,png,json,woff,woff2,json,ogg,wav,mp3,ico,png,svg}",
|
||||||
|
"favicon/*.{js,js.gz,css,html,gif,png,json,woff,woff2,json,ogg,wav,mp3,ico,png,svg}",
|
||||||
],
|
],
|
||||||
runtimeCaching: [
|
runtimeCaching: [
|
||||||
{
|
{
|
||||||
@ -35,14 +36,9 @@ const vitePWAconfiguration = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
manifest: false,
|
||||||
"favicon/favicon.icon",
|
|
||||||
"favicon/apple-touch-icon.png",
|
|
||||||
"mask-icon.svg",
|
|
||||||
],
|
|
||||||
manifest: "manifest.webmanifest",
|
|
||||||
registerType: "autoUpdate",
|
registerType: "autoUpdate",
|
||||||
injectRegister: "auto",
|
injectRegister: "script-defer",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineConfig(({ command, mode, ssrBuild }) => {
|
export default defineConfig(({ command, mode, ssrBuild }) => {
|
||||||
@ -54,6 +50,13 @@ export default defineConfig(({ command, mode, ssrBuild }) => {
|
|||||||
port: 8000,
|
port: 8000,
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
},
|
},
|
||||||
|
build: {
|
||||||
|
outDir: "dist",
|
||||||
|
emptyOutDir: true,
|
||||||
|
cssCodeSplit: true,
|
||||||
|
cssMinify: true,
|
||||||
|
minify: true,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
|||||||
12
yarn.lock
@ -2203,7 +2203,7 @@ fast-glob@^3.2.12:
|
|||||||
merge2 "^1.3.0"
|
merge2 "^1.3.0"
|
||||||
micromatch "^4.0.4"
|
micromatch "^4.0.4"
|
||||||
|
|
||||||
fast-glob@^3.3.1:
|
fast-glob@^3.3.2:
|
||||||
version "3.3.2"
|
version "3.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
|
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
|
||||||
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
|
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
|
||||||
@ -3774,13 +3774,13 @@ vite-plugin-markdown@^2.1.0:
|
|||||||
htmlparser2 "^6.0.0"
|
htmlparser2 "^6.0.0"
|
||||||
markdown-it "^12.0.0"
|
markdown-it "^12.0.0"
|
||||||
|
|
||||||
vite-plugin-pwa@^0.16.7:
|
vite-plugin-pwa@^0.17.4:
|
||||||
version "0.16.7"
|
version "0.17.4"
|
||||||
resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-0.16.7.tgz#3dcacc342766ff3598472ac7d5e0782d14e2853e"
|
resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-0.17.4.tgz#be3b3714d4148681bc73e8e0b1e6ce1a71fa79f2"
|
||||||
integrity sha512-4WMA5unuKlHs+koNoykeuCfTcqEGbiTRr8sVYUQMhc6tWxZpSRnv9Ojk4LKmqVhoPGHfBVCdGaMo8t9Qidkc1Q==
|
integrity sha512-j9iiyinFOYyof4Zk3Q+DtmYyDVBDAi6PuMGNGq6uGI0pw7E+LNm9e+nQ2ep9obMP/kjdWwzilqUrlfVRj9OobA==
|
||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
fast-glob "^3.3.1"
|
fast-glob "^3.3.2"
|
||||||
pretty-bytes "^6.1.1"
|
pretty-bytes "^6.1.1"
|
||||||
workbox-build "^7.0.0"
|
workbox-build "^7.0.0"
|
||||||
workbox-window "^7.0.0"
|
workbox-window "^7.0.0"
|
||||||
|
|||||||