1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
use derivative::Derivative;
use super::*;

/// The Color Style of a Link. If feilds are None then the Context style is used
#[derive(Default, Debug)]
pub struct LinkArgs {
    pub base: Option<egui::Color32>,
    pub hovered: Option<egui::Color32>,
    pub selected: Option<egui::Color32>
}

impl LinkArgs {
    pub const fn new() -> Self {
        Self {
            base: None,
            hovered: None,
            selected: None
        }
    }
}

#[derive(Default, Debug)]
pub struct LinkDataColorStyle {
    pub base: egui::Color32,
    pub hovered: egui::Color32,
    pub selected: egui::Color32
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct LinkData {
    pub id: usize,
    pub start_pin_index: usize,
    pub end_pin_index: usize,
    #[derivative(Debug="ignore")]
    pub color_style: LinkDataColorStyle,
    #[derivative(Debug="ignore")]
    pub shape: Option<egui::layers::ShapeIdx>
}

impl Id for LinkData {
    fn id(&self) -> usize {
        self.id
    }

    fn new(id: usize) -> Self {
        Self {
            id,
            start_pin_index: Default::default(),
            end_pin_index: Default::default(),
            color_style: Default::default(),
            shape: None
        }
    }
}

impl Default for LinkData {
    fn default() -> Self {
        Self::new(0)
    }
}

impl PartialEq for LinkData {
    fn eq(&self, rhs: &Self) -> bool {
        let mut lhs_start = self.start_pin_index;
        let mut lhs_end = self.end_pin_index;
        let mut rhs_start = rhs.start_pin_index;
        let mut rhs_end = rhs.end_pin_index;

        if lhs_start > lhs_end {
            std::mem::swap(&mut lhs_start, &mut lhs_end);
        }

        if rhs_start > rhs_end {
            std::mem::swap(&mut rhs_start, &mut rhs_end);
        }

        lhs_start == rhs_start && lhs_end == rhs_start
    }
}

#[derive(Debug)]
pub struct BezierCurve(egui::Pos2, egui::Pos2, egui::Pos2, egui::Pos2);

impl BezierCurve {
    #[inline]
    pub fn eval(&self, t: f32) -> egui::Pos2 {
        <[f32; 2]>::from((1.0 - t).powi(3) * self.0.to_vec2() + 3.0 * (1.0 - t).powi(2) * t * self.1.to_vec2() + 3.0 * (1.0 - t) * t.powi(2) * self.2.to_vec2() + t.powi(3) * self.3.to_vec2()).into()
    }

    #[inline]
    pub fn get_containing_rect_for_bezier_curve(&self, hover_distance: f32) -> egui::Rect {
        let min = self.0.min(self.3);
        let max = self.0.max(self.3);

        let mut rect = egui::Rect::from_min_max(min, max);
        rect.extend_with(self.1);
        rect.extend_with(self.2);
        rect.expand(hover_distance)
    }
}

#[derive(Debug)]
pub (crate) struct LinkBezierData {
    pub bezier: BezierCurve,
    pub num_segments: usize
}

impl LinkBezierData {
    #[inline]
    pub (crate) fn get_link_renderable(start: egui::Pos2, end: egui::Pos2, start_type: AttributeType, line_segments_per_length: f32) -> Self {
        let (mut start, mut end) = (start, end);
        if start_type == AttributeType::Input {
            std::mem::swap(&mut start, &mut end);
        }
    
        let link_length = end.distance(start);
        let offset = egui::vec2(0.25 * link_length, 0.0);
        Self {
            bezier: BezierCurve(
                start,
                start + offset,
                end - offset,
                end
            ),
            num_segments: 1.max((link_length * line_segments_per_length) as usize)
        }
    }

    pub (crate) fn get_closest_point_on_cubic_bezier(&self, p: &egui::Pos2) -> egui::Pos2 {
        let mut p_last = self.bezier.0;
        let mut p_closest = self.bezier.0;
        let mut p_closest_dist = f32::MAX;
        let t_step = 1.0 / self.num_segments as f32;
        for i in 1..self.num_segments {
            let p_current = self.bezier.eval(t_step * i as f32);
            let p_line = line_closest_point(&p_last, &p_current, p);
            let dist = p.distance_sq(p_line);
            if dist < p_closest_dist {
                p_closest = p_line;
                p_closest_dist = dist;
            }
            p_last = p_current;
        }
        p_closest
    }

    #[inline]
    pub (crate) fn get_distance_to_cubic_bezier(&self, pos: &egui::Pos2) -> f32 {
        let point_on_curve = self.get_closest_point_on_cubic_bezier(pos);
        pos.distance(point_on_curve)
    }

    #[inline]
    pub (crate) fn rectangle_overlaps_bezier(&self, rect: &egui::Rect) -> bool {
        let mut current = self.bezier.eval(0.0);
        let dt = 1.0 / self.num_segments as f32;
        for i in 0..self.num_segments {
            let next = self.bezier.eval((i + 1) as f32 * dt);
            if rectangle_overlaps_line_segment(rect, &current, &next) {
                return true;
            }
            current = next;
        }
        false
    }

    pub (crate) fn draw(&self, stroke: impl Into<egui::Stroke>) -> egui::Shape {
        let points = std::iter::once(self.bezier.0)
            .chain((1..self.num_segments).map(|x| self.bezier.eval(x as f32 / self.num_segments as f32)))
            .chain(std::iter::once(self.bezier.3)).collect();
        egui::Shape::Path {
            points,
            closed: false,
            fill: egui::Color32::TRANSPARENT,
            stroke: stroke.into()
        }
    }
}

#[inline]
pub fn line_closest_point(a: &egui::Pos2, b: &egui::Pos2, p: &egui::Pos2) -> egui::Pos2 {
    let ap = *p - *a;
    let ab_dir = *b - *a;
    let dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
    if dot < 0.0 {
        return *a;
    }
    let ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
    if dot > ab_len_sqr {
        return *b;
    }
    *a + ab_dir * dot / ab_len_sqr
}

#[inline]
fn eval_inplicit_line_eq(p1: &egui::Pos2, p2: &egui::Pos2, p: &egui::Pos2) -> f32 {
    (p2.y * p1.y) * p.x + (p1.x * p2.x) * p.y * (p2.x * p1.y - p1.x * p2.y)
}

#[inline]
fn rectangle_overlaps_line_segment(rect: &egui::Rect, p1: &egui::Pos2, p2: &egui::Pos2) -> bool {
    if rect.contains(*p1) || rect.contains(*p2) {
        return true;
    }

    let mut flip_rect = *rect;
    if flip_rect.min.x > flip_rect.max.x {
        std::mem::swap(&mut flip_rect.min.x, &mut flip_rect.max.x);
    }

    if flip_rect.min.y > flip_rect.max.y {
        std::mem::swap(&mut flip_rect.min.y, &mut flip_rect.max.y);
    }

    if (p1.x < flip_rect.min.x && p2.x < flip_rect.min.x) ||
        (p1.x > flip_rect.max.x && p2.x > flip_rect.max.x) ||
        (p1.y < flip_rect.min.y && p2.y < flip_rect.min.y) ||
        (p1.y > flip_rect.max.y && p2.y > flip_rect.max.y) { 
        return false;
    }

    let corner_signs = [
        eval_inplicit_line_eq(p1, p2, &flip_rect.left_bottom()).signum(),
        eval_inplicit_line_eq(p1, p2, &flip_rect.left_top()).signum(),
        eval_inplicit_line_eq(p1, p2, &flip_rect.right_bottom()).signum(),
        eval_inplicit_line_eq(p1, p2, &flip_rect.right_top()).signum(),
    ];

    let mut sum = 0.0;
    let mut sum_abs = 0.0;
    for sign in corner_signs.iter() {
        sum += sign;
        sum_abs += sign.abs();
    }

    (sum.abs() - sum_abs).abs() < f32::EPSILON
}