mod poly; use poly::Poly; use std::ffi::{CStr, CString}; use gl::types::*; pub struct Bezier { pub degree: usize, pub vx: Vec, pub vy: Vec, pub px: Poly, pub py: Poly, pub dpx: Poly, pub dpy: Poly, } impl Bezier { pub fn new() -> Bezier { Bezier { vx: Vec::::new(), vy: Vec::::new(), px: Poly::new(vec![]), py: Poly::new(vec![]), dpy: Poly::new(vec![]), dpx: Poly::new(vec![]), degree: 0, } } pub fn push(&mut self, x: i32, y: i32) { self.vx.push(x as f32); self.vy.push(y as f32); self.px = to_poly(&self.vx); self.py = to_poly(&self.vy); self.dpx = self.px.deriv(); self.dpy = self.py.deriv(); self.degree += 1; } pub fn remove(&mut self, index: usize) { self.vx.remove(index); self.vy.remove(index); self.px = to_poly(&self.vx); self.py = to_poly(&self.vy); self.dpx = self.px.deriv(); self.dpy = self.py.deriv(); self.degree -= 1; } fn closer(&self, x: f32, y: f32) -> usize { let mut best = 10000.0; let mut best_index = 0; for i in 0..self.degree { let candidate = ((x - self.vx[i]).powi(2) + (y - self.vy[i]).powi(2)).sqrt(); if candidate < best { best = candidate; best_index = i; } } best_index } pub fn grab_closer(&mut self, x: f32, y: f32) { let closer = self.closer(x, y); self.vx[closer] = x; self.vy[closer] = y; self.px = to_poly(&self.vx); self.py = to_poly(&self.vy); self.dpx = self.px.deriv(); self.dpy = self.py.deriv(); } fn show_x(&self) -> String { let mut s = String::new(); for i in 0..self.degree { s.push_str(&self.vx[i].to_string()); if i < self.degree - 1 { s.push_str(", "); } } s } fn show_y(&self) -> String { let mut s = String::new(); for i in 0..self.degree { s.push_str(&self.vy[i].to_string()); if i < self.degree - 1 { s.push_str(", "); } } s } pub fn draw(&self) { let vertex_shader_source = b" #version 410 core layout (location = 0) in vec3 aPos; void main() { gl_Position = vec4(aPos.xyz, 1.0); } " .to_vec(); let fragment_shader_source = format!( " #version 410 core out vec4 FragColor; float vx[{s}] = float[{s}]({vx}); float vy[{s}] = float[{s}]({vy}); float px[{sx}] = float[{sx}]({px}); float py[{sy}] = float[{sy}]({py}); float dpx[{dsx}] = float[{dsx}]({dpx}); float dpy[{dsy}] = float[{dsy}]({dpy}); float thr = 16; float step_size = 5; float inf = 1000000; float eval_px(float t) {{ float result = 0.0; for (int i = 0; i < {sx}; i++) {{ result += px[i] * pow(t, i); }} return result; }} float eval_py(float t) {{ float result = 0.0; for (int i = 0; i < {sy}; i++) {{ result += py[i] * pow(t, i); }} return result; }} float eval_dpx(float t) {{ float result = 0.0; for (int i = 0; i < {dsx}; i++) {{ result += dpx[i] * pow(t, i); }} return result; }} float eval_dpy(float t) {{ float result = 0.0; for (int i = 0; i < {dsy}; i++) {{ result += dpy[i] * pow(t, i); }} return result; }} float control(vec2 pos) {{ float closest = inf; for (int i = 0; i < {s}; i++) {{ float d = distance(pos, vec2(vx[i], vy[i])); if (d < closest) {{ closest = d; }} }} return 1 - closest / thr; }} float inside(vec2 pos) {{ float closest = inf; float t = 0.0; while (t < 1.0) {{ float dstep = step_size / length(vec2(eval_dpx(t), eval_dpy(t))); float step = min(t + dstep, 1.0); vec2 a = vec2(eval_px(t), eval_py(t)); vec2 b = vec2(eval_px(step), eval_py(step)); float c = pow(length(a - b), 2); float d = distance(pos, a + clamp(dot(pos - a, b - a) / c, 0, 1) * (b - a)); if (d < closest) {{ closest = d; }} t = step; }} return 1 - closest / thr; }} void main() {{ float r = inside(gl_FragCoord.xy); float c = control(gl_FragCoord.xy); FragColor = vec4(r, c, 0.0, 1.0); }} ", s = self.degree, vx = self.show_x(), vy = self.show_y(), px = &self.px, py = &self.py, sx = self.px.degree() + 1, sy = self.py.degree() + 1, dpx = &self.dpx, dpy = &self.dpy, dsx = self.dpx.degree() + 1, dsy = self.dpy.degree() + 1, ) .into_bytes(); let shader_program: GLuint; unsafe { let vertex_shader = shader_from_source( &CString::from_vec_unchecked(vertex_shader_source), gl::VERTEX_SHADER, ); let fragment_shader = shader_from_source( &CString::from_vec_unchecked(fragment_shader_source), gl::FRAGMENT_SHADER, ); shader_program = gl::CreateProgram(); gl::AttachShader(shader_program, vertex_shader); gl::AttachShader(shader_program, fragment_shader); gl::LinkProgram(shader_program); gl::DeleteShader(vertex_shader); gl::DeleteShader(fragment_shader); gl::UseProgram(shader_program); } } } fn to_poly(v: &Vec) -> Poly { match v.len() { 0 => Poly::new(vec![0.0]), 1 => Poly::new(vec![v[0]]), 2 => Poly::new(vec![v[0], v[1] - v[0]]), _ => { let mut pv = Vec::new(); for i in 0..v.len() - 1 { pv.push(Poly::new(vec![v[i], v[i + 1] - v[i]])); } while pv.len() > 1 { let mut npv = Vec::new(); for i in 0..pv.len() - 1 { let c = &pv[i + 1] - &pv[i]; npv.push(&pv[i] + &c.shift()); } pv = npv; } pv[0].clone() } } } fn shader_from_source(source: &CStr, shader_type: GLenum) -> GLuint { unsafe { let shader: GLuint = gl::CreateShader(shader_type); gl::ShaderSource(shader, 1, &source.as_ptr(), std::ptr::null()); gl::CompileShader(shader); let mut success: gl::types::GLint = 1; gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success); if success == 0 { let mut length: GLint = 0; gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut length); let mut buffer: Vec = Vec::with_capacity(length as usize + 1); buffer.extend([b' '].iter().cycle().take(length as usize)); let error: CString = CString::from_vec_unchecked(buffer); gl::GetShaderInfoLog(shader, length, std::ptr::null_mut(), error.as_ptr() as *mut GLchar); println!("Compilation error: {}", error.to_str().unwrap()); } shader } }