Naloga 2: WIP Bezier curves

This commit is contained in:
Gašper Dobrovoljc 2024-11-17 19:09:01 +01:00
parent d825f4e090
commit 0671004a38
No known key found for this signature in database
GPG Key ID: 0E7E037018CFA5A5
11 changed files with 301 additions and 1 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

View File

@ -6,6 +6,7 @@
<JSCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
@ -113,6 +114,7 @@
<TypeScriptCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />

7
.idea/prettier.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
<option name="myRunOnSave" value="true" />
</component>
</project>

View File

@ -1,5 +1,6 @@
{
"semi": true,
"trailingComma": "all",
"tabWidth": 4
"tabWidth": 4,
"singleQuote": true
}

20
naloga_2/index.html Normal file
View File

@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Domača Naloga 2</title>
<script defer src="main.js" type="module"></script>
</head>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
<body>
<canvas></canvas>
</body>
</html>

103
naloga_2/main.js Normal file
View File

@ -0,0 +1,103 @@
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
/** @type {number[][]} */
const points = [];
/**
* @param {number} x
* @param {number} y
*/
function addPoint(x, y) {
if (points.length === 0) {
points.push([x, y]);
return;
}
if (points.length === 1) {
const h1x = lerp(0.25, points[0][0], x);
const h1y = lerp(0.25, points[0][1], y);
const h2x = lerp(0.75, points[0][0], x);
const h2y = lerp(0.75, points[0][1], y);
points.push([h1x, h1y], [h2x, h2y], [x, y]);
return;
}
}
/**
* @param {number} t
* @param {number} a
* @param {number} b
* @returns {number}
*/
function lerp(t, a, b) {
return (1 - t) * a + t * b;
}
/**
* interpolates the points
* @param {number} t
* @param {number[]} p array of points
*/
function curve(t, p) {
if (p.length === 1) return p[0];
return lerp(t, curve(t, p.slice(0, -1)), curve(t, p.slice(1)));
}
function drawCircles() {
for (const [x, y] of points) {
context.beginPath();
context.ellipse(x, y, 10, 10, 0, 0, Math.PI * 2);
context.stroke();
}
}
function drawLines() {
for (let i = 0; i < points.length - 3; i += 3) {
const p1 = points[i];
const p2 = points[i + 1];
const p3 = points[i + 2];
const p4 = points[i + 3];
context.beginPath();
context.moveTo(p1[0], p1[1]);
context.lineTo(p2[0], p2[1]);
context.moveTo(p3[0], p3[1]);
context.lineTo(p4[0], p4[1]);
context.moveTo(p1[0], p1[1]);
for (let t = 0; t <= 1; t += 0.01) {
const x = curve(t, [p1[0], p2[0], p3[0], p4[0]]);
const y = curve(t, [p1[1], p2[1], p3[1], p4[1]]);
context.lineTo(x, y);
}
context.stroke();
}
}
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
if (points.length <= 0) return;
drawCircles();
drawLines();
}
function resizeCanvas() {
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
draw();
}
window.addEventListener('mousedown', (e) => {
addPoint(e.pageX, e.pageY);
draw();
});
window.addEventListener('resize', resizeCanvas);
resizeCanvas();

14
package.json Normal file
View File

@ -0,0 +1,14 @@
{
"name": "rg",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@webgpu/types": "^0.1.50",
"prettier": "^3.3.3"
}
}

32
pnpm-lock.yaml generated Normal file
View File

@ -0,0 +1,32 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
'@webgpu/types':
specifier: ^0.1.50
version: 0.1.50
prettier:
specifier: ^3.3.3
version: 3.3.3
packages:
'@webgpu/types@0.1.50':
resolution: {integrity: sha512-GjG3CQV7SyWk/lEXqFPuKchRPHIBbD317Gj8NUqqB+UOnQlOYtjGLCTRIWzO9Ta698LVzlBCSE9XKqBSWpIDmg==}
prettier@3.3.3:
resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==}
engines: {node: '>=14'}
hasBin: true
snapshots:
'@webgpu/types@0.1.50': {}
prettier@3.3.3: {}

13
vaja_1/index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Vaja 1</title>
<script type="module" src="main.js"></script>
</head>
<body>
<canvas width="512" height="512"></canvas>
</body>
</html>

90
vaja_1/main.js Normal file
View File

@ -0,0 +1,90 @@
// Initialize WebGPU
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const format = 'rgba8unorm';
context.configure({ device, format });
// Create shaders
const code = await fetch('shader.wgsl').then((res) => res.text());
const module = device.createShaderModule({ code });
// Create vertex buffer layouts
/** @type {GPUVertexBufferLayout} */
const vertexBufferLayout = {
arrayStride: 32,
attributes: [
{
offset: 0,
format: 'float32x4',
shaderLocation: 0,
},
{
offset: 16,
format: 'float32x4',
shaderLocation: 1,
},
],
};
// Create render pipeline
const pipeline = device.createRenderPipeline({
vertex: {
module,
buffers: [vertexBufferLayout],
},
fragment: {
module,
targets: [{ format }],
},
layout: 'auto',
});
// Create vertex buffer
const vertices = new Float32Array([
0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1,
1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1,
]);
const vertexBuffer = device.createBuffer({
size: vertices.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(vertexBuffer, 0, vertices);
const indices = new Uint32Array([0, 1, 2, 1, 2, 3]);
const indexBuffer = device.createBuffer({
size: indices.byteLength,
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(indexBuffer, 0, indices);
// Create command encoder
const commandEncoder = device.createCommandEncoder();
// Encode render pass
const renderPass = commandEncoder.beginRenderPass({
colorAttachments: [
{
view: context.getCurrentTexture().createView(),
loadOp: 'clear',
clearValue: [1, 1, 1, 1],
storeOp: 'store',
},
],
});
renderPass.setPipeline(pipeline);
renderPass.setVertexBuffer(0, vertexBuffer);
renderPass.setIndexBuffer(indexBuffer, 'uint32');
renderPass.drawIndexed(indices.length);
renderPass.end();
const commandBuffer = commandEncoder.finish();
// Send commands to GPU
device.queue.submit([commandBuffer]);

17
vaja_1/shader.wgsl Normal file
View File

@ -0,0 +1,17 @@
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) color: vec4f,
}
@vertex
fn vertex(@location(0) position: vec4f, @location(1) color: vec4f) -> VertexOutput {
var output: VertexOutput;
output.position = position;
output.color = color;
return output;
}
@fragment
fn fragment(@location(0) color: vec4f) -> @location(0) vec4f {
return color;
}