graphics/
triangulation.rs

1//! Methods for converting shapes into triangles.
2use interpolation::lerp;
3
4use crate::{
5    math::{multiply, orient, translate, Matrix2d, Scalar, Vec2d},
6    radians::Radians,
7    types::{Line, Polygon, Polygons, Radius, Rectangle, Resolution, SourceRectangle},
8    ImageSize, BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE,
9};
10
11/// Transformed x coordinate as f32.
12#[inline(always)]
13pub fn tx(m: Matrix2d, x: Scalar, y: Scalar) -> f32 {
14    (m[0][0] * x + m[0][1] * y + m[0][2]) as f32
15}
16
17/// Transformed y coordinate as f32.
18#[inline(always)]
19pub fn ty(m: Matrix2d, x: Scalar, y: Scalar) -> f32 {
20    (m[1][0] * x + m[1][1] * y + m[1][2]) as f32
21}
22
23/// Streams tweened polygons using linear interpolation.
24#[inline(always)]
25pub fn with_lerp_polygons_tri_list<F>(
26    m: Matrix2d,
27    polygons: Polygons<'_>,
28    tween_factor: Scalar,
29    f: F,
30) where
31    F: FnMut(&[[f32; 2]]),
32{
33    let poly_len = polygons.len() as Scalar;
34    // Map to interval between 0 and 1.
35    let tw = tween_factor % 1.0;
36    // Map negative values to positive.
37    let tw = if tw < 0.0 { tw + 1.0 } else { tw };
38    // Map to frame.
39    let tw = tw * poly_len;
40    // Get the current frame.
41    let frame = tw as usize;
42    // Get the next frame.
43    let next_frame = (frame + 1) % polygons.len();
44    let p0 = polygons[frame];
45    let p1 = polygons[next_frame];
46    // Get factor between frames.
47    let tw = tw - frame as Scalar;
48    let n = polygons[0].len();
49    stream_polygon_tri_list(m, (0..n).map(|j| lerp(&p0[j], &p1[j], &tw)), f);
50}
51
52/// Streams an ellipse specified by a resolution.
53#[inline(always)]
54pub fn with_ellipse_tri_list<F>(resolution: Resolution, m: Matrix2d, rect: Rectangle, f: F)
55where
56    F: FnMut(&[[f32; 2]]),
57{
58    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
59    let (cw, ch) = (0.5 * w, 0.5 * h);
60    let (cx, cy) = (x + cw, y + ch);
61    let n = resolution;
62    stream_polygon_tri_list(
63        m,
64        (0..n).map(|i| {
65            let angle = i as Scalar / n as Scalar * <Scalar as Radians>::_360();
66            [cx + angle.cos() * cw, cy + angle.sin() * ch]
67        }),
68        f,
69    );
70}
71
72/// Streams a round border line.
73#[inline(always)]
74pub fn with_round_border_line_tri_list<F>(
75    resolution_cap: Resolution,
76    m: Matrix2d,
77    line: Line,
78    round_border_radius: Radius,
79    f: F,
80) where
81    F: FnMut(&[[f32; 2]]),
82{
83    let radius = round_border_radius;
84    let (x1, y1, x2, y2) = (line[0], line[1], line[2], line[3]);
85    let (dx, dy) = (x2 - x1, y2 - y1);
86    let w = (dx * dx + dy * dy).sqrt();
87    let m = multiply(m, translate([x1, y1]));
88    let m = multiply(m, orient(dx, dy));
89    let n = resolution_cap * 2;
90    stream_polygon_tri_list(
91        m,
92        (0..n).map(|j| {
93            // Detect the half circle from index.
94            // There is one half circle at each end of the line.
95            // Together they form a full circle if
96            // the length of the line is zero.
97            match j {
98                j if j >= resolution_cap => {
99                    // Compute the angle to match start and end
100                    // point of half circle.
101                    // This requires an angle offset since
102                    // the other end of line is the first half circle.
103                    let angle = (j - resolution_cap) as Scalar / (resolution_cap - 1) as Scalar
104                        * <Scalar as Radians>::_180()
105                        + <Scalar as Radians>::_180();
106                    // Rotate 90 degrees since the line is horizontal.
107                    let angle = angle + <Scalar as Radians>::_90();
108                    [w + angle.cos() * radius, angle.sin() * radius]
109                }
110                j => {
111                    // Compute the angle to match start and end
112                    // point of half circle.
113                    let angle =
114                        j as Scalar / (resolution_cap - 1) as Scalar * <Scalar as Radians>::_180();
115                    // Rotate 90 degrees since the line is horizontal.
116                    let angle = angle + <Scalar as Radians>::_90();
117                    [angle.cos() * radius, angle.sin() * radius]
118                }
119            }
120        }),
121        f,
122    );
123}
124
125/// Streams a round rectangle.
126#[allow(clippy::identity_op)] // Identity operations are used for readibility.
127#[inline(always)]
128pub fn with_round_rectangle_tri_list<F>(
129    resolution_corner: Resolution,
130    m: Matrix2d,
131    rect: Rectangle,
132    round_radius: Radius,
133    f: F,
134) where
135    F: FnMut(&[[f32; 2]]),
136{
137    use vecmath::traits::FromPrimitive;
138
139    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
140    let radius = round_radius;
141    let n = resolution_corner * 4;
142    stream_polygon_tri_list(
143        m,
144        (0..n).map(|j| {
145            // Detect quarter circle from index.
146            // There is one quarter circle at each corner.
147            // Together they form a full circle if
148            // each side of rectangle is 2 times the radius.
149            match j {
150                j if j >= resolution_corner * 3 => {
151                    // Compute the angle to match start and end
152                    // point of quarter circle.
153                    // This requires an angle offset since this
154                    // is the last quarter.
155                    let angle: Scalar = (j - resolution_corner * 3) as Scalar
156                        / (resolution_corner - 1) as Scalar
157                        * <Scalar as Radians>::_90()
158                        + <Scalar as FromPrimitive>::from_f64(3.0) * <Scalar as Radians>::_90();
159                    // Set center of the circle to the last corner.
160                    let (cx, cy) = (x + w - radius, y + radius);
161                    [cx + angle.cos() * radius, cy + angle.sin() * radius]
162                }
163                j if j >= resolution_corner * 2 => {
164                    // Compute the angle to match start and end
165                    // point of quarter circle.
166                    // This requires an angle offset since
167                    // this is the second last quarter.
168                    let angle = (j - resolution_corner * 2) as Scalar
169                        / (resolution_corner - 1) as Scalar
170                        * <Scalar as Radians>::_90()
171                        + <Scalar as Radians>::_180();
172                    // Set center of the circle to the second last corner.
173                    let (cx, cy) = (x + radius, y + radius);
174                    [cx + angle.cos() * radius, cy + angle.sin() * radius]
175                }
176                j if j >= resolution_corner * 1 => {
177                    // Compute the angle to match start and end
178                    // point of quarter circle.
179                    // This requires an angle offset since
180                    // this is the second quarter.
181                    let angle = (j - resolution_corner) as Scalar
182                        / (resolution_corner - 1) as Scalar
183                        * <Scalar as Radians>::_90()
184                        + <Scalar as Radians>::_90();
185                    // Set center of the circle to the second corner.
186                    let (cx, cy) = (x + radius, y + h - radius);
187                    [cx + angle.cos() * radius, cy + angle.sin() * radius]
188                }
189                j => {
190                    // Compute the angle to match start and end
191                    // point of quarter circle.
192                    let angle = j as Scalar / (resolution_corner - 1) as Scalar
193                        * <Scalar as Radians>::_90();
194                    // Set center of the circle to the first corner.
195                    let (cx, cy) = (x + w - radius, y + h - radius);
196                    [cx + angle.cos() * radius, cy + angle.sin() * radius]
197                }
198            }
199        }),
200        f,
201    );
202}
203
204/// Streams a polygon into tri list.
205/// Uses buffers that fit inside L1 cache.
206///
207/// `polygon` is a function that provides the vertices that comprise the polygon. Each
208/// call to E will return a new vertex until there are none left.
209///
210/// `f` is a function that consumes the tri list constructed by the output of `polygon`,
211/// one chunk (buffer) at a time.
212///
213/// Each chunk (buffer) is a fixed size array) of the format:
214///
215/// ```
216/// //     [[x0, y0], [x1, y1], [x2, y2], [x3, y3], ... [y4, y5], ...]
217/// //      ^--------------------------^  ^--------------------^
218/// //        3 Points of triangle   3 points of second triangle,
219/// ```
220///
221/// Together all the chunks comprise the full tri list. Each time the buffer size is
222/// reached, that chunk is fed to `f`, then this function proceeds using a new buffer
223/// until a call to `polygon` returns `None`, indicating there are no points left in
224/// the polygon. (in which case the last partially filled buffer is sent to `f`)
225pub fn stream_polygon_tri_list<E, F>(m: Matrix2d, mut polygon: E, mut f: F)
226where
227    E: Iterator<Item = Vec2d>,
228    F: FnMut(&[[f32; 2]]),
229{
230    let mut vertices: [[f32; 2]; BUFFER_SIZE] = [[0.0; 2]; BUFFER_SIZE];
231    // Get the first point which will be used a lot.
232    let fp = match polygon.next() {
233        None => return,
234        Some(val) => val,
235    };
236    let f1 = [tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1])];
237    let gp = match polygon.next() {
238        None => return,
239        Some(val) => val,
240    };
241    let mut g1 = [tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1])];
242    let mut i = 0;
243    let vertices_per_triangle = 3;
244    let align_vertices = vertices_per_triangle;
245    'read_vertices: loop {
246        let ind_out = i * align_vertices;
247        vertices[ind_out] = f1;
248
249        // Copy vertex.
250        let p = match polygon.next() {
251            None => break 'read_vertices,
252            Some(val) => val,
253        };
254        let pos = [tx(m, p[0], p[1]), ty(m, p[0], p[1])];
255
256        vertices[ind_out + 1] = g1;
257        vertices[ind_out + 2] = pos;
258        g1 = pos;
259
260        i += 1;
261        // Buffer is full.
262        if (i + 1) * align_vertices > BUFFER_SIZE {
263            // Send chunk and start over.
264            f(&vertices[0..i * align_vertices]);
265            i = 0;
266        }
267    }
268
269    if i > 0 {
270        f(&vertices[0..i * align_vertices]);
271    }
272}
273
274/// Streams an ellipse border specified by a resolution.
275#[inline(always)]
276pub fn with_ellipse_border_tri_list<F>(
277    resolution: Resolution,
278    m: Matrix2d,
279    rect: Rectangle,
280    border_radius: Radius,
281    f: F,
282) where
283    F: FnMut(&[[f32; 2]]),
284{
285    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
286    let (cw, ch) = (0.5 * w, 0.5 * h);
287    let (cw1, ch1) = (cw + border_radius, ch + border_radius);
288    let (cw2, ch2) = (cw - border_radius, ch - border_radius);
289    let (cx, cy) = (x + cw, y + ch);
290    let n = resolution;
291    let mut i = 0;
292    stream_quad_tri_list(
293        m,
294        || {
295            if i > n {
296                return None;
297            }
298
299            let angle = i as Scalar / n as Scalar * <Scalar as Radians>::_360();
300            let cos = angle.cos();
301            let sin = angle.sin();
302            i += 1;
303            Some((
304                [cx + cos * cw1, cy + sin * ch1],
305                [cx + cos * cw2, cy + sin * ch2],
306            ))
307        },
308        f,
309    );
310}
311
312/// Streams an arc between the two radian boundaries.
313#[inline(always)]
314pub fn with_arc_tri_list<F>(
315    start_radians: Scalar,
316    end_radians: Scalar,
317    resolution: Resolution,
318    m: Matrix2d,
319    rect: Rectangle,
320    border_radius: Radius,
321    f: F,
322) where
323    F: FnMut(&[[f32; 2]]),
324{
325    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
326    let (cw, ch) = (0.5 * w, 0.5 * h);
327    let (cw1, ch1) = (cw + border_radius, ch + border_radius);
328    let (cw2, ch2) = (cw - border_radius, ch - border_radius);
329    let (cx, cy) = (x + cw, y + ch);
330    let mut i = 0;
331
332    let twopi = <Scalar as Radians>::_360();
333    let max_seg_size = twopi / resolution as Scalar;
334
335    let (start_radians, delta) = if (end_radians - start_radians).abs() >= twopi {
336        // Remove overlap.
337        (0.0, twopi)
338    } else {
339        // Take true modulus by 2pi.
340        (
341            start_radians,
342            (((end_radians - start_radians) % twopi) + twopi) % twopi,
343        )
344    };
345
346    // Taking ceiling here implies that the resolution parameter provides a
347    // lower bound on the drawn resolution.
348    let n_quads = (delta / max_seg_size).ceil() as u64;
349
350    // n_quads * seg_size exactly spans the included angle.
351    let seg_size = delta / n_quads as Scalar;
352    stream_quad_tri_list(
353        m,
354        || {
355            if i > n_quads {
356                return None;
357            }
358
359            let angle = start_radians + (i as Scalar * seg_size);
360
361            let cos = angle.cos();
362            let sin = angle.sin();
363            i += 1;
364            Some((
365                [cx + cos * cw1, cy + sin * ch1],
366                [cx + cos * cw2, cy + sin * ch2],
367            ))
368        },
369        f,
370    );
371}
372
373/// Streams a round rectangle border.
374#[allow(clippy::identity_op)] // Identity operations are used for readibility.
375#[inline(always)]
376pub fn with_round_rectangle_border_tri_list<F>(
377    resolution_corner: Resolution,
378    m: Matrix2d,
379    rect: Rectangle,
380    round_radius: Radius,
381    border_radius: Radius,
382    f: F,
383) where
384    F: FnMut(&[[f32; 2]]),
385{
386    use vecmath::traits::FromPrimitive;
387
388    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
389    let radius = round_radius;
390    let radius1 = round_radius + border_radius;
391    let radius2 = round_radius - border_radius;
392    let n = resolution_corner * 4;
393    let mut i = 0;
394    stream_quad_tri_list(
395        m,
396        || {
397            if i > n {
398                return None;
399            }
400
401            let j = i;
402            i += 1;
403            // Detect quarter circle from index.
404            // There is one quarter circle at each corner.
405            // Together they form a full circle if
406            // each side of rectangle is 2 times the radius.
407            match j {
408                j if j == n => {
409                    let (cx, cy) = (x + w - radius, y + h - radius);
410                    Some(([cx + radius1, cy], [cx + radius2, cy]))
411                }
412                j if j >= resolution_corner * 3 => {
413                    // Compute the angle to match start and end
414                    // point of quarter circle.
415                    // This requires an angle offset since this
416                    // is the last quarter.
417                    let angle: Scalar = (j - resolution_corner * 3) as Scalar
418                        / (resolution_corner - 1) as Scalar
419                        * <Scalar as Radians>::_90()
420                        + <Scalar as FromPrimitive>::from_f64(3.0) * <Scalar as Radians>::_90();
421                    // Set center of the circle to the last corner.
422                    let (cx, cy) = (x + w - radius, y + radius);
423                    let cos = angle.cos();
424                    let sin = angle.sin();
425                    Some((
426                        [cx + cos * radius1, cy + sin * radius1],
427                        [cx + cos * radius2, cy + sin * radius2],
428                    ))
429                }
430                j if j >= resolution_corner * 2 => {
431                    // Compute the angle to match start and end
432                    // point of quarter circle.
433                    // This requires an angle offset since
434                    // this is the second last quarter.
435                    let angle = (j - resolution_corner * 2) as Scalar
436                        / (resolution_corner - 1) as Scalar
437                        * <Scalar as Radians>::_90()
438                        + <Scalar as Radians>::_180();
439                    // Set center of the circle to the second last corner.
440                    let (cx, cy) = (x + radius, y + radius);
441                    let cos = angle.cos();
442                    let sin = angle.sin();
443                    Some((
444                        [cx + cos * radius1, cy + sin * radius1],
445                        [cx + cos * radius2, cy + sin * radius2],
446                    ))
447                }
448                j if j >= resolution_corner * 1 => {
449                    // Compute the angle to match start and end
450                    // point of quarter circle.
451                    // This requires an angle offset since
452                    // this is the second quarter.
453                    let angle = (j - resolution_corner) as Scalar
454                        / (resolution_corner - 1) as Scalar
455                        * <Scalar as Radians>::_90()
456                        + <Scalar as Radians>::_90();
457                    // Set center of the circle to the second corner.
458                    let (cx, cy) = (x + radius, y + h - radius);
459                    let cos = angle.cos();
460                    let sin = angle.sin();
461                    Some((
462                        [cx + cos * radius1, cy + sin * radius1],
463                        [cx + cos * radius2, cy + sin * radius2],
464                    ))
465                }
466                j => {
467                    // Compute the angle to match start and end
468                    // point of quarter circle.
469                    let angle = j as Scalar / (resolution_corner - 1) as Scalar
470                        * <Scalar as Radians>::_90();
471                    // Set center of the circle to the first corner.
472                    let (cx, cy) = (x + w - radius, y + h - radius);
473                    let cos = angle.cos();
474                    let sin = angle.sin();
475                    Some((
476                        [cx + cos * radius1, cy + sin * radius1],
477                        [cx + cos * radius2, cy + sin * radius2],
478                    ))
479                }
480            }
481        },
482        f,
483    );
484}
485
486/// Streams a quad into tri list.
487///
488/// Uses buffers that fit inside L1 cache.
489/// The 'quad_edge' stream returns two points
490/// defining the next edge.
491///
492/// `quad_edge` is a function that returns two vertices, which together comprise
493/// one edge of a quad
494///
495///
496/// `f` is a function that consumes the tri list constructed by the output of
497/// `quad_edge`, one chunk (buffer) at a time
498///
499/// The tri list is series of buffers (fixed size array) of the format:
500///
501/// ```
502/// //     [[x0, y0], [x1, y1], [x2, y2], [x3, y3], ... [y4, y5], ...]
503/// //      ^--------------------------^  ^--------------------^
504/// //        3 Points of triangle   3 points of second triangle,
505/// //      ^------------------------------------^          __
506/// //         Two triangles together form a single quad |\\ 2|
507/// //                                                   |1\\ |
508/// //                                                   |__\\|
509/// ```
510/// Together all the chunks comprise the full tri list. Each time the buffer size is
511/// reached, that chunk is fed to `f`, then this function proceeds using a new buffer
512/// until a call to `quad_edge` returns `None`, indicating there are no more edges left.
513/// (in which case the last partially filled buffer is sent to `f`)
514#[allow(clippy::identity_op)] // Identity operations are used for readibility.
515pub fn stream_quad_tri_list<E, F>(m: Matrix2d, mut quad_edge: E, mut f: F)
516where
517    E: FnMut() -> Option<(Vec2d, Vec2d)>,
518    F: FnMut(&[[f32; 2]]),
519{
520    let mut vertices: [[f32; 2]; BUFFER_SIZE] = [[0.0; 2]; BUFFER_SIZE];
521    // Get the two points .
522    let (fp1, fp2) = match quad_edge() {
523        None => return,
524        Some((val1, val2)) => (val1, val2),
525    };
526    // Transform the points using the matrix.
527    let mut f1 = [tx(m, fp1[0], fp1[1]), ty(m, fp1[0], fp1[1])];
528    let mut f2 = [tx(m, fp2[0], fp2[1]), ty(m, fp2[0], fp2[1])];
529    // Counts the quads.
530    let mut i = 0;
531    let triangles_per_quad = 2;
532    let vertices_per_triangle = 3;
533    let align_vertices = triangles_per_quad * vertices_per_triangle;
534    loop {
535        // Read two more points.
536        let (gp1, gp2) = match quad_edge() {
537            None => break,
538            Some((val1, val2)) => (val1, val2),
539        };
540        // Transform the points using the matrix.
541        let g1 = [tx(m, gp1[0], gp1[1]), ty(m, gp1[0], gp1[1])];
542        let g2 = [tx(m, gp2[0], gp2[1]), ty(m, gp2[0], gp2[1])];
543        let ind_out = i * align_vertices;
544
545        // First triangle.
546        vertices[ind_out + 0] = f1;
547        vertices[ind_out + 1] = f2;
548        vertices[ind_out + 2] = g1;
549
550        // Second triangle.
551        vertices[ind_out + 3] = f2;
552        vertices[ind_out + 4] = g1;
553        vertices[ind_out + 5] = g2;
554
555        // Next quad.
556        i += 1;
557
558        // Set next current edge.
559        f1 = g1;
560        f2 = g2;
561
562        // Buffer is full.
563        if (i + 1) * align_vertices > BUFFER_SIZE {
564            // Send chunk and start over.
565            f(&vertices[0..i * align_vertices]);
566            i = 0;
567        }
568    }
569
570    if i > 0 {
571        f(&vertices[0..i * align_vertices]);
572    }
573}
574
575/// Splits polygon into convex segments.
576/// Create a buffer that fits into L1 cache with 1KB overhead.
577///
578/// See stream_polygon_tri_list docs for detailed explanation.
579pub fn with_polygon_tri_list<F>(m: Matrix2d, polygon: Polygon<'_>, f: F)
580where
581    F: FnMut(&[[f32; 2]]),
582{
583    stream_polygon_tri_list(m, (0..polygon.len()).map(|i| polygon[i]), f);
584}
585
586/// Creates triangle list vertices from rectangle.
587#[inline(always)]
588pub fn rect_tri_list_xy(m: Matrix2d, rect: Rectangle) -> [[f32; 2]; 6] {
589    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
590    let (x2, y2) = (x + w, y + h);
591    [
592        [tx(m, x, y), ty(m, x, y)],
593        [tx(m, x2, y), ty(m, x2, y)],
594        [tx(m, x, y2), ty(m, x, y2)],
595        [tx(m, x2, y), ty(m, x2, y)],
596        [tx(m, x2, y2), ty(m, x2, y2)],
597        [tx(m, x, y2), ty(m, x, y2)],
598    ]
599}
600
601/// Creates triangle list vertices from rectangle.
602#[inline(always)]
603pub fn rect_border_tri_list_xy(
604    m: Matrix2d,
605    rect: Rectangle,
606    border_radius: Radius,
607) -> [[f32; 2]; 24] {
608    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
609    let (w1, h1) = (w + border_radius, h + border_radius);
610    let (w2, h2) = (w - border_radius, h - border_radius);
611    let (x11, y11) = (x - border_radius, y - border_radius);
612    let (x21, y21) = (x + border_radius, y + border_radius);
613    let (x12, y12) = (x + w1, y + h1);
614    let (x22, y22) = (x + w2, y + h2);
615    [
616        [tx(m, x11, y11), ty(m, x11, y11)],
617        [tx(m, x12, y11), ty(m, x12, y11)],
618        [tx(m, x21, y21), ty(m, x21, y21)],
619        [tx(m, x21, y21), ty(m, x21, y21)],
620        [tx(m, x12, y11), ty(m, x12, y11)],
621        [tx(m, x22, y21), ty(m, x22, y21)],
622        [tx(m, x22, y21), ty(m, x22, y21)],
623        [tx(m, x12, y11), ty(m, x12, y11)],
624        [tx(m, x12, y12), ty(m, x12, y12)],
625        [tx(m, x22, y21), ty(m, x22, y21)],
626        [tx(m, x12, y12), ty(m, x12, y12)],
627        [tx(m, x22, y22), ty(m, x22, y22)],
628        [tx(m, x12, y12), ty(m, x12, y12)],
629        [tx(m, x22, y22), ty(m, x22, y22)],
630        [tx(m, x11, y12), ty(m, x11, y12)],
631        [tx(m, x22, y22), ty(m, x22, y22)],
632        [tx(m, x11, y12), ty(m, x11, y12)],
633        [tx(m, x21, y22), ty(m, x21, y22)],
634        [tx(m, x11, y12), ty(m, x11, y12)],
635        [tx(m, x21, y21), ty(m, x21, y21)],
636        [tx(m, x21, y22), ty(m, x21, y22)],
637        [tx(m, x11, y12), ty(m, x11, y12)],
638        [tx(m, x11, y11), ty(m, x11, y11)],
639        [tx(m, x21, y21), ty(m, x21, y21)],
640    ]
641}
642
643/// Creates triangle list texture coords from image.
644#[inline(always)]
645pub fn rect_tri_list_uv<I: ImageSize>(image: &I, source_rect: SourceRectangle) -> [[f32; 2]; 6] {
646    let (w, h) = image.get_size();
647    let (src_x, src_y, src_w, src_h) = (
648        source_rect[0],
649        source_rect[1],
650        source_rect[2],
651        source_rect[3],
652    );
653
654    let x1 = src_x as f32 / w as f32;
655    let y1 = src_y as f32 / h as f32;
656    let x2 = (src_w + src_x) as f32 / w as f32;
657    let y2 = (src_h + src_y) as f32 / h as f32;
658    [[x1, y1], [x2, y1], [x1, y2], [x2, y1], [x2, y2], [x1, y2]]
659}