@solidrt/2d 0.0.53 → 0.0.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/package.json +2 -2
- package/src/components.tsx +11 -3
- package/src/oversample.ts +12 -11
package/AGENTS.md
CHANGED
|
@@ -224,7 +224,8 @@ is flat. Event x/y are layer pixels with the camera undone.
|
|
|
224
224
|
(nearest inside keeps blocks square), the linear composite spreads the
|
|
225
225
|
fraction over one device pixel at block edges. The components pick n
|
|
226
226
|
every layout from their leaf's window box (`getBoundingBoxViewport` x
|
|
227
|
-
`displayScale()`, which composes designSize fits and camera zoom)
|
|
227
|
+
`displayScale()`, which composes designSize fits and camera zoom), within
|
|
228
|
+
a budget of the window's own device pixel count; the
|
|
228
229
|
primitives default to 1 and take `{ oversample }` / `setOversample(n)`
|
|
229
230
|
- with `output` on `<SpriteLayer>` there is no built-in leaf, so set it
|
|
230
231
|
yourself. Never fix a shimmer by snapping the fit to an integer: the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/2d",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.54",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"funding": "https://github.com/sponsors/wellawaretech",
|
|
6
6
|
"author": "Antoine van Wel",
|
|
@@ -16,6 +16,6 @@
|
|
|
16
16
|
],
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"@solidjs/signals": "2.0.0-rc.3",
|
|
19
|
-
"@solidrt/core": "0.0.
|
|
19
|
+
"@solidrt/core": "0.0.54"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/src/components.tsx
CHANGED
|
@@ -6,13 +6,21 @@
|
|
|
6
6
|
// sprite with `ref` and call setSprite from onFrame - signals carry
|
|
7
7
|
// structure and slow state, per-frame motion goes straight to the layer.
|
|
8
8
|
// The same split, with the same reasoning, as @solidrt/3d's components.
|
|
9
|
-
import { createContext, createEffect, createSignal, displayScale, For, getBoundingBoxViewport, onCleanup, onLayout, untrack, useContext } from "@solidrt/core"
|
|
9
|
+
import { createContext, createEffect, createSignal, displayScale, For, getBoundingBoxViewport, onCleanup, onLayout, untrack, useContext, windowSize } from "@solidrt/core"
|
|
10
10
|
import type { Element, ParentComponent, TextureId, VoidComponent } from "@solidrt/core"
|
|
11
11
|
import type { FilterMode } from "@solidrt/core/gpu"
|
|
12
12
|
import { addGroup, addSprite, createSpriteLayer, removeGroup, removeSprite, setGroup, setGroupTransition, setSprite, setSpriteTransition } from "./layer.ts"
|
|
13
13
|
import type { NodeTransition } from "flux:spatial"
|
|
14
14
|
import type { CameraUpdate, Sprite as SpriteHandle, SpriteGroup, SpriteLayer as LayerHandle, SpriteOptions, SpritePointerEvent, TransitionEndEvent } from "./layer.ts"
|
|
15
15
|
import { fitOversample } from "./oversample.ts"
|
|
16
|
+
|
|
17
|
+
// The window's device pixel count: the texel budget an auto-picked
|
|
18
|
+
// oversample target stays within (see fitOversample).
|
|
19
|
+
function windowTexels(): number {
|
|
20
|
+
let win = windowSize()
|
|
21
|
+
let scale = displayScale()
|
|
22
|
+
return win.width * scale * (win.height * scale)
|
|
23
|
+
}
|
|
16
24
|
import { createTileLayer } from "./tiles.ts"
|
|
17
25
|
import type { TileChunk, TileLayer as TileLayerHandle } from "./tiles.ts"
|
|
18
26
|
|
|
@@ -117,7 +125,7 @@ export let SpriteLayer: ParentComponent<SpriteLayerProps> = props => {
|
|
|
117
125
|
let box = getBoundingBoxViewport(leaf)
|
|
118
126
|
if (!box) return
|
|
119
127
|
let scale = displayScale() * Math.max(box.width / props.width, box.height / props.height)
|
|
120
|
-
layer.setOversample(fitOversample(scale, props.width, props.height))
|
|
128
|
+
layer.setOversample(fitOversample(scale, props.width, props.height, windowTexels()))
|
|
121
129
|
}
|
|
122
130
|
onLayout(pick)
|
|
123
131
|
createEffect(() => displayScale(), pick)
|
|
@@ -316,7 +324,7 @@ export let TileLayer: VoidComponent<TileLayerProps> = props => {
|
|
|
316
324
|
let box = getBoundingBoxViewport(world)
|
|
317
325
|
if (!box) return
|
|
318
326
|
let scale = displayScale() * Math.max(box.width / layer.width, box.height / layer.height)
|
|
319
|
-
layer.setOversample(fitOversample(scale, layer.chunkW, layer.chunkH))
|
|
327
|
+
layer.setOversample(fitOversample(scale, layer.chunkW, layer.chunkH, windowTexels()))
|
|
320
328
|
}
|
|
321
329
|
onLayout(pick)
|
|
322
330
|
createEffect(() => displayScale(), pick)
|
package/src/oversample.ts
CHANGED
|
@@ -6,11 +6,6 @@
|
|
|
6
6
|
// instead of snapping to uneven widths). okf/backlog/2d-layer-display-scale.md.
|
|
7
7
|
import { limits } from "@solidrt/core/gpu"
|
|
8
8
|
|
|
9
|
-
/** Ceiling on an auto-picked oversample: a small layer in a huge box would
|
|
10
|
-
* otherwise ask for a target the size of the window squared. Eight covers a
|
|
11
|
-
* 4x design fit on a 2x display. */
|
|
12
|
-
export const MAX_OVERSAMPLE = 8
|
|
13
|
-
|
|
14
9
|
/** Tolerance under a whole number when rounding a display scale up, so a
|
|
15
10
|
* scale that is 3 up to float noise picks 3, not 4. */
|
|
16
11
|
const FIT_EPSILON = 1e-6
|
|
@@ -28,12 +23,18 @@ export function checkOversample(verb: string, n: number, width: number, height:
|
|
|
28
23
|
|
|
29
24
|
/**
|
|
30
25
|
* The oversample for a layer shown at `scale` device pixels per layer pixel:
|
|
31
|
-
* the ceiling of the scale, so no device pixel goes without a target texel
|
|
32
|
-
*
|
|
33
|
-
*
|
|
26
|
+
* the ceiling of the scale, so no device pixel goes without a target texel.
|
|
27
|
+
* Bounded by `budget` texels for the `targetW x targetH` target (the
|
|
28
|
+
* window's own device pixel count: a target beyond it buys nothing on
|
|
29
|
+
* screen, and a layer stretched far past the window inside a scroller must
|
|
30
|
+
* not ask for one) and by what the target can grow to on this device.
|
|
34
31
|
*/
|
|
35
|
-
export function fitOversample(scale: number, targetW: number, targetH: number): number {
|
|
36
|
-
|
|
32
|
+
export function fitOversample(scale: number, targetW: number, targetH: number, budget: number): number {
|
|
33
|
+
// The budget bounds the scale, not the rounded factor: the ceiling may
|
|
34
|
+
// still round a fit that fills the window up by one (a 320 x 200 design
|
|
35
|
+
// on a 2560 x 1440 panel fits at 7.2 and needs 8).
|
|
36
|
+
let byBudget = Math.sqrt(budget / (targetW * targetH))
|
|
37
|
+
let n = Math.max(1, Math.ceil(Math.min(scale, byBudget) - FIT_EPSILON))
|
|
37
38
|
let byDevice = Math.floor(limits.maxTextureSize / Math.max(targetW, targetH))
|
|
38
|
-
return Math.max(1, Math.min(n,
|
|
39
|
+
return Math.max(1, Math.min(n, byDevice))
|
|
39
40
|
}
|