Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom pin drawing functionality to PinInfo struct #48

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

oligamiq
Copy link

@oligamiq oligamiq commented Dec 28, 2024

I used it in the previous version.

7128496

It is not a very good implementation, but we want to use it now, so it is implemented with compatibility in mind.

@zakarumych
Copy link
Owner

This functionality is available with SnarlViewer::draw_input_pin and SnarlViewer::draw_output_pin.
Is it inconvenient to use?

@oligamiq
Copy link
Author

I'll give it a try.

@oligamiq
Copy link
Author

It is very difficult to use. show_input returns PinInfo, and draw_input_pin uses the pin information and PinInfo to render. These are simple enough if you only want to change the existing pin rendering method. However, you cannot pass information representing a new pin, and you need to use something like a HashMap in the structure implementing SnarlViewer to store pin information.

This approach is neither intuitive nor direct. However, I don’t think my pull request is necessarily correct either. To pass information, PinInfo would need to hold some additional data.

Another idea I came up with is:

pub custom_data: Option<Box<dyn std::any::Any + Sync + Send>>,

Add this to the structure, or instead of PinInfo, prepare a marker trait and make it possible to pass types that implement this trait.

pub trait PinInfoMarker: Sync + Send;
impl PinInfoMarker for PinInfo {};
fn show_input(&mut self, pin: &InPin, ui: &mut Ui, scale: f32, snarl: &mut Snarl<T>)
    -> impl PinInfoMarker + 'static;
fn check() {
    core::any::type_name::<PinInfo>() == core::any::type_name_of_val(pin_info)
}

@oligamiq
Copy link
Author

The content that can be processed by draw_input_pin is the same as what can be processed by show_input, with the only difference being that it can render pins. Moreover, show_input and draw_input_pin have a 1:1 correspondence, and draw_input_pin operates based on the information passed from show_input. However, the information passed from show_input is very limited. Since similar processing should not be performed in both functions, the current structure does not seem to be a very good design.

@zakarumych
Copy link
Owner

Aha, so the problem is that you need complex computations to figure out how you want to draw the pin in draw_input_pin, while you already done it in show_input?

For example, if you only need to check some data in the node, e.g. match enum variant and compare some value in it, then it's cheaper than boxing a closure in show_input.

But I guess, removing a feature like this was wrong move anyway, so I'll put it back.

@oligamiq
Copy link
Author

I think I would prefer Trait instead of Closure.

@zakarumych
Copy link
Owner

Do you know how to avoid boxing with a trait?

@oligamiq
Copy link
Author

oligamiq commented Dec 30, 2024

The content of the pull request has been updated.

This is a disruptive change. Below is the code I used when I changed my code to be compilable.

※The associated types of traits and their related features are still largely unstable, so caution is advised.

impl SnarlViewer<FlowNodes> for FlowNodesViewer {
    type Drawer = MyDrawer;

    fn inputs(&mut self, node: &FlowNodes) -> usize {
        node.to_as_info().inputs()
    }

    fn outputs(&mut self, node: &FlowNodes) -> usize {
        node.to_as_info().outputs()
    }

