Another project
1struct Frame {
2 clip_from_world: mat4x4<f32>,
3 stroke_color: vec4<f32>,
4 construction: vec4<f32>,
5 pixels_per_mm: f32,
6 dash_period_px: f32,
7 dash_on_ratio: f32,
8 _pad: f32,
9};
10
11struct Instance {
12 @location(0) center: vec2<f32>,
13 @location(1) radius_mm: f32,
14 @location(2) half_width_px: f32,
15 @location(3) start_rad: f32,
16 @location(4) sweep_rad: f32,
17 @location(5) aabb_min_mm: vec2<f32>,
18 @location(6) aabb_max_mm: vec2<f32>,
19 @location(7) pick_id: u32,
20 @location(8) style_bits: u32,
21};
22
23struct VsOut {
24 @builtin(position) clip: vec4<f32>,
25 @location(0) local_px: vec2<f32>,
26 @location(1) radius_px: f32,
27 @location(2) half_width_px: f32,
28 @location(3) start_rad: f32,
29 @location(4) sweep_rad: f32,
30 @location(5) @interpolate(flat) style_bits: u32,
31 @location(6) @interpolate(flat) pick_id: u32,
32};
33
34struct FsOut {
35 @location(0) color: vec4<f32>,
36 @location(1) pick_id: u32,
37};
38
39@group(0) @binding(0) var<uniform> u: Frame;
40
41const CORNERS: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
42 vec2<f32>(0.0, 0.0),
43 vec2<f32>(1.0, 0.0),
44 vec2<f32>(0.0, 1.0),
45 vec2<f32>(0.0, 1.0),
46 vec2<f32>(1.0, 0.0),
47 vec2<f32>(1.0, 1.0),
48);
49
50const TWO_PI: f32 = 6.283185307179586;
51const CONSTRUCTION_BIT: u32 = 1u;
52
53@vertex
54fn vs(@builtin(vertex_index) vid: u32, inst: Instance) -> VsOut {
55 var corners = CORNERS;
56 let corner = corners[vid];
57 let expand_mm = (inst.half_width_px + 1.0) / u.pixels_per_mm;
58 let bbox_min = inst.aabb_min_mm - vec2<f32>(expand_mm, expand_mm);
59 let bbox_max = inst.aabb_max_mm + vec2<f32>(expand_mm, expand_mm);
60 let offset_mm = mix(bbox_min, bbox_max, corner);
61 let world_mm = inst.center + offset_mm;
62 let clip = u.clip_from_world * vec4<f32>(world_mm, 0.0, 1.0);
63
64 var out: VsOut;
65 out.clip = clip;
66 out.local_px = offset_mm * u.pixels_per_mm;
67 out.radius_px = inst.radius_mm * u.pixels_per_mm;
68 out.half_width_px = inst.half_width_px;
69 out.start_rad = inst.start_rad;
70 out.sweep_rad = inst.sweep_rad;
71 out.style_bits = inst.style_bits;
72 out.pick_id = inst.pick_id;
73 return out;
74}
75
76struct ArcPoint {
77 distance_px: f32,
78 along_px: f32,
79};
80
81fn arc_sample(p: vec2<f32>, radius_px: f32, start_rad: f32, sweep_rad: f32) -> ArcPoint {
82 let r_px = length(p);
83 let full_circle = sweep_rad >= TWO_PI - 1.0e-4;
84 if (full_circle) {
85 let theta = atan2(p.y, p.x);
86 var delta = theta - start_rad;
87 delta = delta - floor(delta / TWO_PI) * TWO_PI;
88 var out: ArcPoint;
89 out.distance_px = abs(r_px - radius_px);
90 out.along_px = delta * radius_px;
91 return out;
92 }
93 let theta = atan2(p.y, p.x);
94 var delta = theta - start_rad;
95 delta = delta - floor(delta / TWO_PI) * TWO_PI;
96 var theta_nearest: f32;
97 var along_rad: f32;
98 if (delta <= sweep_rad) {
99 theta_nearest = theta;
100 along_rad = delta;
101 } else {
102 let to_end = delta - sweep_rad;
103 let to_start = TWO_PI - delta;
104 if (to_end < to_start) {
105 theta_nearest = start_rad + sweep_rad;
106 along_rad = sweep_rad;
107 } else {
108 theta_nearest = start_rad;
109 along_rad = 0.0;
110 }
111 }
112 let nearest = vec2<f32>(cos(theta_nearest), sin(theta_nearest)) * radius_px;
113 var out: ArcPoint;
114 out.distance_px = length(p - nearest);
115 out.along_px = along_rad * radius_px;
116 return out;
117}
118
119@fragment
120fn fs(in: VsOut) -> FsOut {
121 let sample = arc_sample(in.local_px, in.radius_px, in.start_rad, in.sweep_rad);
122 let aa = 0.5;
123 let coverage = 1.0 - smoothstep(in.half_width_px - aa, in.half_width_px + aa, sample.distance_px);
124 if (coverage <= 0.0) {
125 discard;
126 }
127
128 let is_construction = (in.style_bits & CONSTRUCTION_BIT) != 0u;
129 let dash_visible = !is_construction
130 || u.dash_period_px <= 0.0
131 || fract(sample.along_px / u.dash_period_px) <= u.dash_on_ratio;
132 let color_coverage = select(0.0, coverage, dash_visible);
133
134 let base = select(u.stroke_color, u.construction, is_construction);
135 let a = base.a * color_coverage;
136
137 var out: FsOut;
138 out.color = vec4<f32>(base.rgb * a, a);
139 out.pick_id = in.pick_id;
140 return out;
141}