@@ 3,5 3,7 @@ use druid::{Data, Lens};
#[derive(Clone, Data, Default, Lens)]
pub struct AppData {
pub k0: f64,
+ pub bias0: f64,
pub k1: f64,
+ pub bias1: f64,
}
@@ 1,6 1,6 @@
//! Custom widget for drawing stuff
-use druid::kurbo::{BezPath, Line, Point};
+use druid::kurbo::{Affine, BezPath, Line, Point, Vec2};
use druid::piet::Color;
use druid::widget::prelude::*;
use druid::Data;
@@ 49,6 49,36 @@ fn compute_basis(bias: f64) -> Vec<f64> {
result
}
+fn compute_thetas(data: &AppData) -> Vec<f64> {
+ let mut basis0 = compute_basis(data.bias0);
+ let basis1 = compute_basis(data.bias1);
+ for (x, y) in basis0.iter_mut().zip(basis1.iter().rev()) {
+ *x = *x * data.k0 - *y * data.k1
+ }
+ basis0
+}
+
+fn integrate_curve(thetas: &[f64]) -> Vec<Point> {
+ let n = thetas.len();
+ let mut p = Point::ORIGIN;
+ let scale = 1.0 / (n - 1) as f64;
+ let mut result = (0..n)
+ .map(|i| {
+ let this_p = p;
+ if i < thetas.len() - 1 {
+ let th = 0.5 * (thetas[i] + thetas[i + 1]);
+ p += scale * Vec2::from_angle(th);
+ }
+ this_p
+ })
+ .collect::<Vec<_>>();
+ let a = Affine::new([p.x, p.y, -p.y, p.x, 0.0, 0.0]).inverse();
+ for p in &mut result {
+ *p = a * *p;
+ }
+ result
+}
+
fn plot(ctx: &mut PaintCtx, seq: &[f64], origin: Point, width: f64, scale: f64) {
let mut path = BezPath::new();
for i in 0..seq.len() {
@@ 63,6 93,19 @@ fn plot(ctx: &mut PaintCtx, seq: &[f64], origin: Point, width: f64, scale: f64)
ctx.stroke(path, &Color::WHITE, 1.0);
}
+fn plot_xy(ctx: &mut PaintCtx, seq: &[Point], origin: Point, scale: f64) {
+ let mut path = BezPath::new();
+ for i in 0..seq.len() {
+ let p = origin + scale * seq[i].to_vec2();
+ if i == 0 {
+ path.move_to(p);
+ } else {
+ path.line_to(p);
+ }
+ }
+ ctx.stroke(path, &Color::WHITE, 1.0);
+}
+
impl Widget<AppData> for Grapher {
fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut AppData, env: &Env) {}
@@ 86,7 129,7 @@ impl Widget<AppData> for Grapher {
_ctx: &mut LayoutCtx,
bc: &BoxConstraints,
_data: &AppData,
- env: &Env,
+ _env: &Env,
) -> Size {
bc.max()
}
@@ 94,7 137,10 @@ impl Widget<AppData> for Grapher {
fn paint(&mut self, ctx: &mut PaintCtx, data: &AppData, _env: &Env) {
let line = Line::new((0.0, 0.0), (100.0, 100.0));
ctx.stroke(line, &Color::WHITE, 1.0);
- let basis = compute_basis(data.k1);
- plot(ctx, &basis, Point::new(50.0, 400.0), 600.0, 100.0);
+ //let basis = compute_basis(data.bias0);
+ let thetas = compute_thetas(data);
+ plot(ctx, &thetas, Point::new(50.0, 400.0), 600.0, 100.0);
+ let pts = integrate_curve(&thetas);
+ plot_xy(ctx, &pts, Point::new(100.0, 300.0), 400.0);
}
}
@@ 1,7 1,5 @@
use druid::widget::{Flex, Label, Slider};
-use druid::{
- AppLauncher, Data, Lens, LocalizedString, PlatformError, Widget, WidgetExt, WindowDesc,
-};
+use druid::{AppLauncher, Env, PlatformError, Widget, WidgetExt, WindowDesc};
mod appdata;
mod grapher;
@@ 18,21 16,45 @@ fn main() -> Result<(), PlatformError> {
fn ui_builder() -> impl Widget<AppData> {
// The label text will be computed dynamically based on the current locale and count
- let text = LocalizedString::new("hello-counter").with_arg("count", |data: &AppData, _env| {
- format!("{:.1}", data.k1).into()
- });
- let label = Label::new(text).padding(5.0).center();
- let slider0 = Slider::new().lens(AppData::k0).padding(5.0);
- let slider1 = Slider::new()
+ let label0 = Label::new(|data: &AppData, _env: &Env| format!("k0: {:.2}", data.k0))
+ .padding(5.0)
+ .center();
+ let slider0 = Slider::new()
+ .with_range(-2.0, 2.0)
+ .lens(AppData::k0)
+ .padding(5.0);
+ let labelb0 = Label::new(|data: &AppData, _env: &Env| format!("bias0: {:.2}", data.bias0))
+ .padding(5.0)
+ .center();
+ let sliderb0 = Slider::new()
.with_range(-1.0, 2.0)
+ .lens(AppData::bias0)
+ .padding(5.0);
+ let label1 = Label::new(|data: &AppData, _env: &Env| format!("k1: {:.2}", data.k1))
+ .padding(5.0)
+ .center();
+ let slider1 = Slider::new()
+ .with_range(-2.0, 2.0)
.lens(AppData::k1)
.padding(5.0);
+ let labelb1 = Label::new(|data: &AppData, _env: &Env| format!("bias1: {:.2}", data.bias1))
+ .padding(5.0)
+ .center();
+ let sliderb1 = Slider::new()
+ .with_range(-1.0, 2.0)
+ .lens(AppData::bias1)
+ .padding(5.0);
let grapher = grapher::Grapher;
Flex::column()
- .with_child(label)
+ .with_child(label0)
.with_child(slider0)
+ .with_child(labelb0)
+ .with_child(sliderb0)
+ .with_child(label1)
.with_child(slider1)
+ .with_child(labelb1)
+ .with_child(sliderb1)
.with_flex_child(grapher, 1.0)
.must_fill_main_axis(true)
}