makepad_draw/shader/
draw_line.rs

1use {
2    crate::{
3        makepad_platform::*,
4        //draw_list_2d::ManyInstances,
5        //geometry::GeometryQuad2D,
6        cx_2d::Cx2d,
7        //turtle::{Walk, Layout},
8        DrawQuad
9    },
10};
11
12live_design! {
13    use makepad_draw::shader::std::*;
14    DrawLine= {{DrawLine}} {
15       
16        fn stroke(self, side:float, progress: float) -> vec4{
17            return self.color;
18        }
19
20        fn pixel(self) -> vec4 {
21            let p = self.pos * self.rect_size;
22            let b = self.line_end;
23            let a = self.line_start;
24            
25            let ba = b-a;
26            let pa = p-a;
27            let h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
28            let dist= length(pa-h*ba)
29            
30            let linemult = smoothstep(self.half_line_width-1., self.half_line_width, dist);
31            let C = self.stroke(dist, h);
32            return vec4(C.xyz*(1.-linemult),(1.0-linemult)*C.a);
33        }
34    }
35}
36
37#[derive(Live, LiveHook, LiveRegister)]
38#[repr(C)]
39pub struct DrawLine {
40    #[deref] pub draw_super: DrawQuad,
41    #[calc]  pub line_start: Vec2,
42    #[calc]  pub line_end: Vec2,
43    #[calc]  pub half_line_width: f32,
44    #[calc]  pub color: Vec4,    
45}
46
47impl DrawLine
48{
49    pub fn  get_bezier_point(&mut self,t: f64, control_points: &Vec<DVec2>,index: usize, count: usize) -> DVec2
50    {
51        if count == 1
52        {
53            return control_points[index];
54        }
55        let  p0 = self.get_bezier_point(t, control_points, index, count - 1);
56        let  p1 = self.get_bezier_point(t, control_points, index + 1, count - 1);
57        return p0*t + p1*(1.0-t);
58    }
59
60
61    pub fn draw_bezier_abs(&mut self,  cx: &mut Cx2d, points: &Vec<DVec2>, color: Vec4, line_width: f64 )
62    {
63        let step = 0.01;
64        let mut t = 0.0;
65        //let mut omt = 1.0;
66        let mut from = points[points.len()-1];
67        while t< 1.
68        {
69            let nextt = t + step;
70            //let nextomt = 1.0 - nextt;
71            let next = self.get_bezier_point(nextt, &points, 0 , points.len())            ;
72           
73            self.draw_line_abs(cx, from, next, color, line_width);
74            from = next;
75            //omt = nextomt;
76            t = nextt;
77        }
78
79    }
80
81    pub fn draw_line_abs(&mut self,  cx: &mut Cx2d, line_start: DVec2, line_end: DVec2, color: Vec4, line_width: f64 )
82    {
83        let maxpixels = 300. as f64;
84        
85        let hw = line_width / 2.;
86
87        self.half_line_width = hw as f32;
88        self.color = color;
89
90
91        let linerect = line_end - line_start;
92        if (line_start.y - line_end.y).abs().floor() == 0.0
93            || (line_start.x - line_end.x).abs().floor() == 0.0
94        {
95            let r = Rect {
96                pos: dvec2(
97                    min(line_start.x, line_end.x) - hw,
98                    min(line_start.y, line_end.y) - hw,
99                ),
100                size: dvec2(
101                    linerect.x.abs() + line_width,
102                    linerect.y.abs() + line_width,
103                ),
104            };
105
106            self.line_start = (line_start - r.pos).into_vec2();
107            self.line_end = (line_end - r.pos).into_vec2();
108
109            self.draw_abs(cx, r);
110
111            return;
112        }
113
114        if linerect.x.abs() > linerect.y.abs()
115        // more horizontal than vertical
116        {
117            let mut actualstart = line_start;
118            let mut actualend = line_end;
119
120            if actualend.x < actualstart.x {
121                std::mem::swap(&mut actualstart, &mut actualend);
122            }
123
124            let delta = actualend - actualstart;
125            let normalizedelta = delta.normalize();
126            let xnormalizedelta = delta.normalize_to_x();
127            let normalizedarea = (xnormalizedelta.x * xnormalizedelta.y).abs();
128            let scaledup = (maxpixels / normalizedarea).sqrt();
129
130            let angle = delta.angle_in_radians();
131            let tanangle = angle.tan();
132
133            let clocktang = normalizedelta.clockwise_tangent();
134
135            let circlepoint = clocktang * hw;
136            let overside = hw - circlepoint.y;
137            let aanliggend = overside / tanangle;
138            let backoffset = circlepoint.x.abs() - aanliggend.abs();
139
140
141            let rectstart = Rect {
142                pos: actualstart - dvec2(hw, hw),
143                size: dvec2(hw - backoffset, line_width),
144            };
145
146            let rectend = Rect {
147                pos: actualend - dvec2(-backoffset, hw),
148                size: dvec2(hw - backoffset, line_width),
149            };
150            
151            let miny = min(rectstart.pos.y, rectend.pos.y);
152            let maxy = max(
153                rectend.pos.y + rectend.size.y,
154                rectstart.pos.y + rectstart.size.y,
155            );
156
157            let innerwidth = rectend.pos.x - (rectstart.pos.x + rectstart.size.x);
158            let numblocks = (innerwidth / scaledup).ceil();
159            let blockwidth = innerwidth / (numblocks as f64);
160
161            let step = dvec2(blockwidth, xnormalizedelta.y * blockwidth);
162            let mut adjust = -backoffset * 2. * xnormalizedelta.y;
163            if step.y < 0. {
164                adjust = step.y;
165            }
166            let blockheight = line_width / angle.cos() + step.y.abs();
167
168            let segmentstart = dvec2(rectstart.pos.x + rectstart.size.x, rectstart.pos.y + adjust);
169
170            for i in 0..(numblocks as i32) as i32 {
171                let mut r = Rect {
172                    pos: segmentstart + step * (i as f64),
173                    size: dvec2(blockwidth, blockheight),
174                };
175                r.clip_y_between(miny, maxy);
176
177                self.line_start = (actualstart - r.pos).into_vec2();
178                self.line_end = (actualend - r.pos).into_vec2();
179
180                self.draw_abs(cx, r);
181            }
182
183            self.line_start = (actualstart - rectstart.pos).into_vec2();
184            self.line_end = (actualend - rectstart.pos).into_vec2();
185
186            self.draw_abs(cx, rectstart);
187
188            self.line_start = (actualstart - rectend.pos).into_vec2();
189            self.line_end = (actualend - rectend.pos).into_vec2();
190
191            self.draw_abs(cx, rectend);
192
193
194        } else {
195             let mut actualstart = line_start;
196            let mut actualend: DVec2 = line_end;
197
198            if actualend.y < actualstart.y {
199                std::mem::swap(&mut actualstart, &mut actualend);
200            }
201            let delta = actualend - actualstart;
202            let normalizedelta = delta.normalize();
203            let ynormalizedelta = delta.normalize_to_y();
204            let normalizedarea = (ynormalizedelta.x * ynormalizedelta.y).abs();
205            let scaledup = (maxpixels / normalizedarea).sqrt();
206            let angle =  delta.angle_in_radians() - std::f64::consts::PI/2.;
207            let tanangle = angle.tan();  
208            let circlepoint = normalizedelta * hw;
209            let overside = hw - circlepoint.y;
210            let aanliggend = overside / tanangle;
211            let backoffset = circlepoint.x.abs() - aanliggend.abs();
212
213            let rectstart = Rect {
214                pos: actualstart - dvec2(hw, hw),
215                size: dvec2(line_width, hw - backoffset),
216            };
217            let rectend = Rect {
218                pos: actualend - dvec2(hw, -backoffset),
219                size: dvec2(line_width, hw - backoffset),
220            };
221            let minx = min(rectstart.pos.x, rectend.pos.x);
222            let maxx = max(
223                rectend.pos.x + rectend.size.x,
224                rectstart.pos.x + rectstart.size.x,
225            );
226
227            let innerheight = rectend.pos.y - (rectstart.pos.y + rectstart.size.y);
228            let numblocks = (innerheight / scaledup).ceil();
229            let blockheight = innerheight / (numblocks as f64);
230
231            let step = dvec2( ynormalizedelta.x * blockheight, blockheight);
232            let mut adjust = -backoffset * 2. * ynormalizedelta.x;
233            if step.x < 0. {
234                adjust = step.x;
235            }
236            let blockwidth = line_width / angle.cos() + step.x.abs();
237
238            
239          
240            let segmentstart = dvec2(rectstart.pos.x + adjust, rectstart.pos.y + rectstart.size.y);
241
242
243            for i in 0..(numblocks as i32) as i32 {
244                let mut r = Rect {
245                    pos: segmentstart + step * (i as f64),
246                    size: dvec2(blockwidth, blockheight),
247                };
248                r.clip_x_between(minx, maxx);
249
250                self.line_start = (actualstart - r.pos).into_vec2();
251                self.line_end = (actualend - r.pos).into_vec2();
252
253                self.draw_abs(cx, r);
254            }
255
256            self.line_start = (actualstart - rectstart.pos).into_vec2();
257            self.line_end = (actualend - rectstart.pos).into_vec2();
258
259            self.draw_abs(cx, rectstart);
260
261            self.line_start = (actualstart - rectend.pos).into_vec2();
262            self.line_end = (actualend - rectend.pos).into_vec2();
263
264            self.draw_abs(cx, rectend);
265
266            
267        }
268    }
269
270}