Compare commits

...

4 commits

Author SHA1 Message Date
62ee692291 Add version bump script
All checks were successful
Check / check (push) Successful in 37s
Publish / build (push) Successful in 39s
Publish / publish-npm (push) Has been skipped
2026-06-06 15:50:38 +01:00
974a97c134 Add CI 2026-06-06 15:42:40 +01:00
c6438a9e21 Bump dependencies 2026-06-06 14:58:32 +01:00
346fc96df8 Allow albedo over 1 if supported 2026-06-06 14:43:22 +01:00
24 changed files with 302 additions and 74 deletions

View file

@ -1 +0,0 @@
**/*.js

View file

@ -1,30 +0,0 @@
{
"root": true,
"env": {
"browser": true,
"es2020": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"prettier/@typescript-eslint"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"plugins": ["unused-imports", "@typescript-eslint", "prettier"],
"rules": {
"prettier/prettier": "error",
"no-unused-vars": "off",
"unused-imports/no-unused-imports-ts": "error",
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/ban-ts-comment": "off"
}
}

View file

@ -0,0 +1,45 @@
name: Check
on:
push:
branches: ['main']
pull_request:
branches: ['main']
jobs:
check:
runs-on: docker
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22.x'
check-latest: true
# package-lock.json is gitignored, so cache the global npm download cache
# keyed on package.json rather than relying on `npm ci` + a lockfile.
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('package.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install dependencies
run: npm install
- name: Check formatting
run: npm run format
- name: Lint
run: npm run lint
- name: Type-check
run: npm test
- name: Build
run: npm run build

View file

@ -0,0 +1,88 @@
name: Publish
on:
push:
branches: ['main']
tags: ['*']
workflow_dispatch:
concurrency:
group: 'pages'
cancel-in-progress: false
jobs:
build:
runs-on: docker
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22.x'
check-latest: true
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('package.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install dependencies
run: npm install
- name: Check formatting
run: npm run format
- name: Lint
run: npm run lint
- name: Type-check
run: npm test
- name: Build library and docs
run: npm run build
- name: Deploy docs to pages mount
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: http://forgejo:3000/andras/ci-actions/deploy-pages@main
with:
source: docs
target: sdf-2d-docs
publish-npm:
needs: build
runs-on: docker
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22.x'
check-latest: true
registry-url: 'https://registry.npmjs.org'
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('package.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install dependencies
run: npm install
- name: Build library
run: npm run build
- name: Publish to NPM
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

3
custom.d.ts vendored
View file

@ -3,3 +3,6 @@ declare module '*.glsl' {
const content: string; const content: string;
export default content; export default content;
} }
/** Injected at build time by webpack's DefinePlugin. @internal */
declare const __VERSION__: string;

43
eslint.config.mjs Normal file
View file

@ -0,0 +1,43 @@
import js from '@eslint/js';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import prettier from 'eslint-plugin-prettier';
import prettierConfig from 'eslint-config-prettier';
import unusedImports from 'eslint-plugin-unused-imports';
import globals from 'globals';
export default [
{
ignores: ['lib/**', 'docs/**', 'node_modules/**', '**/*.js', '**/*.mjs', '**/*.cjs'],
},
js.configs.recommended,
tsPlugin.configs['flat/eslint-recommended'],
...tsPlugin.configs['flat/recommended'],
prettierConfig,
{
files: ['src/**/*.ts'],
languageOptions: {
ecmaVersion: 2020,
sourceType: 'module',
globals: {
...globals.browser,
__VERSION__: 'readonly',
},
},
plugins: {
'unused-imports': unusedImports,
prettier,
},
rules: {
'prettier/prettier': 'error',
// Newly part of eslint:recommended in v9+; would require ES2022 Error `cause`.
'preserve-caught-error': 'off',
'no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
},
},
];

View file

