diff --git a/examples/demo.rs b/examples/demo.rs index 109ca17..1ffa142 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -1,3 +1,5 @@ +#![allow(clippy::use_self)] + use std::collections::HashMap; use eframe::{App, CreationContext}; @@ -34,7 +36,7 @@ enum DemoNode { } impl DemoNode { - fn name(&self) -> &str { + const fn name(&self) -> &str { match self { DemoNode::Sink => "Sink", DemoNode::Number(_) => "Number", @@ -96,6 +98,7 @@ impl SnarlViewer for DemoViewer { #[inline] fn connect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl) { // Validate connection + #[allow(clippy::match_same_arms)] // For match clarity match (&snarl[from.id.node], &snarl[to.id.node]) { (DemoNode::Sink, _) => { unreachable!("Sink node has no outputs") @@ -153,10 +156,8 @@ impl SnarlViewer for DemoViewer { fn inputs(&mut self, node: &DemoNode) -> usize { match node { - DemoNode::Sink => 1, - DemoNode::Number(_) => 0, - DemoNode::String(_) => 0, - DemoNode::ShowImage(_) => 1, + DemoNode::Sink | DemoNode::ShowImage(_) => 1, + DemoNode::Number(_) | DemoNode::String(_) => 0, DemoNode::ExprNode(expr_node) => 1 + expr_node.bindings.len(), } } @@ -164,13 +165,14 @@ impl SnarlViewer for DemoViewer { fn outputs(&mut self, node: &DemoNode) -> usize { match node { DemoNode::Sink => 0, - DemoNode::Number(_) => 1, - DemoNode::String(_) => 1, - DemoNode::ShowImage(_) => 1, - DemoNode::ExprNode(_) => 1, + DemoNode::Number(_) + | DemoNode::String(_) + | DemoNode::ShowImage(_) + | DemoNode::ExprNode(_) => 1, } } + #[allow(clippy::too_many_lines)] fn show_input( &mut self, pin: &InPin, @@ -196,7 +198,7 @@ impl SnarlViewer for DemoViewer { } DemoNode::String(ref value) => { assert_eq!(remote.output, 0, "String node has only one output"); - ui.label(format!("{:?}", value)); + ui.label(format!("{value:?}")); PinInfo::circle().with_fill(STRING_COLOR).with_wire_style( WireStyle::AxisAligned { @@ -286,11 +288,11 @@ impl SnarlViewer for DemoViewer { .show(ui); let input = snarl[pin.id.node].string_in(); - if new_string != *input { + if new_string == *input { + false + } else { *input = new_string; true - } else { - false } } _ => unreachable!("Expr pins has only one wire"), @@ -449,11 +451,11 @@ impl SnarlViewer for DemoViewer { ui.close_menu(); } if ui.button("String").clicked() { - snarl.insert_node(pos, DemoNode::String("".to_owned())); + snarl.insert_node(pos, DemoNode::String(String::new())); ui.close_menu(); } if ui.button("Show image").clicked() { - snarl.insert_node(pos, DemoNode::ShowImage("".to_owned())); + snarl.insert_node(pos, DemoNode::ShowImage(String::new())); ui.close_menu(); } if ui.button("Sink").clicked() { @@ -480,29 +482,25 @@ impl SnarlViewer for DemoViewer { // In your implementation, you may want to define specifications for each node's // pin inputs and outputs and compatibility to make this easier. - ui.label("Add node"); - type PinCompat = usize; const PIN_NUM: PinCompat = 1; const PIN_STR: PinCompat = 2; const PIN_IMG: PinCompat = 4; const PIN_SINK: PinCompat = PIN_NUM | PIN_STR | PIN_IMG; - fn pin_out_compat(node: &DemoNode) -> PinCompat { + const fn pin_out_compat(node: &DemoNode) -> PinCompat { match node { DemoNode::Sink => 0, - DemoNode::Number(_) => PIN_NUM, DemoNode::String(_) => PIN_STR, DemoNode::ShowImage(_) => PIN_IMG, - DemoNode::ExprNode(_) => PIN_NUM, + DemoNode::Number(_) | DemoNode::ExprNode(_) => PIN_NUM, } } - fn pin_in_compat(node: &DemoNode, pin: usize) -> PinCompat { + const fn pin_in_compat(node: &DemoNode, pin: usize) -> PinCompat { match node { DemoNode::Sink => PIN_SINK, - DemoNode::Number(_) => 0, - DemoNode::String(_) => 0, + DemoNode::Number(_) | DemoNode::String(_) => 0, DemoNode::ShowImage(_) => PIN_STR, DemoNode::ExprNode(_) => { if pin == 0 { @@ -514,6 +512,8 @@ impl SnarlViewer for DemoViewer { } } + ui.label("Add node"); + match src_pins { AnyPins::Out(src_pins) => { assert!( @@ -525,7 +525,7 @@ impl SnarlViewer for DemoViewer { let src_out_ty = pin_out_compat(snarl.get_node(src_pin.node).unwrap()); let dst_in_candidates = [ ("Sink", (|| DemoNode::Sink) as fn() -> DemoNode, PIN_SINK), - ("Show Image", || DemoNode::ShowImage("".to_owned()), PIN_STR), + ("Show Image", || DemoNode::ShowImage(String::new()), PIN_STR), ("Expr", || DemoNode::ExprNode(ExprNode::new()), PIN_STR), ]; @@ -555,9 +555,9 @@ impl SnarlViewer for DemoViewer { (|| DemoNode::Number(0.)) as fn() -> DemoNode, PIN_NUM, ), - ("String", || DemoNode::String("".to_owned()), PIN_STR), + ("String", || DemoNode::String(String::new()), PIN_STR), ("Expr", || DemoNode::ExprNode(ExprNode::new()), PIN_NUM), - ("Show Image", || DemoNode::ShowImage("".to_owned()), PIN_IMG), + ("Show Image", || DemoNode::ShowImage(String::new()), PIN_IMG), ]; for (name, ctor, out_ty) in dst_out_candidates { @@ -934,19 +934,16 @@ impl Expr { let next_op = input.parse::()?; - match (op, next_op) { - (BinOp::Add | BinOp::Sub, BinOp::Mul | BinOp::Div) => { - let rhs = Self::parse_binop(rhs, next_op, input)?; - Ok(Expr::BinOp { - lhs, - op, - rhs: Box::new(rhs), - }) - } - _ => { - let lhs = Expr::BinOp { lhs, op, rhs }; - Self::parse_binop(Box::new(lhs), next_op, input) - } + if let (BinOp::Add | BinOp::Sub, BinOp::Mul | BinOp::Div) = (op, next_op) { + let rhs = Self::parse_binop(rhs, next_op, input)?; + Ok(Self::BinOp { + lhs, + op, + rhs: Box::new(rhs), + }) + } else { + let lhs = Self::BinOp { lhs, op, rhs }; + Self::parse_binop(Box::new(lhs), next_op, input) } } } @@ -957,7 +954,7 @@ pub struct DemoApp { snarl_ui_id: Option, } -fn default_style() -> SnarlStyle { +const fn default_style() -> SnarlStyle { SnarlStyle { node_layout: Some(NodeLayout::FlippedSandwich), pin_placement: Some(PinPlacement::Edge), @@ -993,22 +990,20 @@ impl DemoApp { cx.egui_ctx.style_mut(|style| style.animation_time *= 10.0); - let snarl = match cx.storage { - None => Snarl::new(), - Some(storage) => storage + let snarl = cx.storage.map_or_else(Snarl::new, |storage| { + storage .get_string("snarl") .and_then(|snarl| serde_json::from_str(&snarl).ok()) - .unwrap_or_else(Snarl::new), - }; + .unwrap_or_default() + }); // let snarl = Snarl::new(); - let style = match cx.storage { - None => default_style(), - Some(storage) => storage + let style = cx.storage.map_or_else(default_style, |storage| { + storage .get_string("style") .and_then(|style| serde_json::from_str(&style).ok()) - .unwrap_or_else(default_style), - }; + .unwrap_or_else(default_style) + }); // let style = SnarlStyle::new(); DemoApp { @@ -1029,7 +1024,7 @@ impl App for DemoApp { { ui.menu_button("File", |ui| { if ui.button("Quit").clicked() { - ctx.send_viewport_cmd(egui::ViewportCommand::Close) + ctx.send_viewport_cmd(egui::ViewportCommand::Close); } }); ui.add_space(16.0); @@ -1038,7 +1033,7 @@ impl App for DemoApp { egui::widgets::global_theme_preference_switch(ui); if ui.button("Clear All").clicked() { - self.snarl = Default::default(); + self.snarl = Snarl::default(); } }); }); @@ -1067,7 +1062,7 @@ impl App for DemoApp { for (id, node) in selected { ui.horizontal(|ui| { - ui.label(format!("{:?}", id)); + ui.label(format!("{id:?}")); ui.label(node.name()); ui.add_space(ui.spacing().item_spacing.x); if ui.button("Remove").clicked() { @@ -1146,5 +1141,5 @@ fn main() { fn format_float(v: f64) -> String { let v = (v * 1000.0).round() / 1000.0; - format!("{}", v) + format!("{v}") } diff --git a/src/lib.rs b/src/lib.rs index a947caa..c5987f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ #![deny(missing_docs)] #![deny(clippy::correctness, clippy::complexity, clippy::perf, clippy::style)] // #![warn(clippy::pedantic)] -#![allow(clippy::inline_always)] +#![allow(clippy::inline_always, clippy::use_self)] pub mod ui; @@ -360,10 +360,7 @@ impl Snarl { /// Returns reference to the node. #[must_use] pub fn get_node(&self, idx: NodeId) -> Option<&T> { - match self.nodes.get(idx.0) { - Some(node) => Some(&node.value), - None => None, - } + self.nodes.get(idx.0).map(|node| &node.value) } /// Returns mutable reference to the node. @@ -377,18 +374,12 @@ impl Snarl { /// Returns reference to the node data. #[must_use] pub fn get_node_info(&self, idx: NodeId) -> Option<&Node> { - match self.nodes.get(idx.0) { - Some(node) => Some(node), - None => None, - } + self.nodes.get(idx.0) } /// Returns mutable reference to the node data. pub fn get_node_info_mut(&mut self, idx: NodeId) -> Option<&mut Node> { - match self.nodes.get_mut(idx.0) { - Some(node) => Some(node), - None => None, - } + self.nodes.get_mut(idx.0) } /// Iterates over shared references to each node. diff --git a/src/ui.rs b/src/ui.rs index f6524bf..6898ad9 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -32,7 +32,7 @@ pub use self::{ }; /// Controls how header, pins, body and footer are laid out in the node. -#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))] pub enum NodeLayout { @@ -365,10 +365,12 @@ impl SnarlStyle { } fn get_pin_stroke(&self, scale: f32, style: &Style) -> Stroke { - self.pin_stroke.zoomed(scale).unwrap_or(Stroke::new( - style.visuals.widgets.active.bg_stroke.width, - style.visuals.widgets.active.bg_stroke.color, - )) + self.pin_stroke.zoomed(scale).unwrap_or_else(|| { + Stroke::new( + style.visuals.widgets.active.bg_stroke.width, + style.visuals.widgets.active.bg_stroke.color, + ) + }) } fn get_pin_shape(&self) -> PinShape { @@ -382,13 +384,13 @@ impl SnarlStyle { fn get_wire_width(&self, scale: f32, style: &Style) -> f32 { self.wire_width .zoomed(scale) - .unwrap_or(self.get_pin_size(scale, style) * 0.1) + .unwrap_or_else(|| self.get_pin_size(scale, style) * 0.1) } fn get_wire_frame_size(&self, scale: f32, style: &Style) -> f32 { self.wire_frame_size .zoomed(scale) - .unwrap_or(self.get_pin_size(scale, style) * 3.0) + .unwrap_or_else(|| self.get_pin_size(scale, style) * 3.0) } fn get_downscale_wire_frame(&self) -> bool { @@ -410,7 +412,7 @@ impl SnarlStyle { fn get_header_drag_space(&self, scale: f32, style: &Style) -> Vec2 { self.header_drag_space .zoomed(scale) - .unwrap_or(vec2(style.spacing.icon_width, style.spacing.icon_width)) + .unwrap_or_else(|| vec2(style.spacing.icon_width, style.spacing.icon_width)) } fn get_collapsible(&self) -> bool { @@ -418,7 +420,7 @@ impl SnarlStyle { } fn get_bg_frame(&self, style: &Style) -> Frame { - self.bg_frame.unwrap_or(Frame::canvas(style)) + self.bg_frame.unwrap_or_else(|| Frame::canvas(style)) } fn get_bg_pattern_stroke(&self, scale: f32, style: &Style) -> Stroke { @@ -456,15 +458,17 @@ impl SnarlStyle { } fn get_select_stroke(&self, scale: f32, style: &Style) -> Stroke { - self.select_stoke.zoomed(scale).unwrap_or(Stroke::new( - style.visuals.selection.stroke.width, - style.visuals.selection.stroke.color.gamma_multiply(0.5), - )) + self.select_stoke.zoomed(scale).unwrap_or_else(|| { + Stroke::new( + style.visuals.selection.stroke.width, + style.visuals.selection.stroke.color.gamma_multiply(0.5), + ) + }) } fn get_select_fill(&self, style: &Style) -> Color32 { self.select_fill - .unwrap_or(style.visuals.selection.bg_fill.gamma_multiply(0.3)) + .unwrap_or_else(|| style.visuals.selection.bg_fill.gamma_multiply(0.3)) } fn get_select_rect_contained(&self) -> bool { @@ -472,12 +476,14 @@ impl SnarlStyle { } fn get_select_style(&self, scale: f32, style: &Style) -> SelectionStyle { - self.select_style.zoomed(scale).unwrap_or(SelectionStyle { - margin: style.spacing.window_margin, - rounding: style.visuals.window_rounding, - fill: self.get_select_fill(style), - stroke: self.get_select_stroke(scale, style), - }) + self.select_style + .zoomed(scale) + .unwrap_or_else(|| SelectionStyle { + margin: style.spacing.window_margin, + rounding: style.visuals.window_rounding, + fill: self.get_select_fill(style), + stroke: self.get_select_stroke(scale, style), + }) } } @@ -495,6 +501,7 @@ mod serde_frame_option { pub stroke: egui::Stroke, } + #[allow(clippy::ref_option)] pub fn serialize(frame: &Option, serializer: S) -> Result where S: Serializer, @@ -616,7 +623,7 @@ impl Snarl { style: &SnarlStyle, snarl_state: &SnarlState, viewport: &Rect, - ui: &mut Ui, + ui: &Ui, ) where V: SnarlViewer, { @@ -637,7 +644,6 @@ impl Snarl { } /// Render [`Snarl`] using given viewer and style into the [`Ui`]. - pub fn show(&mut self, viewer: &mut V, style: &SnarlStyle, id_salt: impl Hash, ui: &mut Ui) where V: SnarlViewer, @@ -694,7 +700,7 @@ impl Snarl { { if input.scroll_delta != 0.0 { let new_scale = (snarl_state.scale() - * (1.0 + input.scroll_delta * style.get_scale_velocity())) + * input.scroll_delta.mul_add(style.get_scale_velocity(), 1.0)) .clamp(style.get_min_scale(), style.get_max_scale()); snarl_state.set_scale(new_scale); @@ -836,7 +842,7 @@ impl Snarl { } if bg_r.drag_started_by(PointerButton::Primary) && input.modifiers.shift { - let screen_pos = input.interact_pos.unwrap_or(viewport.center()); + let screen_pos = input.interact_pos.unwrap_or_else(|| viewport.center()); let graph_pos = snarl_state.screen_pos_to_graph(screen_pos, viewport); snarl_state.start_rect_selection(graph_pos); } @@ -854,9 +860,10 @@ impl Snarl { if bg_r.drag_stopped_by(PointerButton::Primary) { if let Some(select_rect) = snarl_state.rect_selection() { let select_nodes = node_rects.into_iter().filter_map(|(id, rect)| { - let select = match style.get_select_rect_contained() { - true => select_rect.contains_rect(rect), - false => select_rect.intersects(rect), + let select = if style.get_select_rect_contained() { + select_rect.contains_rect(rect) + } else { + select_rect.intersects(rect) }; if select { @@ -900,7 +907,10 @@ impl Snarl { // Do centering unless no nodes are present. if style.get_centering() && bg_r.double_clicked() && centers_weight > 0 { - centers_sum /= centers_weight as f32; + #[allow(clippy::cast_precision_loss)] + { + centers_sum /= centers_weight as f32; + } snarl_state.set_offset(centers_sum * snarl_state.scale()); } @@ -1025,7 +1035,7 @@ impl Snarl { Stroke::new(wire_width, to_r.pin_color), to_r.wire_style .zoomed(snarl_state.scale()) - .unwrap_or(style.get_wire_style(snarl_state.scale())), + .unwrap_or_else(|| style.get_wire_style(snarl_state.scale())), ); } } @@ -1046,7 +1056,7 @@ impl Snarl { from_r .wire_style .zoomed(snarl_state.scale()) - .unwrap_or(style.get_wire_style(snarl_state.scale())), + .unwrap_or_else(|| style.get_wire_style(snarl_state.scale())), ); } } @@ -1401,7 +1411,7 @@ impl Snarl { body_rect: Rect, clip_rect: Rect, viewport: Rect, - snarl_state: &mut SnarlState, + snarl_state: &SnarlState, ) -> DrawBodyResponse where V: SnarlViewer, @@ -1587,45 +1597,52 @@ impl Snarl { let mut new_pins_size = Vec2::ZERO; let r = node_frame.show(node_ui, |ui| { - let min_pin_y = node_rect.min.y + node_state.header_height() * 0.5; + let min_pin_y = node_state.header_height().mul_add(0.5, node_rect.min.y); // Input pins' center side by X axis. let input_x = match pin_placement { PinPlacement::Inside => { - node_frame_rect.left() + node_frame.inner_margin.left + pin_size * 0.5 + pin_size.mul_add(0.5, node_frame_rect.left() + node_frame.inner_margin.left) } PinPlacement::Edge => node_frame_rect.left(), - PinPlacement::Outside { margin } => { - node_frame_rect.left() - margin * snarl_state.scale() - pin_size * 0.5 - } + PinPlacement::Outside { margin } => pin_size.mul_add( + -0.5, + margin.mul_add(-snarl_state.scale(), node_frame_rect.left()), + ), }; // Input pins' spacing required. let input_spacing = match pin_placement { PinPlacement::Inside => Some(pin_size), - PinPlacement::Edge => { - Some((pin_size * 0.5 - node_frame.inner_margin.left).max(0.0)) - } + PinPlacement::Edge => Some( + pin_size + .mul_add(0.5, -node_frame.inner_margin.left) + .max(0.0), + ), PinPlacement::Outside { .. } => None, }; // Output pins' center side by X axis. let output_x = match pin_placement { - PinPlacement::Inside => { - node_frame_rect.right() - node_frame.inner_margin.right - pin_size * 0.5 - } + PinPlacement::Inside => pin_size.mul_add( + -0.5, + node_frame_rect.right() - node_frame.inner_margin.right, + ), PinPlacement::Edge => node_frame_rect.right(), - PinPlacement::Outside { margin } => { - node_frame_rect.right() + margin * snarl_state.scale() + pin_size * 0.5 - } + PinPlacement::Outside { margin } => pin_size.mul_add( + 0.5, + margin.mul_add(snarl_state.scale(), node_frame_rect.right()), + ), }; // Output pins' spacing required. let output_spacing = match pin_placement { PinPlacement::Inside => Some(pin_size), - PinPlacement::Edge => { - Some((pin_size * 0.5 - node_frame.inner_margin.right).max(0.0)) - } + PinPlacement::Edge => Some( + pin_size + .mul_add(0.5, -node_frame.inner_margin.right) + .max(0.0), + ), PinPlacement::Outside { .. } => None, }; @@ -2123,7 +2140,7 @@ impl Snarl { } } -fn mix_colors(a: Color32, b: Color32) -> Color32 { +const fn mix_colors(a: Color32, b: Color32) -> Color32 { Color32::from_rgba_premultiplied( ((a.r() as u32 + b.r() as u32) / 2) as u8, ((a.g() as u32 + b.g() as u32) / 2) as u8, @@ -2201,7 +2218,7 @@ fn mix_colors(a: Color32, b: Color32) -> Color32 { // } #[test] -fn snarl_style_is_send_sync() { - fn is_send_sync() {} +const fn snarl_style_is_send_sync() { + const fn is_send_sync() {} is_send_sync::(); } diff --git a/src/ui/background_pattern.rs b/src/ui/background_pattern.rs index 1172333..73ef1fa 100644 --- a/src/ui/background_pattern.rs +++ b/src/ui/background_pattern.rs @@ -17,36 +17,42 @@ pub struct Viewport { impl Viewport { /// Converts screen-space position to graph-space position. #[inline(always)] + #[must_use] pub fn screen_pos_to_graph(&self, pos: Pos2) -> Pos2 { (pos + self.offset - self.rect.center().to_vec2()) / self.scale } /// Converts graph-space position to screen-space position. #[inline(always)] + #[must_use] pub fn graph_pos_to_screen(&self, pos: Pos2) -> Pos2 { pos * self.scale - self.offset + self.rect.center().to_vec2() } /// Converts screen-space vector to graph-space vector. #[inline(always)] + #[must_use] pub fn graph_vec_to_screen(&self, size: Vec2) -> Vec2 { size * self.scale } /// Converts graph-space vector to screen-space vector. #[inline(always)] + #[must_use] pub fn screen_vec_to_graph(&self, size: Vec2) -> Vec2 { size / self.scale } /// Converts screen-space size to graph-space size. #[inline(always)] + #[must_use] pub fn graph_size_to_screen(&self, size: f32) -> f32 { size * self.scale } /// Converts graph-space size to screen-space size. #[inline(always)] + #[must_use] pub fn screen_size_to_graph(&self, size: f32) -> f32 { size / self.scale } @@ -91,6 +97,7 @@ impl Default for Grid { impl Grid { /// Create new grid with given spacing and angle. + #[must_use] pub const fn new(spacing: Vec2, angle: f32) -> Self { Self { spacing, angle } } @@ -120,7 +127,7 @@ impl Grid { let max_x = (pattern_bounds.max.x / spacing.x).floor(); for x in 0..=(max_x - min_x) as i64 { - #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_precision_loss)] let x = (x as f32 + min_x) * spacing.x; let top = (rot * vec2(x, pattern_bounds.min.y)).to_pos2(); @@ -136,7 +143,7 @@ impl Grid { let max_y = (pattern_bounds.max.y / spacing.y).floor(); for y in 0..=(max_y - min_y) as i64 { - #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_precision_loss)] let y = (y as f32 + min_y) * spacing.y; let top = (rot * vec2(pattern_bounds.min.x, y)).to_pos2(); @@ -177,11 +184,13 @@ impl BackgroundPattern { /// ` and angle - ` #[doc = default_grid_angle!()] /// ` radian. + #[must_use] pub const fn new() -> Self { Self::Grid(Grid::new(DEFAULT_GRID_SPACING, DEFAULT_GRID_ANGLE)) } /// Create new grid background pattern with given spacing and angle. + #[must_use] pub const fn grid(spacing: Vec2, angle: f32) -> Self { Self::Grid(Grid::new(spacing, angle)) } diff --git a/src/ui/pin.rs b/src/ui/pin.rs index ea8d995..cf6d880 100644 --- a/src/ui/pin.rs +++ b/src/ui/pin.rs @@ -63,36 +63,42 @@ pub struct PinInfo { impl PinInfo { /// Sets the shape of the pin. - pub fn with_shape(mut self, shape: PinShape) -> Self { + #[must_use] + pub const fn with_shape(mut self, shape: PinShape) -> Self { self.shape = Some(shape); self } /// Sets the size of the pin. - pub fn with_size(mut self, size: f32) -> Self { + #[must_use] + pub const fn with_size(mut self, size: f32) -> Self { self.size = Some(size); self } /// Sets the fill color of the pin. - pub fn with_fill(mut self, fill: Color32) -> Self { + #[must_use] + pub const fn with_fill(mut self, fill: Color32) -> Self { self.fill = Some(fill); self } /// Sets the outline stroke of the pin. - pub fn with_stroke(mut self, stroke: Stroke) -> Self { + #[must_use] + pub const fn with_stroke(mut self, stroke: Stroke) -> Self { self.stroke = Some(stroke); self } /// Sets the style of the wire connected to the pin. - pub fn with_wire_style(mut self, wire_style: WireStyle) -> Self { + #[must_use] + pub const fn with_wire_style(mut self, wire_style: WireStyle) -> Self { self.wire_style = Some(wire_style); self } /// Creates a circle pin. + #[must_use] pub fn circle() -> Self { PinInfo { shape: Some(PinShape::Circle), @@ -101,6 +107,7 @@ impl PinInfo { } /// Creates a triangle pin. + #[must_use] pub fn triangle() -> Self { PinInfo { shape: Some(PinShape::Triangle), @@ -109,6 +116,7 @@ impl PinInfo { } /// Creates a square pin. + #[must_use] pub fn square() -> Self { PinInfo { shape: Some(PinShape::Square), @@ -117,6 +125,7 @@ impl PinInfo { } /// Creates a star pin. + #[must_use] pub fn star() -> Self { PinInfo { shape: Some(PinShape::Star), @@ -125,25 +134,29 @@ impl PinInfo { } /// Returns the shape of the pin. + #[must_use] pub fn get_shape(&self, snarl_style: &SnarlStyle) -> PinShape { - self.shape.unwrap_or(snarl_style.get_pin_shape()) + self.shape.unwrap_or_else(|| snarl_style.get_pin_shape()) } /// Returns fill color of the pin. + #[must_use] pub fn get_fill(&self, snarl_style: &SnarlStyle, style: &Style) -> Color32 { - self.fill.unwrap_or(snarl_style.get_pin_fill(style)) + self.fill.unwrap_or_else(|| snarl_style.get_pin_fill(style)) } /// Returns outline stroke of the pin. + #[must_use] pub fn get_stroke(&self, snarl_style: &SnarlStyle, style: &Style, scale: f32) -> Stroke { self.stroke .zoomed(scale) - .unwrap_or(snarl_style.get_pin_stroke(scale, style)) + .unwrap_or_else(|| snarl_style.get_pin_stroke(scale, style)) } /// Draws the pin and returns color. /// /// Wires are drawn with returned color by default. + #[must_use] pub fn draw( &self, pos: Pos2, @@ -207,16 +220,16 @@ pub fn draw_pin( PinShape::Star => { let points = vec![ - pos + size * 0.700000 * vec2(0.0, -1.0), - pos + size * 0.267376 * vec2(-0.587785, -0.809017), - pos + size * 0.700000 * vec2(-0.951057, -0.309017), - pos + size * 0.267376 * vec2(-0.951057, 0.309017), - pos + size * 0.700000 * vec2(-0.587785, 0.809017), - pos + size * 0.267376 * vec2(0.0, 1.0), - pos + size * 0.700000 * vec2(0.587785, 0.809017), - pos + size * 0.267376 * vec2(0.951057, 0.309017), - pos + size * 0.700000 * vec2(0.951057, -0.309017), - pos + size * 0.267376 * vec2(0.587785, -0.809017), + pos + size * 0.700_000 * vec2(0.0, -1.0), + pos + size * 0.267_376 * vec2(-0.587_785, -0.809_017), + pos + size * 0.700_000 * vec2(-0.951_057, -0.309_017), + pos + size * 0.267_376 * vec2(-0.951_057, 0.309_017), + pos + size * 0.700_000 * vec2(-0.587_785, 0.809_017), + pos + size * 0.267_376 * vec2(0.0, 1.0), + pos + size * 0.700_000 * vec2(0.587_785, 0.809_017), + pos + size * 0.267_376 * vec2(0.951_057, 0.309_017), + pos + size * 0.700_000 * vec2(0.951_057, -0.309_017), + pos + size * 0.267_376 * vec2(0.587_785, -0.809_017), ]; painter.add(Shape::Path(PathShape { diff --git a/src/ui/state.rs b/src/ui/state.rs index 8bb9e0a..380c248 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -7,7 +7,6 @@ use crate::{InPinId, NodeId, OutPinId, Snarl}; use super::SnarlStyle; /// Node UI state. - pub struct NodeState { /// Node size for this frame. /// It is updated to fit content. @@ -27,16 +26,16 @@ struct NodeData { impl NodeState { pub fn load(cx: &Context, id: Id, spacing: &Spacing, scale: f32) -> Self { - match cx.data_mut(|d| d.get_temp::(id)) { - Some(data) => NodeState { + cx.data_mut(|d| d.get_temp::(id)).map_or_else( + || Self::initial(id, spacing, scale), + |data| NodeState { size: data.unscaled_size * scale, header_height: data.unscaled_header_height * scale, id, scale, dirty: false, }, - None => Self::initial(id, spacing, scale), - } + ) } pub fn clear(self, cx: &Context) { @@ -52,7 +51,7 @@ impl NodeState { unscaled_size: self.size / self.scale, unscaled_header_height: self.header_height / self.scale, }, - ) + ); }); } } @@ -79,18 +78,19 @@ impl NodeState { } } - pub fn header_height(&mut self) -> f32 { + pub const fn header_height(&self) -> f32 { self.header_height } pub fn set_header_height(&mut self, height: f32) { + #[allow(clippy::float_cmp)] if self.header_height != height { self.header_height = height; self.dirty = true; } } - fn initial(id: Id, spacing: &Spacing, scale: f32) -> Self { + const fn initial(id: Id, spacing: &Spacing, scale: f32) -> Self { NodeState { size: spacing.interact_size, header_height: spacing.interact_size.y, @@ -192,16 +192,16 @@ impl SnarlStateData { d.remove::(id); } - if !self.selected_nodes.is_empty() { - d.insert_temp::(id, SelectedNodes(self.selected_nodes)); - } else { + if self.selected_nodes.is_empty() { d.remove::(id); + } else { + d.insert_temp::(id, SelectedNodes(self.selected_nodes)); } - if !self.draw_order.is_empty() { - d.insert_temp::(id, DrawOrder(self.draw_order)); - } else { + if self.draw_order.is_empty() { d.remove::(id); + } else { + d.insert_temp::(id, DrawOrder(self.draw_order)); } }); } @@ -250,14 +250,16 @@ impl SnarlState { let new_scale = cx.animate_value_with_time(id.with("zoom-scale"), data.target_scale, 0.1); - let mut dirty = false; - if new_scale != data.scale { + #[allow(clippy::float_cmp)] + let mut dirty = if new_scale == data.scale { + false + } else { let a = pivot + data.offset - viewport.center().to_vec2(); data.offset += a * new_scale / data.scale - a; data.scale = new_scale; - dirty = true; - } + true + }; dirty |= prune_selected_nodes(&mut data.selected_nodes, snarl); @@ -278,7 +280,7 @@ impl SnarlState { fn initial(id: Id, viewport: Rect, snarl: &Snarl, style: &SnarlStyle) -> Self { let mut bb = Rect::NOTHING; - for (_, node) in snarl.nodes.iter() { + for (_, node) in &snarl.nodes { bb.extend_with(node.pos); } @@ -340,12 +342,12 @@ impl SnarlState { } #[inline(always)] - pub fn scale(&self) -> f32 { + pub const fn scale(&self) -> f32 { self.scale } #[inline(always)] - pub fn offset(&self) -> Vec2 { + pub const fn offset(&self) -> Vec2 { self.offset } @@ -457,11 +459,11 @@ impl SnarlState { } } - pub fn has_new_wires(&self) -> bool { + pub const fn has_new_wires(&self) -> bool { self.new_wires.is_some() } - pub fn new_wires(&self) -> Option<&NewWires> { + pub const fn new_wires(&self) -> Option<&NewWires> { self.new_wires.as_ref() } @@ -485,7 +487,7 @@ impl SnarlState { self.dirty = true; } - pub(crate) fn is_link_menu_open(&self) -> bool { + pub(crate) const fn is_link_menu_open(&self) -> bool { self.is_link_menu_open } @@ -535,18 +537,14 @@ impl SnarlState { } self.deselect_all_nodes(); - self.selected_nodes.push(node); - self.dirty = true; - } else { - if let Some(pos) = self.selected_nodes.iter().position(|n| *n == node) { - if pos == self.selected_nodes.len() - 1 { - return; - } - self.selected_nodes.remove(pos); + } else if let Some(pos) = self.selected_nodes.iter().position(|n| *n == node) { + if pos == self.selected_nodes.len() - 1 { + return; } - self.selected_nodes.push(node); - self.dirty = true; + self.selected_nodes.remove(pos); } + self.selected_nodes.push(node); + self.dirty = true; } pub fn select_many_nodes(&mut self, reset: bool, nodes: impl Iterator) { @@ -593,7 +591,7 @@ impl SnarlState { self.rect_selection = None; } - pub fn is_rect_selection(&self) -> bool { + pub const fn is_rect_selection(&self) -> bool { self.rect_selection.is_some() } diff --git a/src/ui/viewer.rs b/src/ui/viewer.rs index fe4bbfb..ec37d81 100644 --- a/src/ui/viewer.rs +++ b/src/ui/viewer.rs @@ -4,7 +4,7 @@ use crate::{InPin, InPinId, NodeId, OutPin, OutPinId, Snarl}; use super::{pin::AnyPins, BackgroundPattern, NodeLayout, PinInfo, SnarlStyle, Viewport}; -/// SnarlViewer is a trait for viewing a Snarl. +/// `SnarlViewer` is a trait for viewing a Snarl. /// /// It can extract necessary data from the nodes and controls their /// response to certain events. @@ -353,7 +353,7 @@ pub trait SnarlViewer { let _ = snarl; if let Some(background) = background { - background.draw(viewport, snarl_style, style, painter) + background.draw(viewport, snarl_style, style, painter); } } } diff --git a/src/ui/wire.rs b/src/ui/wire.rs index a36455c..8b249df 100644 --- a/src/ui/wire.rs +++ b/src/ui/wire.rs @@ -38,7 +38,7 @@ pub enum WireStyle { }, } -pub(crate) fn pick_wire_style( +pub const fn pick_wire_style( default: WireStyle, left: Option, right: Option, @@ -46,10 +46,9 @@ pub(crate) fn pick_wire_style( match (left, right) { (None, None) => default, (Some(one), None) | (None, Some(one)) => one, - (Some(WireStyle::Bezier3), Some(WireStyle::Bezier3)) => WireStyle::Bezier3, (Some(WireStyle::Bezier5), Some(WireStyle::Bezier5)) => WireStyle::Bezier5, - (Some(WireStyle::Bezier3), Some(WireStyle::Bezier5)) - | (Some(WireStyle::Bezier5), Some(WireStyle::Bezier3)) => WireStyle::Bezier3, + (Some(WireStyle::Bezier3 | WireStyle::Bezier5), Some(WireStyle::Bezier3)) + | (Some(WireStyle::Bezier3), Some(WireStyle::Bezier5)) => WireStyle::Bezier3, ( Some(WireStyle::AxisAligned { corner_radius: a }), Some(WireStyle::AxisAligned { corner_radius: b }), @@ -95,8 +94,8 @@ fn wire_bezier_5(frame_size: f32, from: Pos2, to: Pos2) -> [Pos2; 6] { [from, from_2, middle_1, middle_2, to_2, to] } else if from_2.x <= to_2.x { - let t = - (between - (to_2.y - from_2.y).abs()) / (frame_size * 2.0 - (to_2.y - from_2.y).abs()); + let t = (between - (to_2.y - from_2.y).abs()) + / frame_size.mul_add(2.0, -(to_2.y - from_2.y).abs()); let mut middle_1 = from_2 + (to_2 - from_2).normalized() * frame_size; let mut middle_2 = to_2 + (from_2 - to_2).normalized() * frame_size; @@ -104,7 +103,10 @@ fn wire_bezier_5(frame_size: f32, from: Pos2, to: Pos2) -> [Pos2; 6] { if from_2.y >= to_2.y + frame_size { let u = (from_2.y - to_2.y - frame_size) / frame_size; - let t0_middle_1 = pos2(from_2.x + (1.0 - u) * frame_size, from_2.y - frame_size * u); + let t0_middle_1 = pos2( + (1.0 - u).mul_add(frame_size, from_2.x), + frame_size.mul_add(-u, from_2.y), + ); let t0_middle_2 = pos2(to_2.x, to_2.y + frame_size); middle_1 = t0_middle_1.lerp(middle_1, t); @@ -112,7 +114,10 @@ fn wire_bezier_5(frame_size: f32, from: Pos2, to: Pos2) -> [Pos2; 6] { } else if from_2.y >= to_2.y { let u = (from_2.y - to_2.y) / frame_size; - let t0_middle_1 = pos2(from_2.x + u * frame_size, from_2.y + frame_size * (1.0 - u)); + let t0_middle_1 = pos2( + u.mul_add(frame_size, from_2.x), + frame_size.mul_add(1.0 - u, from_2.y), + ); let t0_middle_2 = pos2(to_2.x, to_2.y + frame_size); middle_1 = t0_middle_1.lerp(middle_1, t); @@ -121,7 +126,10 @@ fn wire_bezier_5(frame_size: f32, from: Pos2, to: Pos2) -> [Pos2; 6] { let u = (to_2.y - from_2.y - frame_size) / frame_size; let t0_middle_1 = pos2(from_2.x, from_2.y + frame_size); - let t0_middle_2 = pos2(to_2.x - (1.0 - u) * frame_size, to_2.y - frame_size * u); + let t0_middle_2 = pos2( + (1.0 - u).mul_add(-frame_size, to_2.x), + frame_size.mul_add(-u, to_2.y), + ); middle_1 = t0_middle_1.lerp(middle_1, t); middle_2 = t0_middle_2.lerp(middle_2, t); @@ -129,7 +137,10 @@ fn wire_bezier_5(frame_size: f32, from: Pos2, to: Pos2) -> [Pos2; 6] { let u = (to_2.y - from_2.y) / frame_size; let t0_middle_1 = pos2(from_2.x, from_2.y + frame_size); - let t0_middle_2 = pos2(to_2.x - u * frame_size, to_2.y + frame_size * (1.0 - u)); + let t0_middle_2 = pos2( + u.mul_add(-frame_size, to_2.x), + frame_size.mul_add(1.0 - u, to_2.y), + ); middle_1 = t0_middle_1.lerp(middle_1, t); middle_2 = t0_middle_2.lerp(middle_2, t); @@ -138,7 +149,7 @@ fn wire_bezier_5(frame_size: f32, from: Pos2, to: Pos2) -> [Pos2; 6] { } [from, from_2, middle_1, middle_2, to_2, to] - } else if from_2.y >= to_2.y + frame_size * 2.0 { + } else if from_2.y >= frame_size.mul_add(2.0, to_2.y) { let middle_1 = pos2(from_2.x, from_2.y - frame_size); let middle_2 = pos2(to_2.x, to_2.y + frame_size); @@ -146,18 +157,24 @@ fn wire_bezier_5(frame_size: f32, from: Pos2, to: Pos2) -> [Pos2; 6] { } else if from_2.y >= to_2.y + frame_size { let t = (from_2.y - to_2.y - frame_size) / frame_size; - let middle_1 = pos2(from_2.x + (1.0 - t) * frame_size, from_2.y - frame_size * t); + let middle_1 = pos2( + (1.0 - t).mul_add(frame_size, from_2.x), + frame_size.mul_add(-t, from_2.y), + ); let middle_2 = pos2(to_2.x, to_2.y + frame_size); [from, from_2, middle_1, middle_2, to_2, to] } else if from_2.y >= to_2.y { let t = (from_2.y - to_2.y) / frame_size; - let middle_1 = pos2(from_2.x + t * frame_size, from_2.y + frame_size * (1.0 - t)); + let middle_1 = pos2( + t.mul_add(frame_size, from_2.x), + frame_size.mul_add(1.0 - t, from_2.y), + ); let middle_2 = pos2(to_2.x, to_2.y + frame_size); [from, from_2, middle_1, middle_2, to_2, to] - } else if to_2.y >= from_2.y + frame_size * 2.0 { + } else if to_2.y >= frame_size.mul_add(2.0, from_2.y) { let middle_1 = pos2(from_2.x, from_2.y + frame_size); let middle_2 = pos2(to_2.x, to_2.y - frame_size); @@ -166,14 +183,20 @@ fn wire_bezier_5(frame_size: f32, from: Pos2, to: Pos2) -> [Pos2; 6] { let t = (to_2.y - from_2.y - frame_size) / frame_size; let middle_1 = pos2(from_2.x, from_2.y + frame_size); - let middle_2 = pos2(to_2.x - (1.0 - t) * frame_size, to_2.y - frame_size * t); + let middle_2 = pos2( + (1.0 - t).mul_add(-frame_size, to_2.x), + frame_size.mul_add(-t, to_2.y), + ); [from, from_2, middle_1, middle_2, to_2, to] } else if to_2.y >= from_2.y { let t = (to_2.y - from_2.y) / frame_size; let middle_1 = pos2(from_2.x, from_2.y + frame_size); - let middle_2 = pos2(to_2.x - t * frame_size, to_2.y + frame_size * (1.0 - t)); + let middle_2 = pos2( + t.mul_add(-frame_size, to_2.x), + frame_size.mul_add(1.0 - t, to_2.y), + ); [from, from_2, middle_1, middle_2, to_2, to] } else { @@ -183,7 +206,7 @@ fn wire_bezier_5(frame_size: f32, from: Pos2, to: Pos2) -> [Pos2; 6] { #[allow(clippy::too_many_arguments)] pub fn draw_wire( - ui: &mut Ui, + ui: &Ui, shapes: &mut Vec, frame_size: f32, upscale: bool, @@ -493,6 +516,7 @@ struct AxisAlignedWire { turns: [(Pos2, f32); 4], } +#[allow(clippy::too_many_lines)] fn wire_axis_aligned(corner_radius: f32, frame_size: f32, from: Pos2, to: Pos2) -> AxisAlignedWire { if from.x + frame_size <= to.x - frame_size { let mid = pos2((from.x + to.x) / 2.0, (from.y + to.y) / 2.0); @@ -715,19 +739,20 @@ fn draw_axis_aligned( let samples = turn_samples_number(radius, stroke.width); for j in 1..samples { + #[allow(clippy::cast_precision_loss)] let a = std::f32::consts::FRAC_PI_2 * (j as f32 / samples as f32); let (sin_a, cos_a) = a.sin_cos(); if i % 2 == 0 { path.push(pos2( - turn.x * (1.0 - sin_a) + wire.points[i + 1].x * sin_a, - wire.points[i].y * cos_a + turn.y * (1.0 - cos_a), + turn.x.mul_add(1.0 - sin_a, wire.points[i + 1].x * sin_a), + wire.points[i].y.mul_add(cos_a, turn.y * (1.0 - cos_a)), )); } else { path.push(pos2( - wire.points[i].x * cos_a + turn.x * (1.0 - cos_a), - turn.y * (1.0 - sin_a) + wire.points[i + 1].y * sin_a, + wire.points[i].x.mul_add(cos_a, turn.x * (1.0 - cos_a)), + turn.y.mul_add(1.0 - sin_a, wire.points[i + 1].y * sin_a), )); } } diff --git a/src/ui/zoom.rs b/src/ui/zoom.rs index 82f9117..62bb6e6 100644 --- a/src/ui/zoom.rs +++ b/src/ui/zoom.rs @@ -186,7 +186,7 @@ where #[inline(always)] fn zoom(&mut self, zoom: f32) { if let Some(value) = self { - value.zoom(zoom) + value.zoom(zoom); } } }