    fn show_input(
        &mut self,
        pin: &egui_snarl::InPin,
        ui: &mut egui::Ui,
        scale: f32,
        snarl: &mut egui_snarl::Snarl<FlowNodes>,
    ) -> MyPinInfo {
        self.show_input(pin, ui, scale, snarl)(snarl, ui)
    }
use egui_snarl::ui::{PinDrawer, PinShape};

use crate::prelude::{egui::*, snarl::*};

pub struct CustomPinInfo;

pub type MyPinInfo = PinInfo<MyDrawer>;

#[derive(Debug, Clone, Copy)]
pub enum MyDrawer {
    Lock,
    Setting { teeth: usize },
    Preset(PinShape),
}

impl From<PinShape> for MyDrawer {
    fn from(pin_shape: PinShape) -> Self {
        MyDrawer::Preset(pin_shape)
    }
}

impl PinDrawer for MyDrawer {
    fn draw(
        &self,
        painter: &egui::Painter,
        fill: egui::Color32,
        stroke: egui::Stroke,
        pos: Pos2,
        size: f32,
    ) {
        match self {
            MyDrawer::Lock => {
                let rect = Rect::from_center_size(pos, egui::vec2(size, size));
                let key_width = rect.width();
                let key_height = rect.height() * 0.7;

                // 丸い頭部分
                painter.circle_stroke(
                    Pos2::new(rect.center().x, rect.center().y - rect.height() / 4.),
                    rect.height() / 3.,
                    stroke,
                );

                // 鍵のシャフト部分(長方形)
                painter.rect(
                    egui::Rect::from_center_size(
                        Pos2::new(rect.center().x, rect.center().y + rect.height() / 4.),
                        Vec2::new(key_width, key_height),
                    ),
                    0.0,
                    fill,
                    stroke,
                );
            }
            MyDrawer::Setting { teeth } => {
                let teeth = *teeth;

                let rect = Rect::from_center_size(pos, egui::vec2(size, size));

                let inner_radius = rect.size().min_elem() * 0.6; // 中央の円のサイズ
                let outer_radius = rect.size().min_elem() * 0.8;
                let center = rect.center();

                let mut pointers = Vec::with_capacity(teeth * 3);

                for i in 0..8 {
                    let inner_angle = (i as f32 - 0.3) * std::f32::consts::PI * 2.0 / teeth as f32;
                    let inner_x = inner_radius * inner_angle.cos();
                    let inner_y = inner_radius * inner_angle.sin();
                    let inner_point = Pos2::new(center.x + inner_x, center.y + inner_y);

                    let angle = (i as f32 - 0.1) * (std::f32::consts::PI * 2.0 / teeth as f32);
                    let x = outer_radius * angle.cos();
                    let y = outer_radius * angle.sin();
                    let outer_point = Pos2::new(center.x + x, center.y + y);

                    let angle = (i as f32 + 0.1) * (std::f32::consts::PI * 2.0 / teeth as f32);
                    let x = outer_radius * angle.cos();
                    let y = outer_radius * angle.sin();
                    let outer_point2 = Pos2::new(center.x + x, center.y + y);

                    let angle = (i as f32 + 0.3) * (std::f32::consts::PI * 2.0 / teeth as f32);
                    let x = inner_radius * angle.cos();
                    let y = inner_radius * angle.sin();
                    let inner_point2 = Pos2::new(center.x + x, center.y + y);

                    pointers.push(inner_point);
                    pointers.push(outer_point);
                    pointers.push(outer_point2);
                    pointers.push(inner_point2);
                }

                let mut shape = PathShape::closed_line(pointers, stroke);
                shape.fill = fill;

                painter.add(Shape::Path(shape));
            }
            MyDrawer::Preset(pin_shape) => {
                pin_shape.draw(painter, fill, stroke, pos, size);
            }
        }
    }
}

impl CustomPinInfo {
    pub fn lock() -> PinInfo<MyDrawer> {
        PinInfo::shape(MyDrawer::Lock)
    }

    pub fn setting(teeth: usize) -> PinInfo<MyDrawer> {
        PinInfo::shape(MyDrawer::Setting { teeth })
    }

    pub fn ok_status() -> PinInfo<MyDrawer> {
        PinInfo::circle().with_fill(egui::Color32::from_rgb(0, 0, 255))
    }

    pub fn ng_status() -> PinInfo<MyDrawer> {
        PinInfo::circle().with_fill(egui::Color32::from_rgb(255, 0, 0))
    }

    pub fn none_status() -> PinInfo<MyDrawer> {
        PinInfo::circle().with_fill(egui::Color32::from_rgb(0, 0, 0))
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants