From 346fc96df8249d276ca5986fc564c5c9d9cbb0c7 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 6 Jun 2026 14:43:22 +0100 Subject: [PATCH 1/4] Allow albedo over 1 if supported --- .../graphics-library/texture/color-texture.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/graphics/graphics-library/texture/color-texture.ts b/src/graphics/graphics-library/texture/color-texture.ts index eaab31b..8da3b36 100644 --- a/src/graphics/graphics-library/texture/color-texture.ts +++ b/src/graphics/graphics-library/texture/color-texture.ts @@ -1,4 +1,5 @@ import { vec2 } from 'gl-matrix'; +import { tryEnableExtension } from '../helper/enable-extension'; import { UniversalRenderingContext } from '../universal-rendering-context'; import { Texture } from './texture'; import { FilteringOptions } from './texture-options'; @@ -7,24 +8,31 @@ import { FilteringOptions } from './texture-options'; export class ColorTexture extends Texture { public static readonly textureUnitId = 0; + private floatEnabled = false; + constructor(gl: UniversalRenderingContext) { super(gl, ColorTexture.textureUnitId, { minFilter: 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) { this.bind(); + const gl2 = this.gl as WebGL2RenderingContext; this.gl.texImage2D( this.gl.TEXTURE_2D, 0, - this.gl.RGBA, + this.floatEnabled ? gl2.RGBA16F : this.gl.RGBA, size.x, size.y, 0, this.gl.RGBA, - this.gl.UNSIGNED_BYTE, + this.floatEnabled ? gl2.HALF_FLOAT : this.gl.UNSIGNED_BYTE, null ); } From c6438a9e21e0795867e037e439abbb4191977b94 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 6 Jun 2026 14:58:32 +0100 Subject: [PATCH 2/4] Bump dependencies --- .eslintignore | 1 - .eslintrc.json | 30 ------------- custom.d.ts | 3 ++ eslint.config.mjs | 43 +++++++++++++++++++ package.json | 38 ++++++++-------- src/drawables/lights/light-drawable.ts | 6 ++- src/drawables/shapes/circle-factory.ts | 5 ++- src/drawables/shapes/colorful-circle.ts | 6 ++- src/drawables/shapes/hexagon-factory.ts | 5 ++- src/drawables/shapes/meta-circle-factory.ts | 5 ++- src/drawables/shapes/noisy-polygon-factory.ts | 4 +- .../shapes/rotated-rectangle-factory.ts | 6 ++- .../graphics-library/parallel-compiler.ts | 2 +- .../texture/palette-texture.ts | 5 ++- .../renderer/renderer-implementation.ts | 3 +- src/main.ts | 5 +++ src/run-animation.ts | 2 +- tsconfig.json | 2 + typedoc.json | 15 +++---- webpack.config.js | 1 - 20 files changed, 115 insertions(+), 72 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.json create mode 100644 eslint.config.mjs diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 2cb7d2a..0000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -**/*.js diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index a83b081..0000000 --- a/.eslintrc.json +++ /dev/null @@ -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" - } -} diff --git a/custom.d.ts b/custom.d.ts index f1f7948..0d0511c 100644 --- a/custom.d.ts +++ b/custom.d.ts @@ -3,3 +3,6 @@ declare module '*.glsl' { const content: string; export default content; } + +/** Injected at build time by webpack's DefinePlugin. @internal */ +declare const __VERSION__: string; diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..3f45f24 --- /dev/null +++ b/eslint.config.mjs @@ -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', + }, + }, +]; diff --git a/package.json b/package.json index 97b7bd9..d8f506b 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "scripts": { "start": "rm -rf lib/* && webpack --mode development -w", "build": "rm -rf lib/* docs/* && npx typedoc && webpack --mode production", - "lint": "eslint src/**/*.{js,ts,json} --fix && prettier --write src/**/*.{js,ts,json}" + "lint": "eslint src --fix && prettier --write \"src/**/*.{ts,json}\"" }, "main": "lib/main.js", "types": "lib/src/main.d.ts", @@ -46,26 +46,26 @@ "src/main.ts" ], "dependencies": { - "gl-matrix": "^3.3.0", + "gl-matrix": "^3.4.4", "resize-observer-polyfill": "^1.5.1" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^3.10.1", - "@typescript-eslint/parser": "^3.10.1", - "eslint": "^7.9.0", - "eslint-config-prettier": "^6.11.0", - "eslint-plugin-import": "^2.22.0", - "eslint-plugin-json-format": "^2.0.1", - "eslint-plugin-prettier": "^3.1.4", - "eslint-plugin-unused-imports": "^0.1.3", - "prettier": "^2.1.2", - "raw-loader": "^4.0.1", - "terser-webpack-plugin": "^2.3.8", - "ts-loader": "^8.0.3", - "typedoc": "^0.19.2", - "typedoc-plugin-extras": "^1.1.6", - "typescript": "^3.9.7", - "webpack": "^4.44.2", - "webpack-cli": "^3.3.11" + "@eslint/js": "^10.0.1", + "@typescript-eslint/eslint-plugin": "^8.60.1", + "@typescript-eslint/parser": "^8.60.1", + "eslint": "^10.4.1", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.6", + "eslint-plugin-unused-imports": "^4.4.1", + "globals": "^17.6.0", + "prettier": "^3.8.3", + "raw-loader": "^4.0.2", + "terser-webpack-plugin": "^5.6.1", + "ts-loader": "^9.6.0", + "typedoc": "^0.28.19", + "typedoc-plugin-extras": "^4.0.1", + "typescript": "^6.0.3", + "webpack": "^5.107.2", + "webpack-cli": "^7.0.3" } } diff --git a/src/drawables/lights/light-drawable.ts b/src/drawables/lights/light-drawable.ts index dfa75aa..2fb0534 100644 --- a/src/drawables/lights/light-drawable.ts +++ b/src/drawables/lights/light-drawable.ts @@ -5,7 +5,11 @@ import { Drawable } from '../../main'; export abstract class LightDrawable extends Drawable { protected lightnessRatio = 1; - constructor(public center: vec2, public color: vec3, public intensity: number) { + constructor( + public center: vec2, + public color: vec3, + public intensity: number + ) { super(); } diff --git a/src/drawables/shapes/circle-factory.ts b/src/drawables/shapes/circle-factory.ts index f611bea..b88b5cd 100644 --- a/src/drawables/shapes/circle-factory.ts +++ b/src/drawables/shapes/circle-factory.ts @@ -7,7 +7,10 @@ import { EmptyDrawable } from '../empty-drawable'; * @category Drawable */ class CircleBase extends EmptyDrawable { - constructor(public center: vec2, public radius: number) { + constructor( + public center: vec2, + public radius: number + ) { super(); } } diff --git a/src/drawables/shapes/colorful-circle.ts b/src/drawables/shapes/colorful-circle.ts index a06465e..1a2e8d9 100644 --- a/src/drawables/shapes/colorful-circle.ts +++ b/src/drawables/shapes/colorful-circle.ts @@ -35,7 +35,11 @@ export class ColorfulCircle extends Drawable { 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(); } diff --git a/src/drawables/shapes/hexagon-factory.ts b/src/drawables/shapes/hexagon-factory.ts index ba9ecc9..956b896 100644 --- a/src/drawables/shapes/hexagon-factory.ts +++ b/src/drawables/shapes/hexagon-factory.ts @@ -7,7 +7,10 @@ import { EmptyDrawable } from '../empty-drawable'; * @category Drawable */ class HexagonBase extends EmptyDrawable { - constructor(public center: vec2, public radius: number) { + constructor( + public center: vec2, + public radius: number + ) { super(); } } diff --git a/src/drawables/shapes/meta-circle-factory.ts b/src/drawables/shapes/meta-circle-factory.ts index c716fbd..cf8b548 100644 --- a/src/drawables/shapes/meta-circle-factory.ts +++ b/src/drawables/shapes/meta-circle-factory.ts @@ -8,7 +8,10 @@ import { numberToGlslFloat } from '../../helper/number-to-glsl-float'; * @category Drawable */ class MetaCircleBase extends EmptyDrawable { - constructor(public center: vec2, public radius: number) { + constructor( + public center: vec2, + public radius: number + ) { super(); } } diff --git a/src/drawables/shapes/noisy-polygon-factory.ts b/src/drawables/shapes/noisy-polygon-factory.ts index d1ff919..3ca55fa 100644 --- a/src/drawables/shapes/noisy-polygon-factory.ts +++ b/src/drawables/shapes/noisy-polygon-factory.ts @@ -112,9 +112,9 @@ export const NoisyPolygonFactory = ( }, uniformCountMacroName: `NOISY_POLYGON${vertexCount}_COUNT`, shaderCombinationSteps: [0, 1, 2, 3, 8, 16], - empty: (new NoisyPolygon( + empty: new NoisyPolygon( new Array(vertexCount).fill(vec2.create()) - ) as unknown) as Drawable, + ) as unknown as Drawable, }; public randomOffset = 0; diff --git a/src/drawables/shapes/rotated-rectangle-factory.ts b/src/drawables/shapes/rotated-rectangle-factory.ts index c740ece..1672669 100644 --- a/src/drawables/shapes/rotated-rectangle-factory.ts +++ b/src/drawables/shapes/rotated-rectangle-factory.ts @@ -7,7 +7,11 @@ import { EmptyDrawable } from '../empty-drawable'; * @category Drawable */ 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(); } } diff --git a/src/graphics/graphics-library/parallel-compiler.ts b/src/graphics/graphics-library/parallel-compiler.ts index 268c039..bb170cb 100644 --- a/src/graphics/graphics-library/parallel-compiler.ts +++ b/src/graphics/graphics-library/parallel-compiler.ts @@ -135,7 +135,7 @@ export class ParallelCompiler { private prettyPrintErrorsIfThereAreAny(shader: ShaderWithSource) { try { this.checkShader(shader); - } catch (e) { + } catch (e: any) { for (const match of e.toString().matchAll(/ERROR: 0:(\d+): (.*)$/gm)) { const line = Number.parseInt(match[1]); const error = match[2]; diff --git a/src/graphics/graphics-library/texture/palette-texture.ts b/src/graphics/graphics-library/texture/palette-texture.ts index f98dcc5..faee8a1 100644 --- a/src/graphics/graphics-library/texture/palette-texture.ts +++ b/src/graphics/graphics-library/texture/palette-texture.ts @@ -6,7 +6,10 @@ import { Texture } from './texture'; export class PaletteTexture extends Texture { public static readonly textureUnitId = 2; - constructor(gl: UniversalRenderingContext, private readonly paletteSize: number) { + constructor( + gl: UniversalRenderingContext, + private readonly paletteSize: number + ) { super(gl, PaletteTexture.textureUnitId); } diff --git a/src/graphics/rendering/renderer/renderer-implementation.ts b/src/graphics/rendering/renderer/renderer-implementation.ts index 872b94a..f5b8480 100644 --- a/src/graphics/rendering/renderer/renderer-implementation.ts +++ b/src/graphics/rendering/renderer/renderer-implementation.ts @@ -230,7 +230,8 @@ export class RendererImplementation implements Renderer { this.stopwatch.start(); } else { this.stopwatch.tryGetResults(); - this.gl.insights.gpuRenderTimeInMilliseconds = this.stopwatch.resultsInMilliseconds; + this.gl.insights.gpuRenderTimeInMilliseconds = + this.stopwatch.resultsInMilliseconds; } } diff --git a/src/main.ts b/src/main.ts index c98086c..1fa29db 100644 --- a/src/main.ts +++ b/src/main.ts @@ -23,6 +23,11 @@ declare global { x: number; y: number; } + + interface Iterable { + x: T; + y: T; + } } applyArrayPlugins(); diff --git a/src/run-animation.ts b/src/run-animation.ts index 98c49d3..47a4b0b 100644 --- a/src/run-animation.ts +++ b/src/run-animation.ts @@ -60,7 +60,7 @@ export async function runAnimation( const deltaTimeCalculator = new DeltaTimeCalculator(); let triggerIsOver: () => void; - const isOver = new Promise((resolve) => (triggerIsOver = resolve)); + const isOver = new Promise((resolve) => (triggerIsOver = resolve)); renderer.setRuntimeSettings(settings); const autoscaler = new FpsQualityAutoscaler(renderer); diff --git a/tsconfig.json b/tsconfig.json index b8c22f0..ad5e2cd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,9 @@ "declaration": true, "declarationMap": true, "target": "es5", + "ignoreDeprecations": "6.0", "esModuleInterop": true, + "skipLibCheck": true, "strict": true, "downlevelIteration": true, "moduleResolution": "Node", diff --git a/typedoc.json b/typedoc.json index 5de5e0c..c6072da 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,18 +1,15 @@ { - "mode": "file", "out": "docs", "name": "SDF-2D", - "includeDeclarations": true, + "entryPoints": ["src"], + "entryPointStrategy": "expand", "excludeExternals": true, - "excludeNotExported": true, "excludePrivate": true, - "stripInternal": true, - "media": "media", + "excludeInternal": true, "includeVersion": true, "hideGenerator": true, - "inputFiles": "src", + "readme": "documentation-readme.md", "favicon": "media/favicon.ico", - "ignoreCompilerErrors": true, - "listInvalidSymbolLinks": true, - "readme": "documentation-readme.md" + "skipErrorChecking": true, + "plugin": ["typedoc-plugin-extras"] } diff --git a/webpack.config.js b/webpack.config.js index 2c57e7f..f28bacf 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -38,7 +38,6 @@ module.exports = { usedExports: true, minimizer: [ new TerserJSPlugin({ - sourceMap: true, test: /\.js$/i, }), ], From 974a97c134be86d61accf786cbe9576b6d7900d6 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 6 Jun 2026 15:42:40 +0100 Subject: [PATCH 3/4] Add CI --- .forgejo/workflows/check.yml | 45 +++++++++++++++++ .forgejo/workflows/publish.yml | 88 ++++++++++++++++++++++++++++++++++ package.json | 7 ++- 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 .forgejo/workflows/check.yml create mode 100644 .forgejo/workflows/publish.yml diff --git a/.forgejo/workflows/check.yml b/.forgejo/workflows/check.yml new file mode 100644 index 0000000..b611423 --- /dev/null +++ b/.forgejo/workflows/check.yml @@ -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 diff --git a/.forgejo/workflows/publish.yml b/.forgejo/workflows/publish.yml new file mode 100644 index 0000000..ea3663b --- /dev/null +++ b/.forgejo/workflows/publish.yml @@ -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 }} diff --git a/package.json b/package.json index d8f506b..a03c6f7 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,12 @@ "scripts": { "start": "rm -rf lib/* && webpack --mode development -w", "build": "rm -rf lib/* docs/* && npx typedoc && webpack --mode production", - "lint": "eslint src --fix && prettier --write \"src/**/*.{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", "types": "lib/src/main.d.ts", From 62ee692291e8681defa01e737368a2aa5808da15 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 6 Jun 2026 15:50:38 +0100 Subject: [PATCH 4/4] Add version bump script --- scripts/bump-version.sh | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 scripts/bump-version.sh diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh new file mode 100755 index 0000000..a39bcb6 --- /dev/null +++ b/scripts/bump-version.sh @@ -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"