@ -35,7 +35,12 @@
"scripts": { "scripts": {
"start": "rm -rf lib/* && webpack --mode development -w", "start": "rm -rf lib/* && webpack --mode development -w",
"build": "rm -rf lib/* docs/* && npx typedoc && webpack --mode production", "build": "rm -rf lib/* docs/* && npx typedoc && webpack --mode production",
"lint": "eslint src/**/*.{js,ts,json} --fix && prettier --write src/**/*.{js,ts,json}" "test": "tsc --noEmit",
"format": "prettier --check \"src/**/*.{ts,json}\"",
"format:fix": "prettier --write \"src/**/*.{ts,json}\"",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"fix": "npm run lint:fix && npm run format:fix"
}, },
"main": "lib/main.js", "main": "lib/main.js",
"types": "lib/src/main.d.ts", "types": "lib/src/main.d.ts",
@ -46,26 +51,26 @@
"src/main.ts" "src/main.ts"
], ],
"dependencies": { "dependencies": {
"gl-matrix": "^3.3.0", "gl-matrix": "^3.4.4",
"resize-observer-polyfill": "^1.5.1" "resize-observer-polyfill": "^1.5.1"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^3.10.1", "@eslint/js": "^10.0.1",
"@typescript-eslint/parser": "^3.10.1", "@typescript-eslint/eslint-plugin": "^8.60.1",
"eslint": "^7.9.0", "@typescript-eslint/parser": "^8.60.1",
"eslint-config-prettier": "^6.11.0", "eslint": "^10.4.1",
"eslint-plugin-import": "^2.22.0", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-json-format": "^2.0.1", "eslint-plugin-prettier": "^5.5.6",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-unused-imports": "^4.4.1",
"eslint-plugin-unused-imports": "^0.1.3", "globals": "^17.6.0",
"prettier": "^2.1.2", "prettier": "^3.8.3",
"raw-loader": "^4.0.1", "raw-loader": "^4.0.2",
"terser-webpack-plugin": "^2.3.8", "terser-webpack-plugin": "^5.6.1",
"ts-loader": "^8.0.3", "ts-loader": "^9.6.0",
"typedoc": "^0.19.2", "typedoc": "^0.28.19",
"typedoc-plugin-extras": "^1.1.6", "typedoc-plugin-extras": "^4.0.1",
"typescript": "^3.9.7", "typescript": "^6.0.3",
"webpack": "^4.44.2", "webpack": "^5.107.2",
"webpack-cli": "^3.3.11" "webpack-cli": "^7.0.3"
} }
} }

39
scripts/bump-version.sh Executable file
View file

@ -0,0 +1,39 @@
#!/bin/bash
set -e
if [[ -z $1 ]]; then
echo "Usage: $0 {patch|minor|major}"
exit 1
fi
if [[ $1 =~ ^(patch|minor|major)$ ]]; then
echo "Creating a new '$1' version"
else
echo "Invalid argument: $1"
echo "Usage: $0 {patch|minor|major}"
exit 1
fi
if [[ -n $(git status --porcelain) ]]; then
echo "Your working directory is not clean. Please commit or stash your changes before proceeding."
exit 1
else
echo "Your working directory is clean."
fi
echo "Bumping version"
# Bump package.json only; we create the commit and tag ourselves below so the
# tag matches the bare version (e.g. 0.7.7) that publish.yml triggers on.
npm version "$1" --no-git-tag-version
# Commit and tag
git add .
TAG=$(node -p "require('./package.json').version")
git commit -m "Bump version to $TAG"
git push
echo "Tagging $TAG"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"
echo "Done"

View file

