pub fn gradient_scale<GradientIter: IntoIterator<Item=GradientOp>, const N: usize>(description: GradientIter) -> [[u8; 4]; N] {
    // Create a blank scale
    let mut scale = [[0, 0, 0, 0]; N];

    // Create a list of colour stops by position
    let mut stops = description.into_iter()
        .map(|op| match op {
            GradientOp::Create(col)         => (0.0, col.to_rgba_components()),
            GradientOp::AddStop(pos, col)   => (pos, col.to_rgba_components())
        })
        .collect::<Vec<_>>();

    // Order by position
    stops.sort_by(|(pos_a, _), (pos_b, _)| pos_a.partial_cmp(pos_b).unwrap_or(Ordering::Equal));

    if stops.len() == 0 {
        // No stops means we return the blank scale
        scale
    } else if stops.len() == 1 {
        // A single stop just uses that as a flat colour
        [components_to_bytes(stops[0].1); N]
    } else {
        // Fill the scale using the stops
        let min_pos             = stops[0].0 as f64;
        let max_pos             = stops[stops.len()-1].0 as f64;

        debug_assert!(max_pos > min_pos);

        let distance_per_step   = (max_pos - min_pos) / ((N-1) as f64);
        let final_color         = components_to_bytes(stops[stops.len()-1].1);
        let mut idx             = 0;
        let mut stop_iter       = stops.into_iter().tuple_windows();
        let mut current_stop    = stop_iter.next().unwrap();

        while idx < (N-1) {
            let pos             = ((idx as f64) * distance_per_step) + min_pos;

            // Get the current position
            let ((start_pos, (r1, g1, b1, a1)), (end_pos, (r2, g2, b2, a2))) = &current_stop;

            let start_pos       = *start_pos as f64;
            let end_pos         = *end_pos as f64;

            // Move to the next stop if the current position is already past the end
            if pos >= end_pos {
                current_stop = stop_iter.next().unwrap();
                continue;
            }

            // Blend the colour between the end position and the start position
            let ratio           = ((pos-start_pos)/(end_pos-start_pos)) as f32;
            let (r, g, b, a)    = (
                (r2-r1)*ratio + r1,
                (g2-g1)*ratio + g1,
                (b2-b1)*ratio + b1,
                (a2-a1)*ratio + a1
            );

            // Write this component to the current index
            scale[idx]  = components_to_bytes((r, g, b, a));

            // Move to the next position before continuing
            idx         += 1;
        }

        debug_assert!(idx == N-1);
        scale[idx] = final_color;

        scale
    }
}
///
/// Creates a gradient scale, as 8-bit RGBA quads from a set of gradient operations
///
Creates a gradient scale, as 8-bit RGBA quads from a set of gradient operations
Creates a gradient scale as 8 - bit RGBA quads from a set of gradient operations
pub fn gradient_scale<GradientIter: IntoIterator<Item=GradientOp>, const N: usize>(description: GradientIter) -> [[u8; 4]; N] {
    // Create a blank scale
    let mut scale = [[0, 0, 0, 0]; N];
    // Create a list of colour stops by position
    let mut stops = description.into_iter()
        .map(|op| match op {
            GradientOp::Create(col)         => (0.0, col.to_rgba_components()),
            GradientOp::AddStop(pos, col)   => (pos, col.to_rgba_components())
        })
        .collect::<Vec<_>>();
    // Order by position
    stops.sort_by(|(pos_a, _), (pos_b, _)| pos_a.partial_cmp(pos_b).unwrap_or(Ordering::Equal));
    if stops.len() == 0 {
        // No stops means we return the blank scale
        scale
    } else if stops.len() == 1 {
        // A single stop just uses that as a flat colour
        [components_to_bytes(stops[0].1); N]
    } else {
        // Fill the scale using the stops
        let min_pos             = stops[0].0 as f64;
        let max_pos             = stops[stops.len()-1].0 as f64;
        debug_assert!(max_pos > min_pos);
        let distance_per_step   = (max_pos - min_pos) / ((N-1) as f64);
        let final_color         = components_to_bytes(stops[stops.len()-1].1);
        let mut idx             = 0;
        let mut stop_iter       = stops.into_iter().tuple_windows();
        let mut current_stop    = stop_iter.next().unwrap();
        while idx < (N-1) {
            let pos             = ((idx as f64) * distance_per_step) + min_pos;
            // Get the current position
            let ((start_pos, (r1, g1, b1, a1)), (end_pos, (r2, g2, b2, a2))) = &current_stop;
            let start_pos       = *start_pos as f64;
            let end_pos         = *end_pos as f64;
            // Move to the next stop if the current position is already past the end
            if pos >= end_pos {
                current_stop = stop_iter.next().unwrap();
                continue;
            }
            // Blend the colour between the end position and the start position
            let ratio           = ((pos-start_pos)/(end_pos-start_pos)) as f32;
            let (r, g, b, a)    = (
                (r2-r1)*ratio + r1,
                (g2-g1)*ratio + g1,
                (b2-b1)*ratio + b1,
                (a2-a1)*ratio + a1
            );
            // Write this component to the current index
            scale[idx]  = components_to_bytes((r, g, b, a));
            // Move to the next position before continuing
            idx         += 1;
        }
        debug_assert!(idx == N-1);
        scale[idx] = final_color;
        scale
    }
}
pub fn gradient_scale < GradientIter : IntoIterator < Item = GradientOp > , const N : usize > ( description : GradientIter ) -> [ [ u8 ; 4 ] ; N ] { // Create a blank scale let mut scale = [ [ 0 , 0 , 0 , 0 ] ; N ] ; // Create a list of colour stops by position let mut stops = description . into_iter ( ) . map ( | op | match op { GradientOp :: Create ( col ) => ( 0.0 , col . to_rgba_components ( ) ) , GradientOp :: AddStop ( pos , col ) => ( pos , col . to_rgba_components ( ) ) } ) . collect :: < Vec < _ > > ( ) ; // Order by position stops . sort_by ( | ( pos_a , _ ) , ( pos_b , _ ) | pos_a . partial_cmp ( pos_b ) . unwrap_or ( Ordering :: Equal ) ) ; if stops . len ( ) == 0 { // No stops means we return the blank scale scale } else if stops . len ( ) == 1 { // A single stop just uses that as a flat colour [ components_to_bytes ( stops [ 0 ] . 1 ) ; N ] } else { // Fill the scale using the stops let min_pos = stops [ 0 ] . 0 as f64 ; let max_pos = stops [ stops . len ( ) - 1 ] . 0 as f64 ; debug_assert ! ( max_pos min_pos ) ; let distance_per_step = ( max_pos - min_pos ) / ( ( N - 1 ) as f64 ) ; let final_color = components_to_bytes ( stops [ stops . len ( ) - 1 ] . 1 ) ; let mut idx = 0 ; let mut stop_iter = stops . into_iter ( ) . tuple_windows ( ) ; let mut current_stop = stop_iter . next ( ) . unwrap ( ) ; while idx < ( N - 1 ) { let pos = ( ( idx as f64 ) * distance_per_step ) + min_pos ; // Get the current position let ( ( start_pos , ( r1 , g1 , b1 , a1 ) ) , ( end_pos , ( r2 , g2 , b2 , a2 ) ) ) = & current_stop ; let start_pos = * start_pos as f64 ; let end_pos = * end_pos as f64 ; // Move to the next stop if the current position is already past the end if pos >= end_pos { current_stop = stop_iter . next ( ) . unwrap ( ) ; continue ; } // Blend the colour between the end position and the start position let ratio = ( ( pos - start_pos ) / ( end_pos - start_pos ) ) as f32 ; let ( r , g , b , a ) = ( ( r2 - r1 ) * ratio + r1 , ( g2 - g1 ) * ratio + g1 , ( b2 - b1 ) * ratio + b1 , ( a2 - a1 ) * ratio + a1 ) ; // Write this component to the current index scale [ idx ] = components_to_bytes ( ( r , g , b , a ) ) ; // Move to the next position before continuing idx += 1 ; } debug_assert ! ( idx N 1 ) ; scale [ idx ] = final_color ; scale } }
Creates a gradient scale, as 8-bit RGBA quads from a set of gradient operations
Creates a gradient scale as 8 - bit RGBA quads from a set of gradient operations
description
GradientIter