Skip to content

Instantly share code, notes, and snippets.

@ingenieroariel
Created June 20, 2024 21:59
Show Gist options
  • Select an option

  • Save ingenieroariel/c6366fd4c54f42e73a9d8d97b8e36d5f to your computer and use it in GitHub Desktop.

Select an option

Save ingenieroariel/c6366fd4c54f42e73a9d8d97b8e36d5f to your computer and use it in GitHub Desktop.

Revisions

  1. ingenieroariel created this gist Jun 20, 2024.
    351 changes: 351 additions & 0 deletions ic-webpgu.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,351 @@
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Improved Interaction Combinators Visualization</title>
    <style>
    canvas {
    border: 1px solid black;
    }
    #controls {
    margin-top: 10px;
    }
    button {
    margin-right: 10px;
    }
    #debug {
    margin-top: 10px;
    font-family: monospace;
    white-space: pre;
    }
    </style>
    </head>
    <body>
    <canvas id="icCanvas" width="800" height="600"></canvas>
    <div id="controls">
    <button id="stepBtn">Step</button>
    <button id="resetBtn">Reset</button>
    </div>
    <div id="debug"></div>
    <script type="module">
    const canvas = document.getElementById('icCanvas');
    const adapter = await navigator.gpu.requestAdapter();
    const device = await adapter.requestDevice();
    const context = canvas.getContext('webgpu');

    const format = navigator.gpu.getPreferredCanvasFormat();
    context.configure({
    device: device,
    format: format,
    });

    const shader = device.createShaderModule({
    code: `
    struct VertexOutput {
    @builtin(position) position: vec4f,
    @location(0) color: vec4f,
    };
    @vertex
    fn vertexMain(@location(0) position: vec2f,
    @location(1) color: vec4f) -> VertexOutput {
    var output: VertexOutput;
    output.position = vec4f(position, 0.0, 1.0);
    output.color = color;
    return output;
    }
    @fragment
    fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
    return input.color;
    }
    `
    });

    const pipeline = device.createRenderPipeline({
    layout: 'auto',
    vertex: {
    module: shader,
    entryPoint: 'vertexMain',
    buffers: [{
    arrayStride: 24,
    attributes: [
    { shaderLocation: 0, offset: 0, format: 'float32x2' },
    { shaderLocation: 1, offset: 8, format: 'float32x4' },
    ],
    }],
    },
    fragment: {
    module: shader,
    entryPoint: 'fragmentMain',
    targets: [{ format: format }],
    },
    });

    const CellType = {
    CONSTRUCTOR: 0,
    DUPLICATOR: 1,
    ERASER: 2,
    };

    class Cell {
    constructor(type, x, y) {
    this.type = type;
    this.x = x;
    this.y = y;
    this.ports = [null, null, null]; // principal, aux1, aux2
    }
    }

    class Connection {
    constructor(fromCell, fromPort, toCell, toPort) {
    this.fromCell = fromCell;
    this.fromPort = fromPort;
    this.toCell = toCell;
    this.toPort = toPort;
    }
    }

    let cells = [];
    let connections = [];

    function createInitialConfiguration() {
    cells = [
    new Cell(CellType.CONSTRUCTOR, -0.5, 0.5),
    new Cell(CellType.CONSTRUCTOR, 0.5, 0.5),
    new Cell(CellType.ERASER, 0, -0.5)
    ];

    connections = [
    new Connection(cells[0], 0, cells[1], 0),
    new Connection(cells[0], 1, cells[2], 0),
    new Connection(cells[1], 2, cells[2], 0)
    ];

    updateCellConnections();
    }

    function updateCellConnections() {
    for (let cell of cells) {
    cell.ports = [null, null, null];
    }

    for (let conn of connections) {
    conn.fromCell.ports[conn.fromPort] = { cell: conn.toCell, port: conn.toPort };
    conn.toCell.ports[conn.toPort] = { cell: conn.fromCell, port: conn.fromPort };
    }
    }

    function step() {
    for (let i = 0; i < cells.length; i++) {
    for (let j = i + 1; j < cells.length; j++) {
    if (areConnectedByPrincipalPorts(cells[i], cells[j])) {
    applyInteractionRule(cells[i], cells[j]);
    return true;
    }
    }
    }
    return false;
    }

    function areConnectedByPrincipalPorts(cell1, cell2) {
    return cell1.ports[0] && cell1.ports[0].cell === cell2 && cell1.ports[0].port === 0;
    }

    function applyInteractionRule(cell1, cell2) {
    if (cell1.type === CellType.CONSTRUCTOR && cell2.type === CellType.CONSTRUCTOR) {
    // γγ rule
    let aux1Cell1 = cell1.ports[1] ? cell1.ports[1].cell : null;
    let aux1Port1 = cell1.ports[1] ? cell1.ports[1].port : null;
    let aux2Cell1 = cell1.ports[2] ? cell1.ports[2].cell : null;
    let aux2Port1 = cell1.ports[2] ? cell1.ports[2].port : null;

    let aux1Cell2 = cell2.ports[1] ? cell2.ports[1].cell : null;
    let aux1Port2 = cell2.ports[1] ? cell2.ports[1].port : null;
    let aux2Cell2 = cell2.ports[2] ? cell2.ports[2].cell : null;
    let aux2Port2 = cell2.ports[2] ? cell2.ports[2].port : null;

    // Connect the auxiliary ports of the cells that were connected to the constructors
    if (aux1Cell1 && aux1Cell2) {
    connect(aux1Cell1, aux1Port1, aux1Cell2, aux1Port2);
    }
    if (aux2Cell1 && aux2Cell2) {
    connect(aux2Cell1, aux2Port1, aux2Cell2, aux2Port2);
    }

    // Remove the interacting cells
    cells = cells.filter(c => c !== cell1 && c !== cell2);
    connections = connections.filter(conn =>
    conn.fromCell !== cell1 && conn.fromCell !== cell2 &&
    conn.toCell !== cell1 && conn.toCell !== cell2
    );

    updateCellConnections();
    }
    // Add other rules as needed
    }

    function connect(cell1, port1, cell2, port2) {
    if (!cell1 || !cell2) return;

    // Remove any existing connections for these ports
    connections = connections.filter(conn =>
    !(conn.fromCell === cell1 && conn.fromPort === port1) &&
    !(conn.fromCell === cell2 && conn.fromPort === port2) &&
    !(conn.toCell === cell1 && conn.toPort === port1) &&
    !(conn.toCell === cell2 && conn.toPort === port2)
    );

    // Add the new connection
    connections.push(new Connection(cell1, port1, cell2, port2));
    }

    function render() {
    const vertexData = [];
    const indexData = [];
    let vertexCount = 0;

    // Operation: Render cells
    for (let cell of cells) {
    const cellSize = 0.05;
    const x = cell.x;
    const y = cell.y;

    let color;
    switch (cell.type) {
    case CellType.CONSTRUCTOR:
    color = [1, 0, 0, 1]; // Red
    break;
    case CellType.DUPLICATOR:
    color = [0, 1, 0, 1]; // Green
    break;
    case CellType.ERASER:
    color = [0, 0, 1, 1]; // Blue
    break;
    }

    vertexData.push(
    x - cellSize, y - cellSize, ...color,
    x + cellSize, y - cellSize, ...color,
    x + cellSize, y + cellSize, ...color,
    x - cellSize, y + cellSize, ...color
    );

    indexData.push(
    vertexCount, vertexCount + 1, vertexCount + 2,
    vertexCount, vertexCount + 2, vertexCount + 3
    );

    vertexCount += 4;
    }

    // Operation: Render connections
    for (let conn of connections) {
    const fromX = conn.fromCell.x;
    const fromY = conn.fromCell.y;
    const toX = conn.toCell.x;
    const toY = conn.toCell.y;

    const lineWidth = 0.005;
    const dx = toX - fromX;
    const dy = toY - fromY;
    const length = Math.sqrt(dx * dx + dy * dy);
    const nx = -dy / length * lineWidth;
    const ny = dx / length * lineWidth;

    vertexData.push(
    fromX + nx, fromY + ny, 0, 0, 0, 1,
    fromX - nx, fromY - ny, 0, 0, 0, 1,
    toX + nx, toY + ny, 0, 0, 0, 1,
    toX - nx, toY - ny, 0, 0, 0, 1
    );

    indexData.push(
    vertexCount, vertexCount + 1, vertexCount + 2,
    vertexCount + 1, vertexCount + 2, vertexCount + 3
    );

    vertexCount += 4;
    }

    // Operation: Create and populate buffers
    const vertexBuffer = device.createBuffer({
    size: vertexData.length * 4,
    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
    });

    const indexBuffer = device.createBuffer({
    size: indexData.length * 2,
    usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
    });

    device.queue.writeBuffer(vertexBuffer, 0, new Float32Array(vertexData));
    device.queue.writeBuffer(indexBuffer, 0, new Uint16Array(indexData));

    // Operation: Encode and submit commands
    const commandEncoder = device.createCommandEncoder();
    const textureView = context.getCurrentTexture().createView();

    const renderPass = commandEncoder.beginRenderPass({
    colorAttachments: [{
    view: textureView,
    clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
    loadOp: 'clear',
    storeOp: 'store',
    }],
    });

    renderPass.setPipeline(pipeline);
    renderPass.setVertexBuffer(0, vertexBuffer);
    renderPass.setIndexBuffer(indexBuffer, 'uint16');
    renderPass.drawIndexed(indexData.length);
    renderPass.end();

    device.queue.submit([commandEncoder.finish()]);
    }

    function debugPrint() {
    let output = "Cells:\n";
    cells.forEach((cell, index) => {
    output += `Cell ${index}: Type=${cell.type}, Pos=(${cell.x}, ${cell.y})\n`;
    cell.ports.forEach((port, portIndex) => {
    if (port) {
    output += ` Port ${portIndex}: Connected to Cell ${cells.indexOf(port.cell)}, Port ${port.port}\n`;
    } else {
    output += ` Port ${portIndex}: Not connected\n`;
    }
    });
    });
    output += "\nConnections:\n";
    connections.forEach((conn, index) => {
    output += `Connection ${index}: Cell ${cells.indexOf(conn.fromCell)} Port ${conn.fromPort} -> Cell ${cells.indexOf(conn.toCell)} Port ${conn.toPort}\n`;
    });
    document.getElementById('debug').textContent = output;
    }

    createInitialConfiguration();
    render();
    debugPrint();

    const stepBtn = document.getElementById('stepBtn');
    const resetBtn = document.getElementById('resetBtn');

    stepBtn.addEventListener('click', () => {
    if (step()) {
    render();
    debugPrint();
    console.log("Step applied successfully.");
    } else {
    console.log("No more reductions possible.");
    }
    });

    resetBtn.addEventListener('click', () => {
    createInitialConfiguration();
    render();
    debugPrint();
    });
    </script>
    </body>
    </html>