@bigmistqke/view.gl 0.1.0

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/dev/grid.ts ADDED
@@ -0,0 +1,223 @@
1
+ import { view } from 'view.gl'
2
+ import { attribute, compile, glsl, uniform } from 'view.gl/tag'
3
+ import { createElement, cursor } from './utils'
4
+
5
+ const canvas = createElement('canvas', { width: window.innerWidth, height: window.innerHeight })
6
+ const gl = canvas.getContext('webgl2', { antialias: false })!
7
+
8
+ if (!gl) {
9
+ throw new Error('WebGL not supported')
10
+ }
11
+
12
+ const state = {
13
+ position: { x: 0, y: 0 },
14
+ scale: 1,
15
+ elements: [] as Array<ReturnType<typeof createGridElement>>,
16
+ }
17
+
18
+ const container = createElement('div')
19
+ state.elements.push(createGridElement({ x: 100, y: 100 }))
20
+
21
+ /**********************************************************************************/
22
+ /* */
23
+ /* Utils */
24
+ /* */
25
+ /**********************************************************************************/
26
+
27
+ function createGridElement(position = { x: 0, y: 0 }) {
28
+ position = {
29
+ x: (position.x - state.position.x) / state.scale,
30
+ y: (position.y - state.position.y) / state.scale,
31
+ }
32
+
33
+ const element = createElement('div', {
34
+ innerText: 'HALLO WORLD',
35
+ style: `position: fixed;
36
+ z-index: 10;
37
+ left: 0px;
38
+ top: 0px;
39
+ transform-origin: top left;
40
+ background: white;
41
+ padding: 2px;
42
+ border-radius: 2px;`,
43
+ parentElement: container,
44
+ })
45
+
46
+ function update() {
47
+ element.style.transform = `translate3d(${state.position.x + position.x * state.scale}px,${
48
+ state.position.y + position.y * state.scale
49
+ }px,0) scale(${state.scale})`
50
+ }
51
+ update()
52
+
53
+ return {
54
+ element,
55
+ position,
56
+ update,
57
+ }
58
+ }
59
+
60
+ /**********************************************************************************/
61
+ /* */
62
+ /* Event Handlers */
63
+ /* */
64
+ /**********************************************************************************/
65
+
66
+ canvas.addEventListener('pointerdown', event => {
67
+ cursor(event, event => {
68
+ state.position.x -= event.deltaX
69
+ state.position.y -= event.deltaY
70
+ render()
71
+ })
72
+ })
73
+
74
+ canvas.addEventListener('dblclick', event => {
75
+ state.elements.push(
76
+ createGridElement({
77
+ x: event.clientX,
78
+ y: event.clientY,
79
+ }),
80
+ )
81
+ })
82
+
83
+ window.addEventListener(
84
+ 'wheel',
85
+ event => {
86
+ const rect = canvas.getBoundingClientRect()
87
+ const mouseX = event.clientX - rect.left
88
+ const mouseY = event.clientY - rect.top
89
+
90
+ const scaleFactor = event.deltaY > 0 ? 0.95 : 1.05
91
+ const oldScale = state.scale
92
+ const newScale = Math.max(0.1, Math.min(10, oldScale * scaleFactor))
93
+
94
+ // Calculate the grid coordinate under the mouse cursor before scaling
95
+ const gridX = (mouseX - state.position.x) / oldScale
96
+ const gridY = (mouseY - state.position.y) / oldScale
97
+
98
+ // Update scale
99
+ state.scale = newScale
100
+
101
+ // Adjust offset to keep the grid coordinate under the cursor
102
+ state.position.x = mouseX - gridX * newScale
103
+ state.position.y = mouseY - gridY * newScale
104
+
105
+ render()
106
+ },
107
+ { passive: true },
108
+ )
109
+
110
+ window.addEventListener('resize', render)
111
+
112
+ /**********************************************************************************/
113
+ /* */
114
+ /* Shaders */
115
+ /* */
116
+ /**********************************************************************************/
117
+
118
+ // Vertex shader - maps grid to screen space anchored at top-left
119
+ const vertex = glsl`#version 300 es
120
+ ${attribute.vec2('a_vertex')}
121
+ ${uniform.vec2('u_screenSize')}
122
+ ${uniform.vec2('u_pos')}
123
+ ${uniform.float('u_scale')}
124
+ out vec2 v_gridCoord;
125
+
126
+ // Direct mapping: (-1,-1) is bottom-left, (1,1) is top-right in clip space
127
+ // We want (0,0) to be top-left in pixel space
128
+ vec2 project(vec2 pos, vec2 size){
129
+ return vec2(
130
+ (pos.x + 1.0),
131
+ (1.0 - pos.y)
132
+ ) * size * 0.5;
133
+ }
134
+
135
+ void main() {
136
+ vec2 pixelPos = project(a_vertex, u_screenSize);
137
+
138
+ // Apply offset and scale to get grid coordinates
139
+ v_gridCoord = (pixelPos - u_pos) / u_scale;
140
+
141
+ gl_Position = vec4(a_vertex, 0, 1);
142
+ }`
143
+
144
+ // Fragment shader for rendering the grid
145
+ const renderFragment = glsl`#version 300 es
146
+ precision highp float;
147
+ in vec2 v_gridCoord;
148
+ out vec4 fragColor;
149
+ ${uniform.float('u_thickness')}
150
+
151
+ float grid(vec2 coord, float thickness) {
152
+ vec2 cell = mod(coord, 100.0);
153
+ vec2 derivative = fwidth(coord);
154
+
155
+ // Convert thickness from screen pixels to grid space
156
+ vec2 screenThickness = derivative * thickness;
157
+
158
+ // Ensure minimum thickness to prevent lines from disappearing
159
+ vec2 minThickness = max(screenThickness, derivative * 0.5);
160
+
161
+ // Create hard edges by checking if we're within thickness of a grid line
162
+ float lineX = step(cell.x, minThickness.x) + step(100.0 - minThickness.x, cell.x);
163
+ float lineY = step(cell.y, minThickness.y) + step(100.0 - minThickness.y, cell.y);
164
+
165
+ // Clamp to 0-1 range and return maximum (union of lines)
166
+ return max(min(lineX, 1.0), min(lineY, 1.0));
167
+ }
168
+
169
+ void main() {
170
+ // Create grid pattern with adjustable thickness
171
+ float gridPattern = grid(v_gridCoord, u_thickness);
172
+
173
+ // Grid colors
174
+ vec3 backgroundColor = vec3(0.1, 0.1, 0.15);
175
+ vec3 gridColor = vec3(0.3, 0.3, 0.4);
176
+ vec3 color = mix(backgroundColor, gridColor, gridPattern);
177
+
178
+ fragColor = vec4(color, 1.0);
179
+ }`
180
+
181
+ /**********************************************************************************/
182
+ /* */
183
+ /* Create Program */
184
+ /* */
185
+ /**********************************************************************************/
186
+
187
+ const { program, schema } = compile(gl, vertex, renderFragment)
188
+ const { attributes, uniforms } = view(gl, program, schema)
189
+
190
+ // prettier-ignore
191
+ attributes.a_vertex.set(new Float32Array([
192
+ -1, -1, // bottom-left
193
+ 1, -1, // bottom-right
194
+ -1, 1, // top-left
195
+ -1, 1, // top-left
196
+ 1, -1, // bottom-right
197
+ 1, 1 // top-right
198
+ ]))
199
+
200
+ function render() {
201
+ gl.useProgram(program)
202
+
203
+ // Update canvas size on resize
204
+ canvas.width = window.innerWidth
205
+ canvas.height = window.innerHeight
206
+
207
+ gl.viewport(0, 0, canvas.width, canvas.height)
208
+ gl.clearColor(0, 0, 0, 1)
209
+ gl.clear(gl.COLOR_BUFFER_BIT)
210
+
211
+ attributes.a_vertex.bind()
212
+
213
+ // Pass grid parameters to shaders
214
+ uniforms.u_screenSize.set(canvas.width, canvas.height)
215
+ uniforms.u_pos.set(state.position.x, state.position.y)
216
+ uniforms.u_scale.set(state.scale)
217
+ uniforms.u_thickness.set(1)
218
+
219
+ state.elements.forEach(element => element.update())
220
+
221
+ gl.drawArrays(gl.TRIANGLES, 0, 6)
222
+ }
223
+ render()
package/dev/index.html ADDED
@@ -0,0 +1,20 @@
1
+ <html>
2
+ <head>
3
+ <script src="./bezier.ts" type="module"></script>
4
+ <style>
5
+ body {
6
+ display: flex;
7
+ justify-content: center;
8
+ align-items: center;
9
+ margin: 0px;
10
+ height: 100vh;
11
+ overscroll-behavior-y: none;
12
+ }
13
+ canvas {
14
+ margin: auto;
15
+ image-rendering: pixelated;
16
+ }
17
+ </style>
18
+ </head>
19
+ <body></body>
20
+ </html>
package/dev/index.ts ADDED
@@ -0,0 +1,94 @@
1
+ import { view } from 'view.gl'
2
+ import { attribute, compile, glsl, interleave, uniform } from 'view.gl/tag'
3
+
4
+ const canvas = document.createElement('canvas')
5
+ canvas.width = 800
6
+ canvas.height = 600
7
+ document.body.append(canvas)
8
+
9
+ const gl = canvas.getContext('webgl2')!
10
+ gl.viewport(0, 0, canvas.width, canvas.height)
11
+
12
+ const vertex = glsl`
13
+ precision mediump float;
14
+
15
+ ${interleave('instancedData', [attribute.vec2('a_pos'), attribute.vec3('a_color')], {
16
+ instanced: true,
17
+ })}
18
+ ${attribute.vec2('a_vertex')}
19
+ ${uniform.float('u_time')}
20
+
21
+ varying vec3 v_color;
22
+
23
+ void main() {
24
+ float s = sin(u_time);
25
+ float c = cos(u_time);
26
+ mat2 rotation = mat2(
27
+ c, -s,
28
+ s, c
29
+ );
30
+ vec2 position = a_pos + a_vertex * rotation;
31
+ gl_Position = vec4(position, 0.0, 1.0);
32
+
33
+ v_color = a_color;
34
+ }`
35
+
36
+ const fragment = glsl`
37
+ precision mediump float;
38
+
39
+ varying vec3 v_color;
40
+
41
+ void main() {
42
+ gl_FragColor = vec4(v_color, 1.0);
43
+ }`
44
+
45
+ const { program, schema } = compile(gl, vertex, fragment)
46
+ const {
47
+ attributes: { a_vertex },
48
+ uniforms: { u_time },
49
+ interleavedAttributes: { instancedData },
50
+ } = view(gl, program, schema)
51
+
52
+ // Upload interleaved buffer
53
+ instancedData.set(
54
+ new Float32Array(
55
+ (function* () {
56
+ for (let i = 0; i < 100; i++) {
57
+ // Position
58
+ yield (Math.random() - 0.5) * 2
59
+ yield (Math.random() - 0.5) * 2
60
+
61
+ // Color
62
+ yield Math.random()
63
+ yield Math.random()
64
+ yield Math.random()
65
+ }
66
+ })(),
67
+ ),
68
+ )
69
+ instancedData.bind()
70
+
71
+ // Create triangle vertex buffer
72
+ a_vertex
73
+ .set(
74
+ new Float32Array([
75
+ // top
76
+ 0.0, 1.0,
77
+ // bottom left
78
+ -1.0, -1.0,
79
+ // bottom right
80
+ 1.0, -1.0,
81
+ ]),
82
+ )
83
+ .bind()
84
+
85
+ gl.useProgram(program)
86
+
87
+ requestAnimationFrame(function draw(delta: number) {
88
+ requestAnimationFrame(draw)
89
+
90
+ // Set uniform
91
+ u_time.set(delta / 1_000)
92
+
93
+ gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, 100)
94
+ })