@ -5,7 +5,11 @@ import { Drawable } from '../../main';
export abstract class LightDrawable extends Drawable { export abstract class LightDrawable extends Drawable {
protected lightnessRatio = 1; protected lightnessRatio = 1;
constructor(public center: vec2, public color: vec3, public intensity: number) { constructor(
public center: vec2,
public color: vec3,
public intensity: number
) {
super(); super();
} }

View file

@ -7,7 +7,10 @@ import { EmptyDrawable } from '../empty-drawable';
* @category Drawable * @category Drawable
*/ */
class CircleBase extends EmptyDrawable { class CircleBase extends EmptyDrawable {
constructor(public center: vec2, public radius: number) { constructor(
public center: vec2,
public radius: number
) {
super(); super();
} }
} }

View file

@ -35,7 +35,11 @@ export class ColorfulCircle extends Drawable {
empty: new ColorfulCircle(vec2.create(), 0, 0), empty: new ColorfulCircle(vec2.create(), 0, 0),
}; };
constructor(public center: vec2, public radius: number, public colorIndex: number) { constructor(
public center: vec2,
public radius: number,
public colorIndex: number
) {
super(); super();
} }

View file

@ -7,7 +7,10 @@ import { EmptyDrawable } from '../empty-drawable';
* @category Drawable * @category Drawable
*/ */
class HexagonBase extends EmptyDrawable { class HexagonBase extends EmptyDrawable {
constructor(public center: vec2, public radius: number) { constructor(
public center: vec2,
public radius: number
) {
super(); super();
} }
} }

View file

@ -8,7 +8,10 @@ import { numberToGlslFloat } from '../../helper/number-to-glsl-float';
* @category Drawable * @category Drawable
*/ */
class MetaCircleBase extends EmptyDrawable { class MetaCircleBase extends EmptyDrawable {
constructor(public center: vec2, public radius: number) { constructor(
public center: vec2,
public radius: number
) {
super(); super();
} }
} }

View file

@ -112,9 +112,9 @@ export const NoisyPolygonFactory = (
}, },
uniformCountMacroName: `NOISY_POLYGON${vertexCount}_COUNT`, uniformCountMacroName: `NOISY_POLYGON${vertexCount}_COUNT`,
shaderCombinationSteps: [0, 1, 2, 3, 8, 16], shaderCombinationSteps: [0, 1, 2, 3, 8, 16],
empty: (new NoisyPolygon( empty: new NoisyPolygon(
new Array(vertexCount).fill(vec2.create()) new Array(vertexCount).fill(vec2.create())
) as unknown) as Drawable, ) as unknown as Drawable,
}; };
public randomOffset = 0; public randomOffset = 0;

View file

@ -7,7 +7,11 @@ import { EmptyDrawable } from '../empty-drawable';
* @category Drawable * @category Drawable
*/ */
class RotatedRectangleBase extends EmptyDrawable { class RotatedRectangleBase extends EmptyDrawable {
constructor(public center: vec2, public size: vec2, public rotation: number) { constructor(
public center: vec2,
public size: vec2,
public rotation: number
) {
super(); super();
} }
} }

View file

