// // PointRenderer.metal // Metrika // // Author: Simon-Pierre Boucher // Contact: contact@spboucher.ai // Copyright © 2026 Simon-Pierre Boucher. All rights reserved. // // Instanced point-sprite renderer for large scatter plots (CLAUDE.md §7: // custom MTKView renderer past the Swift Charts threshold). One packed // 12-byte vertex per observation; the fragment stage rounds sprites. // #include using namespace metal; struct PackedPoint { packed_float2 position; // data coordinates uchar4 color; }; struct Uniforms { float2 center; // data-space center of the viewport float2 scale; // data → clip scaling (zoom × aspect) float pointSize; }; struct VertexOut { float4 position [[position]]; float pointSize [[point_size]]; float4 color; }; vertex VertexOut scatterVertex( uint vertexID [[vertex_id]], const device PackedPoint *points [[buffer(0)]], constant Uniforms &uniforms [[buffer(1)]] ) { PackedPoint point = points[vertexID]; float2 clip = (float2(point.position) - uniforms.center) * uniforms.scale; VertexOut out; out.position = float4(clip, 0.0, 1.0); out.pointSize = uniforms.pointSize; out.color = float4(point.color) / 255.0; return out; } fragment float4 scatterFragment( VertexOut in [[stage_in]], float2 pointCoord [[point_coord]] ) { float2 offset = pointCoord - 0.5; if (dot(offset, offset) > 0.25) { discard_fragment(); } return in.color; }