Skip to content

Commit

Permalink
Render thunks correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
calintat committed Apr 12, 2023
1 parent b840745 commit dde7d97
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 49 deletions.
23 changes: 23 additions & 0 deletions sd-graphics/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,29 @@ impl<T> LayoutInternal<T> {
pub type Layout = LayoutInternal<f32>;

impl Layout {
pub fn width(&self) -> f32 {
self.max - self.min
}

pub fn height(&self) -> f32 {
(0..self.nodes.len())
.map(|j| self.slice_height(j))
.sum::<f32>()
+ 1.0
}

pub fn slice_height(&self, j: usize) -> f32 {
self.nodes[j]
.iter()
.map(|n| match n {
Node::Atom(_) => 0.0,
Node::Thunk(body) => body.height(),
})
.max_by(|x, y| x.partial_cmp(y).unwrap())
.unwrap()
+ 1.0
}

fn from_solution(layout: LayoutInternal<Variable>, solution: &impl Solution) -> Self {
Layout {
min: solution.value(layout.min) as f32,
Expand Down
121 changes: 72 additions & 49 deletions sd-graphics/src/render.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use epaint::{
emath::{Align2, RectTransform},
vec2, CircleShape, Color32, CubicBezierShape, Fonts, Pos2, Rect, RectShape, Rounding, Shape,
Stroke, Vec2,
vec2, CircleShape, Color32, CubicBezierShape, Fonts, Pos2, Rect, Rounding, Shape, Stroke, Vec2,
};
use sd_core::monoidal::{MonoidalGraph, MonoidalOp};

Expand All @@ -10,52 +9,69 @@ use crate::layout::Layout;
pub const SCALE: f32 = 50.0;
pub const STROKE_WIDTH: f32 = 1.0;

pub const BOX_SIZE: Vec2 = vec2(20.0, 20.0);
pub const RADIUS_COPY: f32 = 5.0;
pub const RADIUS_OPERATION: f32 = 10.0;

pub fn default_stroke() -> Stroke {
Stroke::new(STROKE_WIDTH, Color32::BLACK)
}

// Specifies how to transform a layout position to a screen position.
struct Transform {
layout_bounds: Vec2,
bounds: Vec2,
to_screen: RectTransform,
}

impl Transform {
fn apply(&self, x: f32, y: f32) -> Pos2 {
// Scale by a constant and translate to the centre of the bounding box.
self.to_screen.transform_pos(
Pos2::new(x * SCALE, y * SCALE) + (self.bounds - self.layout_bounds * SCALE) / 2.0,
)
}
}

pub fn render(
layout: &Layout,
graph: &MonoidalGraph,
fonts: &Fonts,
bounds: Vec2,
to_screen: RectTransform,
) -> Vec<Shape> {
let n = graph.slices.len();

let min_x = layout.min;
let max_x = layout.max;
let height = n as f32 + 1.0;

// Scale by a constant and translate to the centre of the bounding box.
let pos2 = |x: f32, y: f32| {
to_screen.transform_pos(Pos2::new(
(x - min_x) * SCALE + (bounds.x - (max_x - min_x) * SCALE) / 2.0,
y * SCALE + (bounds.y - height * SCALE) / 2.0,
))
let transform = Transform {
bounds,
to_screen,
layout_bounds: vec2(layout.width(), layout.height()),
};

let mut shapes: Vec<Shape> = Vec::new();
let mut shapes = Vec::default();
generate_shapes(&mut shapes, 0.0, layout, graph, fonts, &transform);
shapes
}

fn generate_shapes(
shapes: &mut Vec<Shape>,
mut y_offset: f32,
layout: &Layout,
graph: &MonoidalGraph,
fonts: &Fonts,
transform: &Transform,
) {
// Source
for &x in layout.inputs() {
let start = pos2(x, 0.0);
let end = pos2(x, 0.5);
let start = transform.apply(x, y_offset);
let end = transform.apply(x, y_offset + 0.5);
shapes.push(Shape::line_segment([start, end], default_stroke()));
}

// Target
for &x in layout.outputs() {
let start = pos2(x, n as f32 + 0.5);
let end = pos2(x, n as f32 + 1.0);
shapes.push(Shape::line_segment([start, end], default_stroke()));
}
y_offset += 0.5;

for (j, slice) in graph.slices.iter().enumerate() {
let slice_height = layout.slice_height(j);
let y_in = y_offset;
let y_out = y_offset + slice_height;

let mut offset_i = 0;
let mut offset_o = 0;
for (i, (op, _)) in slice.ops.iter().enumerate() {
Expand All @@ -66,16 +82,12 @@ pub fn render(
let x_ins = &layout.wires[j][offset_i..offset_i + ni];
let x_outs = &layout.wires[j + 1][offset_o..offset_o + no];

let y_op = j as f32 + 1.0;
let y_in = j as f32 + 0.5;
let y_out = j as f32 + 1.5;

match op {
MonoidalOp::Swap => {
let in1 = pos2(x_ins[0], y_in);
let in2 = pos2(x_ins[1], y_in);
let out1 = pos2(x_outs[0], y_out);
let out2 = pos2(x_outs[1], y_out);
let in1 = transform.apply(x_ins[0], y_in);
let in2 = transform.apply(x_ins[1], y_in);
let out1 = transform.apply(x_outs[0], y_out);
let out2 = transform.apply(x_outs[1], y_out);

shapes.push(Shape::CubicBezier(CubicBezierShape::from_points_stroke(
vertical_out_vertical_in(in1, out2),
Expand All @@ -90,34 +102,38 @@ pub fn render(
default_stroke(),
)));
}
MonoidalOp::Thunk { .. } => {
MonoidalOp::Thunk { body, .. } => {
let x_op = x_op.unwrap_thunk();
let diff = (slice_height - x_op.height()) as f32 / 2.0;
let y_min = y_in + diff;
let y_max = y_out - diff;
for &x in x_ins {
let thunk = pos2(x, y_op);
let input = pos2(x, y_in);
let thunk = transform.apply(x, y_min);
let input = transform.apply(x, y_in);
shapes.push(Shape::line_segment([input, thunk], default_stroke()));
}
for &x in x_outs {
let thunk = pos2(x, y_op);
let output = pos2(x, y_out);
let thunk = transform.apply(x, y_max);
let output = transform.apply(x, y_out);
shapes.push(Shape::line_segment([thunk, output], default_stroke()));
}
shapes.push(Shape::Rect(RectShape {
rect: Rect::from_min_max(
pos2(x_op.min, y_op) - BOX_SIZE / 2.0,
pos2(x_op.max, y_op) + BOX_SIZE / 2.0,
shapes.push(Shape::rect_stroke(
Rect::from_min_max(
transform.apply(x_op.min, y_min),
transform.apply(x_op.max, y_max),
),
rounding: Rounding::none(),
fill: Color32::WHITE,
stroke: default_stroke(),
}));
Rounding::none(),
default_stroke(),
));
generate_shapes(shapes, y_min, x_op, body, fonts, transform);
}
_ => {
let x_op = *x_op.unwrap_atom();
let center = pos2(x_op, y_op);
let y_op = (y_in + y_out) / 2.0;
let center = transform.apply(x_op, y_op);

for &x in x_ins {
let input = pos2(x, y_in);
let input = transform.apply(x, y_in);
shapes.push(Shape::CubicBezier(CubicBezierShape::from_points_stroke(
vertical_out_horizontal_in(input, center),
false,
Expand All @@ -127,7 +143,7 @@ pub fn render(
}

for &x in x_outs {
let output = pos2(x, y_out);
let output = transform.apply(x, y_out);
shapes.push(Shape::CubicBezier(CubicBezierShape::from_points_stroke(
horizontal_out_vertical_in(center, output),
false,
Expand Down Expand Up @@ -164,9 +180,16 @@ pub fn render(
offset_i += ni;
offset_o += no;
}

y_offset = y_out;
}

shapes
// Target
for &x in layout.outputs() {
let start = transform.apply(x, y_offset);
let end = transform.apply(x, y_offset + 0.5);
shapes.push(Shape::line_segment([start, end], default_stroke()));
}
}

fn vertical_out_horizontal_in(start: Pos2, end: Pos2) -> [Pos2; 4] {
Expand Down

0 comments on commit dde7d97

Please sign in to comment.