@ -135,7 +135,7 @@ export class ParallelCompiler {
private prettyPrintErrorsIfThereAreAny(shader: ShaderWithSource) { private prettyPrintErrorsIfThereAreAny(shader: ShaderWithSource) {
try { try {
this.checkShader(shader); this.checkShader(shader);
} catch (e) { } catch (e: any) {
for (const match of e.toString().matchAll(/ERROR: 0:(\d+): (.*)$/gm)) { for (const match of e.toString().matchAll(/ERROR: 0:(\d+): (.*)$/gm)) {
const line = Number.parseInt(match[1]); const line = Number.parseInt(match[1]);
const error = match[2]; const error = match[2];

View file

@ -1,4 +1,5 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
import { tryEnableExtension } from '../helper/enable-extension';
import { UniversalRenderingContext } from '../universal-rendering-context'; import { UniversalRenderingContext } from '../universal-rendering-context';
import { Texture } from './texture'; import { Texture } from './texture';
import { FilteringOptions } from './texture-options'; import { FilteringOptions } from './texture-options';
@ -7,24 +8,31 @@ import { FilteringOptions } from './texture-options';
export class ColorTexture extends Texture { export class ColorTexture extends Texture {
public static readonly textureUnitId = 0; public static readonly textureUnitId = 0;
private floatEnabled = false;
constructor(gl: UniversalRenderingContext) { constructor(gl: UniversalRenderingContext) {
super(gl, ColorTexture.textureUnitId, { super(gl, ColorTexture.textureUnitId, {
minFilter: FilteringOptions.LINEAR, minFilter: FilteringOptions.LINEAR,
maxFilter: FilteringOptions.LINEAR, maxFilter: FilteringOptions.LINEAR,
}); });
const bufferFloatExtension = tryEnableExtension(gl, 'EXT_color_buffer_float');
const floatLinearExtension = tryEnableExtension(gl, 'OES_texture_float_linear');
this.floatEnabled = gl.isWebGL2 && !!bufferFloatExtension && !!floatLinearExtension;
} }
public setSize(size: vec2) { public setSize(size: vec2) {
this.bind(); this.bind();
const gl2 = this.gl as WebGL2RenderingContext;
this.gl.texImage2D( this.gl.texImage2D(
this.gl.TEXTURE_2D, this.gl.TEXTURE_2D,
0, 0,
this.gl.RGBA, this.floatEnabled ? gl2.RGBA16F : this.gl.RGBA,
size.x, size.x,
size.y, size.y,
0, 0,
this.gl.RGBA, this.gl.RGBA,
this.gl.UNSIGNED_BYTE, this.floatEnabled ? gl2.HALF_FLOAT : this.gl.UNSIGNED_BYTE,
null null
); );
} }

View file

@ -6,7 +6,10 @@ import { Texture } from './texture';
export class PaletteTexture extends Texture { export class PaletteTexture extends Texture {
public static readonly textureUnitId = 2; public static readonly textureUnitId = 2;
constructor(gl: UniversalRenderingContext, private readonly paletteSize: number) { constructor(
gl: UniversalRenderingContext,
private readonly paletteSize: number
) {
super(gl, PaletteTexture.textureUnitId); super(gl, PaletteTexture.textureUnitId);
} }

View file

@ -230,7 +230,8 @@ export class RendererImplementation implements Renderer {
this.stopwatch.start(); this.stopwatch.start();
} else { } else {
this.stopwatch.tryGetResults(); this.stopwatch.tryGetResults();
this.gl.insights.gpuRenderTimeInMilliseconds = this.stopwatch.resultsInMilliseconds; this.gl.insights.gpuRenderTimeInMilliseconds =
this.stopwatch.resultsInMilliseconds;
} }
} }

View file

@ -23,6 +23,11 @@ declare global {
x: number; x: number;
y: number; y: number;
} }
interface Iterable<T> {
x: T;
y: T;
}
} }
applyArrayPlugins(); applyArrayPlugins();

View file

@ -60,7 +60,7 @@ export async function runAnimation(
const deltaTimeCalculator = new DeltaTimeCalculator(); const deltaTimeCalculator = new DeltaTimeCalculator();
let triggerIsOver: () => void; let triggerIsOver: () => void;
const isOver = new Promise((resolve) => (triggerIsOver = resolve)); const isOver = new Promise<void>((resolve) => (triggerIsOver = resolve));
renderer.setRuntimeSettings(settings); renderer.setRuntimeSettings(settings);
const autoscaler = new FpsQualityAutoscaler(renderer); const autoscaler = new FpsQualityAutoscaler(renderer);

View file

@ -5,7 +5,9 @@
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"target": "es5", "target": "es5",
"ignoreDeprecations": "6.0",
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": true,
"strict": true, "strict": true,
"downlevelIteration": true, "downlevelIteration": true,
"moduleResolution": "Node", "moduleResolution": "Node",

View file

@ -1,18 +1,15 @@
{ {
"mode": "file",
"out": "docs", "out": "docs",
"name": "SDF-2D", "name": "SDF-2D",
"includeDeclarations": true, "entryPoints": ["src"],
"entryPointStrategy": "expand",
"excludeExternals": true, "excludeExternals": true,
"excludeNotExported": true,
"excludePrivate": true, "excludePrivate": true,
"stripInternal": true, "excludeInternal": true,
"media": "media",
"includeVersion": true, "includeVersion": true,
"hideGenerator": true, "hideGenerator": true,
"inputFiles": "src", "readme": "documentation-readme.md",
"favicon": "media/favicon.ico", "favicon": "media/favicon.ico",
"ignoreCompilerErrors": true, "skipErrorChecking": true,
"listInvalidSymbolLinks": true, "plugin": ["typedoc-plugin-extras"]
"readme": "documentation-readme.md"
} }

View file

@ -38,7 +38,6 @@ module.exports = {
usedExports: true, usedExports: true,
minimizer: [ minimizer: [
new TerserJSPlugin({ new TerserJSPlugin({
sourceMap: true,
test: /\.js$/i, test: /\.js$/i,
}), }),
], ],