<commit_before>use std::path::Path;
use ::subtitles::*;
use ::std::fs::File;
use ::std::io::{BufReader,BufRead};
use ::std::error::Error;
use ::subtitles::pos::RowPosition;

/// frm / lyr / fps
impl<'a> Load for (Option<&'a Path>,&'a Path,f64) {
    fn into_subtitles(&self) -> Result<Subtitles, String> {
        // TODO : split this into with / without frm file, where one calls the other
        let frm: Option<&Path> = self.0;
        let lyr: &Path = self.1;
        let fps: f64 = self.2;
        if fps == 0.0 {
            return Err(String::from("File has no frame/second, impossible to load .lyr/.frm, aborting ..."))
        };
        let mut subtitles = Subtitles::default();
        let lyr_file = try!(File::open(lyr).map_err(|e| e.description().to_string()));
        let lyr_file = BufReader::new(&lyr_file);
        let mut subtitles_options : SubtitlesOptions = Default::default();
        for (line_number, lyr_line) in lyr_file.lines().enumerate() {
            let lyr_line = try!(lyr_line.map_err(|e| {
                format!("IoError when reading {} at line {} : '{}'",
                        lyr.display(),
                        line_number,
                        e.description())
            }));
            if (!lyr_line.starts_with("%") && !lyr_line.is_empty()) {
                let mut syllables: Vec<_> = lyr_line.split('&')
                    .map(|s| {
                        Syllable {
                            text: s.to_string(),
                            begin: 0,
                            end: Some(0),
                            syllable_options: None,
                        }
                    })
                    .collect::<Vec<_>>();
                if (lyr_line.starts_with("&")) {
                    syllables.remove(0);
                };
                let sentence = Sentence {
                    syllables: syllables,
                    position: RowPosition::default(),
                    sentence_options: None,
                };
                subtitles.sentences.push(sentence);
            } else if lyr_line.starts_with("%color") {
                let mut syllable_options = SyllableOptions::default();
                use utils::parse_bgr;
                let colors: Vec<_> = lyr_line.split_whitespace().collect();
                if colors.len() == 4 {
                    let alive_color = parse_bgr(colors[1]);
                    let transition_color = parse_bgr(colors[2]);
                    let dead_color = parse_bgr(colors[3]);
                    match (alive_color, transition_color, dead_color) {
                        (Ok(alive_color), Ok(transition_color), Ok(dead_color)) => {
                            syllable_options
                                .alive_color = Some(alive_color);
                            syllable_options
                                .transition_color = Some(transition_color);
                            syllable_options
                                .dead_color = Some(dead_color);
                        }
                        _ => {
                            error!("Invalid %color syntax when reading {} at line {} : '{}'",
                                   lyr.display(),line_number,lyr_line);
                        }
                    }
                } else {
                    error!("Invalid %color syntax when reading {} at line {} : '{}'",
                           lyr.display(),line_number,lyr_line);
                }
                let mut sentence_options = SentenceOptions::default();
                sentence_options.syllable_options = Some(syllable_options);
                subtitles_options.sentence_options = Some(sentence_options);
            };
        }
        if let Some(frm) = frm {
            let frm_file = try!(File::open(frm).map_err(|e| e.description().to_string()));
            let frm_file = BufReader::new(&frm_file);
            let mut frames: Vec<(u32, u32)> = vec![];
            for (line_number, frm_line) in frm_file.lines().enumerate() {
                let frm_line = try!(frm_line.map_err(|e| {
                    format!("IoError when reading {} at line {} : '{}'",
                            frm.display(),
                            line_number,
                            e.description())
                }));
                if !frm_line.trim().is_empty() {
                    let line_frames: Result<Vec<_>, _> = frm_line.split(' ')
                        .map(|s| s.parse::<u32>())
                        .collect();
                    let begin_end = line_frames.map_err(|e| format!("{} at line {}", e, frm_line))
                        .and_then(|line_frames| {
                            match (line_frames.get(0), line_frames.get(1), line_frames.get(2)) {
                                (Some(&begin), Some(&end), None) => Ok((begin, end)),
                                (None, _, _) | (_, None, _) => {
                                    Err(format!("Error while parsing frm file '{}' at line {}, not \
                                                 enough values",
                                                frm.display(),
                                                line_number))
                                }
                                (_, _, Some(_)) => {
                                    Err(format!("Error while parsing frm file '{}' at line {}, too \
                                                 many values",
                                                frm.display(),
                                                line_number))
                                }
                            }
                        });
                    let begin_end = try!(begin_end);
                    frames.push(begin_end);
                } else {
                    warn!("empty line {} in frm file '{}'", line_number, frm.display());
                }
            }
            let mut frame_iter = frames.iter();
            'sentences: for sentence in subtitles.sentences.iter_mut() {
                for syllable in sentence.syllables.iter_mut() {
                    match frame_iter.next() {
                        Some(&(begin, end)) => {
                            syllable.begin = (begin as f64 / fps * 1000.0) as u32 ;
                            syllable.end = Some((end as f64 / fps * 1000.0) as u32);
                        }
                        None => {
                            break 'sentences;
                        }
                    }
                }
            }
        };
        subtitles.subtitles_options = subtitles_options;
        try!(subtitles.check());
        Ok(subtitles)
    }
}
<commit_msg>Added %color division in lyr<commit_after>use std::path::Path;
use ::subtitles::*;
use ::std::fs::File;
use ::std::io::{BufReader,BufRead};
use ::std::error::Error;
use ::subtitles::pos::RowPosition;

/// frm / lyr / fps
impl<'a> Load for (Option<&'a Path>,&'a Path,f64) {
    fn into_subtitles(&self) -> Result<Subtitles, String> {
        // TODO : split this into with / without frm file, where one calls the other
        let frm: Option<&Path> = self.0;
        let lyr: &Path = self.1;
        let fps: f64 = self.2;
        if fps == 0.0 {
            return Err(String::from("File has no frame/second, impossible to load .lyr/.frm, aborting ..."))
        };
        let mut subtitles = Subtitles::default();
        let lyr_file = try!(File::open(lyr).map_err(|e| e.description().to_string()));
        let lyr_file = BufReader::new(&lyr_file);
        let mut subtitles_options : SubtitlesOptions = Default::default();
        let mut current_sentence_options : Option<SentenceOptions> = None;
        for (line_number, lyr_line) in lyr_file.lines().enumerate() {
            let lyr_line = try!(lyr_line.map_err(|e| {
                format!("IoError when reading {} at line {} : '{}'",
                        lyr.display(),
                        line_number,
                        e.description())
            }));
            if (!lyr_line.starts_with("%") && !lyr_line.starts_with("#") && !lyr_line.is_empty()) {
                let mut syllables: Vec<_> = lyr_line.split('&')
                    .map(|s| {
                        Syllable {
                            text: s.to_string(),
                            begin: 0,
                            end: Some(0),
                            syllable_options: None,
                        }
                    })
                    .collect::<Vec<_>>();
                if (lyr_line.starts_with("&")) {
                    syllables.remove(0);
                };
                let sentence = Sentence {
                    syllables: syllables,
                    position: RowPosition::default(),
                    sentence_options: current_sentence_options,
                };
                subtitles.sentences.push(sentence);
            } else if lyr_line.starts_with("%color") {
                let mut syllable_options = SyllableOptions::default();
                use utils::parse_bgr;
                let colors: Vec<_> = lyr_line.split_whitespace().collect();
                if colors.len() == 4 {
                    let alive_color = parse_bgr(colors[1]);
                    let transition_color = parse_bgr(colors[2]);
                    let dead_color = parse_bgr(colors[3]);
                    match (alive_color, transition_color, dead_color) {
                        (Ok(alive_color), Ok(transition_color), Ok(dead_color)) => {
                            syllable_options
                                .alive_color = Some(alive_color);
                            syllable_options
                                .transition_color = Some(transition_color);
                            syllable_options
                                .dead_color = Some(dead_color);
                        }
                        _ => {
                            error!("Invalid %color syntax when reading {} at line {} : '{}'",
                                   lyr.display(),line_number,lyr_line);
                        }
                    }
                } else {
                    error!("Invalid %color syntax when reading {} at line {} : '{}'",
                           lyr.display(),line_number,lyr_line);
                }
                let mut sentence_options = SentenceOptions::default();
                sentence_options.syllable_options = Some(syllable_options);
                match subtitles.sentences.len() {
                    0 => {subtitles_options.sentence_options = Some(sentence_options)}
                    _ => {current_sentence_options = Some(sentence_options)}
                };
            };
        }
        if let Some(frm) = frm {
            let frm_file = try!(File::open(frm).map_err(|e| e.description().to_string()));
            let frm_file = BufReader::new(&frm_file);
            let mut frames: Vec<(u32, u32)> = vec![];
            for (line_number, frm_line) in frm_file.lines().enumerate() {
                let frm_line = try!(frm_line.map_err(|e| {
                    format!("IoError when reading {} at line {} : '{}'",
                            frm.display(),
                            line_number,
                            e.description())
                }));
                if !frm_line.trim().is_empty() {
                    let line_frames: Result<Vec<_>, _> = frm_line.split(' ')
                        .map(|s| s.parse::<u32>())
                        .collect();
                    let begin_end = line_frames.map_err(|e| format!("{} at line {}", e, line_number))
                        .and_then(|line_frames| {
                            match (line_frames.get(0), line_frames.get(1), line_frames.get(2)) {
                                (Some(&begin), Some(&end), None) => Ok((begin, end)),
                                (None, _, _) | (_, None, _) => {
                                    Err(format!("Error while parsing frm file '{}' at line {}, not \
                                                 enough values",
                                                frm.display(),
                                                line_number))
                                }
                                (_, _, Some(_)) => {
                                    Err(format!("Error while parsing frm file '{}' at line {}, too \
                                                 many values",
                                                frm.display(),
                                                line_number))
                                }
                            }
                        });
                    let begin_end = try!(begin_end);
                    frames.push(begin_end);
                } else {
                    warn!("empty line {} in frm file '{}'", line_number, frm.display());
                }
            }
            let mut frame_iter = frames.iter();
            'sentences: for sentence in subtitles.sentences.iter_mut() {
                for syllable in sentence.syllables.iter_mut() {
                    match frame_iter.next() {
                        Some(&(begin, end)) => {
                            syllable.begin = (begin as f64 / fps * 1000.0) as u32 ;
                            syllable.end = Some((end as f64 / fps * 1000.0) as u32);
                        }
                        None => {
                            break 'sentences;
                        }
                    }
                }
            }
        };
        subtitles.subtitles_options = subtitles_options;
        try!(subtitles.check());
        Ok(subtitles)
    }
}
<|endoftext|>


<commit_before>use material::textures::ImageTexture;
use vec3::Vec3;

#[allow(dead_code)]
pub struct CubeMap {
    pub faces: Vec<ImageTexture>
}

impl CubeMap {
    #[allow(dead_code)]
    pub fn load(front: &str, back: &str, up: &str, down: &str, left: &str, right: &str) -> CubeMap {
        CubeMap {
            faces: vec![
                ImageTexture {image: ::util::import::from_ppm(front)}, // +x
                ImageTexture {image: ::util::import::from_ppm(back)},  // -x
                ImageTexture {image: ::util::import::from_ppm(up)},    // +y
                ImageTexture {image: ::util::import::from_ppm(down)},  // -y
                ImageTexture {image: ::util::import::from_ppm(left)},  // +z
                ImageTexture {image: ::util::import::from_ppm(right)}  // -z
            ]
        }
    }

    #[allow(dead_code)]
    pub fn color(&self, dir: Vec3) -> Vec3 {
        // Select cube face
        let x_mag = dir.x.abs();
        let y_mag = dir.y.abs();
        let z_mag = dir.z.abs();

        let mut face = -1;
        let mut s = 0.0;
        let mut t = 0.0;

        if x_mag >= y_mag && x_mag > z_mag {
            // +x -x direction
            face = if dir.x < 0.0 { 0 } else { 1 };
            s = dir.y / dir.x;
            t = dir.z / dir.x;
        } else if y_mag >= x_mag && y_mag > z_mag {
            // +y -y direction
            face = if dir.y < 0.0 { 2 } else { 3 };
            s = dir.x / dir.y;
            t = dir.z / dir.y;
        } else if z_mag >= y_mag && z_mag > x_mag {
            // +z -z direction
            face = if dir.z < 0.0 { 4 } else { 5 };
            s = dir.x / dir.z;
            t = dir.y / dir.z;
        }

        // println!("skybox coords {} {}", s, t);

        // [-1..1] -> [0..1]
        s = s * 0.5 + 0.5;
        t = t * 0.5 + 0.5;

        self.faces[face].sample(s, t)
    }
}
<commit_msg>Minor CubeMap cleanup.<commit_after>use material::textures::ImageTexture;
use vec3::Vec3;

#[allow(dead_code)]
pub struct CubeMap {
    pub faces: Vec<ImageTexture>
}

impl CubeMap {
    #[allow(dead_code)]
    pub fn load(x: &str, x_neg: &str, y: &str, y_neg: &str, z: &str, z_neg: &str) -> CubeMap {
        CubeMap {
            faces: vec![
                ImageTexture::load(x),
                ImageTexture::load(x_neg),
                ImageTexture::load(y),
                ImageTexture::load(y_neg),
                ImageTexture::load(z),
                ImageTexture::load(z_neg)
            ]
        }
    }

    #[allow(dead_code)]
    pub fn color(&self, dir: Vec3) -> Vec3 {
        // Select cube face
        let x_mag = dir.x.abs();
        let y_mag = dir.y.abs();
        let z_mag = dir.z.abs();

        let mut face = -1;
        let mut s = 0.0;
        let mut t = 0.0;

        if x_mag >= y_mag && x_mag > z_mag {
            // +x -x direction
            face = if dir.x < 0.0 { 0 } else { 1 };
            s = dir.y / dir.x;
            t = dir.z / dir.x;
        } else if y_mag >= x_mag && y_mag > z_mag {
            // +y -y direction
            face = if dir.y < 0.0 { 2 } else { 3 };
            s = dir.x / dir.y;
            t = dir.z / dir.y;
        } else if z_mag >= y_mag && z_mag > x_mag {
            // +z -z direction
            face = if dir.z < 0.0 { 4 } else { 5 };
            s = dir.x / dir.z;
            t = dir.y / dir.z;
        }

        // [-1..1] -> [0..1]
        s = s * 0.5 + 0.5;
        t = t * 0.5 + 0.5;

        self.faces[face].sample(s, t)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::cmp;

use enums;
use parse;

pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
    // TODO: not implemented correctly

    // Finding all the descriptors.
    let mut descriptors = Vec::new();
    struct Descriptor {
        set: u32,
        binding: u32,
        desc_ty: String,
        array_count: u64,
        readonly: bool,
    }

    // Looping to find all the elements that have the `DescriptorSet` decoration.
    for instruction in doc.instructions.iter() {
        let (variable_id, descriptor_set) = match instruction {
            &parse::Instruction::Decorate {
                target_id,
                decoration: enums::Decoration::DecorationDescriptorSet,
                ref params,
            } => {
                (target_id, params[0])
            },
            _ => continue,
        };

        // Find which type is pointed to by this variable.
        let pointed_ty = pointer_variable_ty(doc, variable_id);
        // Name of the variable.
        let name = ::name_from_id(doc, variable_id);

        // Find the binding point of this descriptor.
        let binding = doc.instructions
            .iter()
            .filter_map(|i| {
                match i {
                    &parse::Instruction::Decorate {
                        target_id,
                        decoration: enums::Decoration::DecorationBinding,
                        ref params,
                    } if target_id == variable_id => {
                        Some(params[0])
                    },
                    _ => None,      // TODO: other types
                }
            })
            .next()
            .expect(&format!("Uniform `{}` is missing a binding", name));

        // Find informations about the kind of binding for this descriptor.
        let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false)
            .expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe \
                              unimplemented)",
                             name,
                             pointed_ty));

        descriptors.push(Descriptor {
                             desc_ty: desc_ty,
                             set: descriptor_set,
                             binding: binding,
                             array_count: array_count,
                             readonly: readonly,
                         });
    }

    // Looping to find all the push constant structs.
    let mut push_constants_size = 0;
    for instruction in doc.instructions.iter() {
        let type_id = match instruction {
            &parse::Instruction::TypePointer {
                type_id,
                storage_class: enums::StorageClass::StorageClassPushConstant,
                ..
            } => {
                type_id
            },
            _ => continue,
        };

        let (_, size, _) = ::structs::type_from_id(doc, type_id);
        let size = size.expect("Found runtime-sized push constants");
        push_constants_size = cmp::max(push_constants_size, size);
    }

    // Writing the body of the `descriptor` method.
    let descriptor_body = descriptors
        .iter()
        .map(|d| {
            format!(
                "({set}, {binding}) => Some(DescriptorDesc {{
            ty: {desc_ty},
            array_count: {array_count},
            stages: self.0.clone(),
            readonly: {readonly},
        }}),",
                set = d.set,
                binding = d.binding,
                desc_ty = d.desc_ty,
                array_count = d.array_count,
                readonly = if d.readonly { "true" } else { "false" }
            )

        })
        .collect::<Vec<_>>()
        .concat();

    let num_sets = descriptors.iter().fold(0, |s, d| cmp::max(s, d.set + 1));

    // Writing the body of the `num_bindings_in_set` method.
    let num_bindings_in_set_body = {
        (0 .. num_sets)
            .map(|set| {
                     let num =
                        descriptors
                             .iter()
                             .filter(|d| d.set == set)
                             .fold(0, |s, d| cmp::max(s, 1 + d.binding));
                     format!("{set} => Some({num}),", set = set, num = num)
                 })
            .collect::<Vec<_>>()
            .concat()
    };

    // Writing the body of the `num_push_constants_ranges` method.
    let num_push_constants_ranges_body = {
        if push_constants_size == 0 { "0" } else { "1" }
    };

    // Writing the body of the `push_constants_range` method.
    let push_constants_range_body = format!(
        r#"
        if num != 0 || {pc_size} == 0 {{ return None; }}
        Some(PipelineLayoutDescPcRange {{
            offset: 0,                      // FIXME: not necessarily true
            size: {pc_size},
            stages: ShaderStages::all(),     // FIXME: wrong
        }})
    "#,
        pc_size = push_constants_size
    );

    format!(
        r#"
        #[derive(Debug, Clone)]
        pub struct Layout(ShaderStages);

        #[allow(unsafe_code)]
        unsafe impl PipelineLayoutDesc for Layout {{
            fn num_sets(&self) -> usize {{
                {num_sets}
            }}

            fn num_bindings_in_set(&self, set: usize) -> Option<usize> {{
                match set {{
                    {num_bindings_in_set_body}
                    _ => None
                }}
            }}

            fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {{
                match (set, binding) {{
                    {descriptor_body}
                    _ => None
                }}
            }}

            fn num_push_constants_ranges(&self) -> usize {{
                {num_push_constants_ranges_body}
            }}

            fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {{
                {push_constants_range_body}
            }}
        }}
        "#,
        num_sets = num_sets,
        num_bindings_in_set_body = num_bindings_in_set_body,
        descriptor_body = descriptor_body,
        num_push_constants_ranges_body = num_push_constants_ranges_body,
        push_constants_range_body = push_constants_range_body
    )
}

/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type.
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
    let var_ty = doc.instructions
        .iter()
        .filter_map(|i| match i {
                        &parse::Instruction::Variable {
                            result_type_id,
                            result_id,
                            ..
                        } if result_id == variable => {
                            Some(result_type_id)
                        },
                        _ => None,
                    })
        .next()
        .unwrap();

    doc.instructions
        .iter()
        .filter_map(|i| match i {
                        &parse::Instruction::TypePointer { result_id, type_id, .. }
                            if result_id == var_ty => {
                            Some(type_id)
                        },
                        _ => None,
                    })
        .next()
        .unwrap()
}

/// Returns a `DescriptorDescTy` constructor, a bool indicating whether the descriptor is
/// read-only, and the number of array elements.
///
/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface
fn descriptor_infos(doc: &parse::Spirv, pointed_ty: u32, force_combined_image_sampled: bool)
                    -> Option<(String, bool, u64)> {
    doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                // Determine whether there's a Block or BufferBlock decoration.
                let is_ssbo = doc.instructions.iter().filter_map(|i| {
                    match i {
                        &parse::Instruction::Decorate
                            { target_id, decoration: enums::Decoration::DecorationBufferBlock, .. }
                            if target_id == pointed_ty =>
                        {
                            Some(true)
                        },
                        &parse::Instruction::Decorate
                            { target_id, decoration: enums::Decoration::DecorationBlock, .. }
                            if target_id == pointed_ty =>
                        {
                            Some(false)
                        },
                        _ => None,
                    }
                }).next().expect("Found a buffer uniform with neither the Block nor BufferBlock \
                                  decorations");

                // Determine whether there's a NonWritable decoration.
                //let non_writable = false;       // TODO: tricky because the decoration is on struct members

                let desc = format!("DescriptorDescTy::Buffer(DescriptorBufferDesc {{
                    dynamic: Some(false),
                    storage: {},
                }})", if is_ssbo { "true" } else { "false "});

                Some((desc, true, 1))
            },

            &parse::Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled,
                                             ref format, .. } if result_id == pointed_ty =>
            {
                let sampled = sampled.expect("Vulkan requires that variables of type OpTypeImage \
                                              have a Sampled operand of 1 or 2");

                let ms = if ms { "true" } else { "false" };
                let arrayed = if arrayed {
                    "DescriptorImageDescArray::Arrayed { max_layers: None }"
                } else {
                    "DescriptorImageDescArray::NonArrayed"
                };

                if let &enums::Dim::DimSubpassData = dim {
                    // We are an input attachment.
                    assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \
                                                            an OpTypeImage whose dimension is \
                                                            SubpassData");
                    assert!(if let &enums::ImageFormat::ImageFormatUnknown = format { true }
                            else { false }, "If Dim is SubpassData, Image Format must be Unknown");
                    assert!(!sampled, "If Dim is SubpassData, Sampled must be 2");

                    let desc = format!("DescriptorDescTy::InputAttachment {{
                                            multisampled: {},
                                            array_layers: {}
                                        }}", ms, arrayed);

                    Some((desc, true, 1))

                } else if let &enums::Dim::DimBuffer = dim {
                    // We are a texel buffer.
                    let desc = format!("DescriptorDescTy::TexelBuffer {{
                        storage: {},
                        format: None,       // TODO: specify format if known
                    }}", !sampled);

                    Some((desc, true, 1))

                } else {
                    // We are a sampled or storage image.
                    let sampled = if sampled { "true" } else { "false" };
                    let ty = if force_combined_image_sampled { "CombinedImageSampler" }
                             else { "Image" };
                    let dim = match *dim {
                        enums::Dim::Dim1D => "DescriptorImageDescDimensions::OneDimensional",
                        enums::Dim::Dim2D => "DescriptorImageDescDimensions::TwoDimensional",
                        enums::Dim::Dim3D => "DescriptorImageDescDimensions::ThreeDimensional",
                        enums::Dim::DimCube => "DescriptorImageDescDimensions::Cube",
                        enums::Dim::DimRect => panic!("Vulkan doesn't support rectangle textures"),
                        _ => unreachable!()
                    };

                    let desc = format!("DescriptorDescTy::{}(DescriptorImageDesc {{
                        sampled: {},
                        dimensions: {},
                        format: None,       // TODO: specify format if known
                        multisampled: {},
                        array_layers: {},
                    }})", ty, sampled, dim, ms, arrayed);

                    Some((desc, true, 1))
                }
            },

            &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                if result_id == pointed_ty =>
            {
                descriptor_infos(doc, image_type_id, true)
            },

            &parse::Instruction::TypeSampler { result_id } if result_id == pointed_ty => {
                let desc = format!("DescriptorDescTy::Sampler");
                Some((desc, true, 1))
            },

            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
                let (desc, readonly, arr) = match descriptor_infos(doc, type_id, false) {
                    None => return None,
                    Some(v) => v,
                };
                assert_eq!(arr, 1);     // TODO: implement?
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                Some((desc, readonly, len))
            },

            _ => None,      // TODO: other types
        }
    }).next()
}
<commit_msg>Make the Layout generated by vulkano-shaders entirely public (#787)<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::cmp;

use enums;
use parse;

pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
    // TODO: not implemented correctly

    // Finding all the descriptors.
    let mut descriptors = Vec::new();
    struct Descriptor {
        set: u32,
        binding: u32,
        desc_ty: String,
        array_count: u64,
        readonly: bool,
    }

    // Looping to find all the elements that have the `DescriptorSet` decoration.
    for instruction in doc.instructions.iter() {
        let (variable_id, descriptor_set) = match instruction {
            &parse::Instruction::Decorate {
                target_id,
                decoration: enums::Decoration::DecorationDescriptorSet,
                ref params,
            } => {
                (target_id, params[0])
            },
            _ => continue,
        };

        // Find which type is pointed to by this variable.
        let pointed_ty = pointer_variable_ty(doc, variable_id);
        // Name of the variable.
        let name = ::name_from_id(doc, variable_id);

        // Find the binding point of this descriptor.
        let binding = doc.instructions
            .iter()
            .filter_map(|i| {
                match i {
                    &parse::Instruction::Decorate {
                        target_id,
                        decoration: enums::Decoration::DecorationBinding,
                        ref params,
                    } if target_id == variable_id => {
                        Some(params[0])
                    },
                    _ => None,      // TODO: other types
                }
            })
            .next()
            .expect(&format!("Uniform `{}` is missing a binding", name));

        // Find informations about the kind of binding for this descriptor.
        let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false)
            .expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe \
                              unimplemented)",
                             name,
                             pointed_ty));

        descriptors.push(Descriptor {
                             desc_ty: desc_ty,
                             set: descriptor_set,
                             binding: binding,
                             array_count: array_count,
                             readonly: readonly,
                         });
    }

    // Looping to find all the push constant structs.
    let mut push_constants_size = 0;
    for instruction in doc.instructions.iter() {
        let type_id = match instruction {
            &parse::Instruction::TypePointer {
                type_id,
                storage_class: enums::StorageClass::StorageClassPushConstant,
                ..
            } => {
                type_id
            },
            _ => continue,
        };

        let (_, size, _) = ::structs::type_from_id(doc, type_id);
        let size = size.expect("Found runtime-sized push constants");
        push_constants_size = cmp::max(push_constants_size, size);
    }

    // Writing the body of the `descriptor` method.
    let descriptor_body = descriptors
        .iter()
        .map(|d| {
            format!(
                "({set}, {binding}) => Some(DescriptorDesc {{
            ty: {desc_ty},
            array_count: {array_count},
            stages: self.0.clone(),
            readonly: {readonly},
        }}),",
                set = d.set,
                binding = d.binding,
                desc_ty = d.desc_ty,
                array_count = d.array_count,
                readonly = if d.readonly { "true" } else { "false" }
            )

        })
        .collect::<Vec<_>>()
        .concat();

    let num_sets = descriptors.iter().fold(0, |s, d| cmp::max(s, d.set + 1));

    // Writing the body of the `num_bindings_in_set` method.
    let num_bindings_in_set_body = {
        (0 .. num_sets)
            .map(|set| {
                     let num =
                        descriptors
                             .iter()
                             .filter(|d| d.set == set)
                             .fold(0, |s, d| cmp::max(s, 1 + d.binding));
                     format!("{set} => Some({num}),", set = set, num = num)
                 })
            .collect::<Vec<_>>()
            .concat()
    };

    // Writing the body of the `num_push_constants_ranges` method.
    let num_push_constants_ranges_body = {
        if push_constants_size == 0 { "0" } else { "1" }
    };

    // Writing the body of the `push_constants_range` method.
    let push_constants_range_body = format!(
        r#"
        if num != 0 || {pc_size} == 0 {{ return None; }}
        Some(PipelineLayoutDescPcRange {{
            offset: 0,                      // FIXME: not necessarily true
            size: {pc_size},
            stages: ShaderStages::all(),     // FIXME: wrong
        }})
    "#,
        pc_size = push_constants_size
    );

    format!(
        r#"
        #[derive(Debug, Clone)]
        pub struct Layout(pub ShaderStages);

        #[allow(unsafe_code)]
        unsafe impl PipelineLayoutDesc for Layout {{
            fn num_sets(&self) -> usize {{
                {num_sets}
            }}

            fn num_bindings_in_set(&self, set: usize) -> Option<usize> {{
                match set {{
                    {num_bindings_in_set_body}
                    _ => None
                }}
            }}

            fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {{
                match (set, binding) {{
                    {descriptor_body}
                    _ => None
                }}
            }}

            fn num_push_constants_ranges(&self) -> usize {{
                {num_push_constants_ranges_body}
            }}

            fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {{
                {push_constants_range_body}
            }}
        }}
        "#,
        num_sets = num_sets,
        num_bindings_in_set_body = num_bindings_in_set_body,
        descriptor_body = descriptor_body,
        num_push_constants_ranges_body = num_push_constants_ranges_body,
        push_constants_range_body = push_constants_range_body
    )
}

/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type.
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
    let var_ty = doc.instructions
        .iter()
        .filter_map(|i| match i {
                        &parse::Instruction::Variable {
                            result_type_id,
                            result_id,
                            ..
                        } if result_id == variable => {
                            Some(result_type_id)
                        },
                        _ => None,
                    })
        .next()
        .unwrap();

    doc.instructions
        .iter()
        .filter_map(|i| match i {
                        &parse::Instruction::TypePointer { result_id, type_id, .. }
                            if result_id == var_ty => {
                            Some(type_id)
                        },
                        _ => None,
                    })
        .next()
        .unwrap()
}

/// Returns a `DescriptorDescTy` constructor, a bool indicating whether the descriptor is
/// read-only, and the number of array elements.
///
/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface
fn descriptor_infos(doc: &parse::Spirv, pointed_ty: u32, force_combined_image_sampled: bool)
                    -> Option<(String, bool, u64)> {
    doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                // Determine whether there's a Block or BufferBlock decoration.
                let is_ssbo = doc.instructions.iter().filter_map(|i| {
                    match i {
                        &parse::Instruction::Decorate
                            { target_id, decoration: enums::Decoration::DecorationBufferBlock, .. }
                            if target_id == pointed_ty =>
                        {
                            Some(true)
                        },
                        &parse::Instruction::Decorate
                            { target_id, decoration: enums::Decoration::DecorationBlock, .. }
                            if target_id == pointed_ty =>
                        {
                            Some(false)
                        },
                        _ => None,
                    }
                }).next().expect("Found a buffer uniform with neither the Block nor BufferBlock \
                                  decorations");

                // Determine whether there's a NonWritable decoration.
                //let non_writable = false;       // TODO: tricky because the decoration is on struct members

                let desc = format!("DescriptorDescTy::Buffer(DescriptorBufferDesc {{
                    dynamic: Some(false),
                    storage: {},
                }})", if is_ssbo { "true" } else { "false "});

                Some((desc, true, 1))
            },

            &parse::Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled,
                                             ref format, .. } if result_id == pointed_ty =>
            {
                let sampled = sampled.expect("Vulkan requires that variables of type OpTypeImage \
                                              have a Sampled operand of 1 or 2");

                let ms = if ms { "true" } else { "false" };
                let arrayed = if arrayed {
                    "DescriptorImageDescArray::Arrayed { max_layers: None }"
                } else {
                    "DescriptorImageDescArray::NonArrayed"
                };

                if let &enums::Dim::DimSubpassData = dim {
                    // We are an input attachment.
                    assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \
                                                            an OpTypeImage whose dimension is \
                                                            SubpassData");
                    assert!(if let &enums::ImageFormat::ImageFormatUnknown = format { true }
                            else { false }, "If Dim is SubpassData, Image Format must be Unknown");
                    assert!(!sampled, "If Dim is SubpassData, Sampled must be 2");

                    let desc = format!("DescriptorDescTy::InputAttachment {{
                                            multisampled: {},
                                            array_layers: {}
                                        }}", ms, arrayed);

                    Some((desc, true, 1))

                } else if let &enums::Dim::DimBuffer = dim {
                    // We are a texel buffer.
                    let desc = format!("DescriptorDescTy::TexelBuffer {{
                        storage: {},
                        format: None,       // TODO: specify format if known
                    }}", !sampled);

                    Some((desc, true, 1))

                } else {
                    // We are a sampled or storage image.
                    let sampled = if sampled { "true" } else { "false" };
                    let ty = if force_combined_image_sampled { "CombinedImageSampler" }
                             else { "Image" };
                    let dim = match *dim {
                        enums::Dim::Dim1D => "DescriptorImageDescDimensions::OneDimensional",
                        enums::Dim::Dim2D => "DescriptorImageDescDimensions::TwoDimensional",
                        enums::Dim::Dim3D => "DescriptorImageDescDimensions::ThreeDimensional",
                        enums::Dim::DimCube => "DescriptorImageDescDimensions::Cube",
                        enums::Dim::DimRect => panic!("Vulkan doesn't support rectangle textures"),
                        _ => unreachable!()
                    };

                    let desc = format!("DescriptorDescTy::{}(DescriptorImageDesc {{
                        sampled: {},
                        dimensions: {},
                        format: None,       // TODO: specify format if known
                        multisampled: {},
                        array_layers: {},
                    }})", ty, sampled, dim, ms, arrayed);

                    Some((desc, true, 1))
                }
            },

            &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                if result_id == pointed_ty =>
            {
                descriptor_infos(doc, image_type_id, true)
            },

            &parse::Instruction::TypeSampler { result_id } if result_id == pointed_ty => {
                let desc = format!("DescriptorDescTy::Sampler");
                Some((desc, true, 1))
            },

            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
                let (desc, readonly, arr) = match descriptor_infos(doc, type_id, false) {
                    None => return None,
                    Some(v) => v,
                };
                assert_eq!(arr, 1);     // TODO: implement?
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                Some((desc, readonly, len))
            },

            _ => None,      // TODO: other types
        }
    }).next()
}
<|endoftext|>


<commit_before>//! Managing and parsing resource
use std::collections::HashMap;

use crate::websocket::room_object_macros::Updatable;

/// All possible resource identifiers in the game.
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ResourceType {
    /// RESOURCE_ENERGY: "energy",
    #[serde(rename = "energy")]
    Energy,
    /// RESOURCE_POWER: "power",
    #[serde(rename = "power")]
    Power,
    /// RESOURCE_HYDROGEN: "H",
    #[serde(rename = "H")]
    Hydrogen,
    /// RESOURCE_OXYGEN: "O",
    #[serde(rename = "O")]
    Oxygen,
    /// RESOURCE_UTRIUM: "U",
    #[serde(rename = "U")]
    Utrium,
    /// RESOURCE_LEMERGIUM: "L",
    #[serde(rename = "L")]
    Lemergium,
    /// RESOURCE_KEANIUM: "K",
    #[serde(rename = "K")]
    Keanium,
    /// RESOURCE_ZYNTHIUM: "Z",
    #[serde(rename = "Z")]
    Zynthium,
    /// RESOURCE_CATALYST: "X",
    #[serde(rename = "X")]
    Catalyst,
    /// RESOURCE_GHODIUM: "G",
    #[serde(rename = "G")]
    Ghodium,
    /// RESOURCE_HYDROXIDE: "OH",
    #[serde(rename = "OH")]
    Hydroxide,
    /// RESOURCE_ZYNTHIUM_KEANITE: "ZK",
    #[serde(rename = "ZK")]
    ZynthiumKeanite,
    /// RESOURCE_UTRIUM_LEMERGITE: "UL",
    #[serde(rename = "UL")]
    UtriumLemergite,
    /// RESOURCE_UTRIUM_HYDRIDE: "UH",
    #[serde(rename = "UH")]
    UtriumHydride,
    /// RESOURCE_UTRIUM_OXIDE: "UO",
    #[serde(rename = "UO")]
    UtriumOxide,
    /// RESOURCE_KEANIUM_HYDRIDE: "KH",
    #[serde(rename = "KH")]
    KeaniumHydride,
    /// RESOURCE_KEANIUM_OXIDE: "KO",
    #[serde(rename = "KO")]
    KeaniumOxide,
    /// RESOURCE_LEMERGIUM_HYDRIDE: "LH",
    #[serde(rename = "LH")]
    LemergiumHydride,
    /// RESOURCE_LEMERGIUM_OXIDE: "LO",
    #[serde(rename = "LO")]
    LemergiumOxide,
    /// RESOURCE_ZYNTHIUM_HYDRIDE: "ZH",
    #[serde(rename = "ZH")]
    ZynthiumHydride,
    /// RESOURCE_ZYNTHIUM_OXIDE: "ZO",
    #[serde(rename = "ZO")]
    ZynthiumOxide,
    /// RESOURCE_GHODIUM_HYDRIDE: "GH",
    #[serde(rename = "GH")]
    GhodiumHydride,
    /// RESOURCE_GHODIUM_OXIDE: "GO",
    #[serde(rename = "GO")]
    GhodiumOxide,
    /// RESOURCE_UTRIUM_ACID: "UH2O",
    #[serde(rename = "UH2O")]
    UtriumAcid,
    /// RESOURCE_UTRIUM_ALKALIDE: "UHO2",
    #[serde(rename = "UHO2")]
    UtriumAlkalide,
    /// RESOURCE_KEANIUM_ACID: "KH2O",
    #[serde(rename = "KH2O")]
    KeaniumAcid,
    /// RESOURCE_KEANIUM_ALKALIDE: "KHO2",
    #[serde(rename = "KHO2")]
    KeaniumAlkalide,
    /// RESOURCE_LEMERGIUM_ACID: "LH2O",
    #[serde(rename = "LH2O")]
    LemergiumAcid,
    /// RESOURCE_LEMERGIUM_ALKALIDE: "LHO2",
    #[serde(rename = "LHO2")]
    LemergiumAlkalide,
    /// RESOURCE_ZYNTHIUM_ACID: "ZH2O",
    #[serde(rename = "ZH2O")]
    ZynthiumAcid,
    /// RESOURCE_ZYNTHIUM_ALKALIDE: "ZHO2",
    #[serde(rename = "ZHO2")]
    ZynthiumAlkalide,
    /// RESOURCE_GHODIUM_ACID: "GH2O",
    #[serde(rename = "GH2O")]
    GhodiumAcid,
    /// RESOURCE_GHODIUM_ALKALIDE: "GHO2",
    #[serde(rename = "GHO2")]
    GhodiumAlkalide,
    /// RESOURCE_CATALYZED_UTRIUM_ACID: "XUH2O",
    #[serde(rename = "XUH2O")]
    CatalyzedUtriumAcid,
    /// RESOURCE_CATALYZED_UTRIUM_ALKALIDE: "XUHO2",
    #[serde(rename = "XUHO2")]
    CatalyzedUtriumAlkalide,
    /// RESOURCE_CATALYZED_KEANIUM_ACID: "XKH2O",
    #[serde(rename = "XKH2O")]
    CatalyzedKeaniumAcid,
    /// RESOURCE_CATALYZED_KEANIUM_ALKALIDE: "XKHO2",
    #[serde(rename = "XKHO2")]
    CatalyzedKeaniumAlkalide,
    /// RESOURCE_CATALYZED_LEMERGIUM_ACID: "XLH2O",
    #[serde(rename = "XLH2O")]
    CatalyzedLemergiumAcid,
    /// RESOURCE_CATALYZED_LEMERGIUM_ALKALIDE: "XLHO2",
    #[serde(rename = "XLHO2")]
    CatalyzedLemergiumAlkalide,
    /// RESOURCE_CATALYZED_ZYNTHIUM_ACID: "XZH2O",
    #[serde(rename = "XZH2O")]
    CatalyzedZynthiumAcid,
    /// RESOURCE_CATALYZED_ZYNTHIUM_ALKALIDE: "XZHO2",
    #[serde(rename = "XZHO2")]
    CatalyzedZynthiumAlkalide,
    /// RESOURCE_CATALYZED_GHODIUM_ACID: "XGH2O",
    #[serde(rename = "XGH2O")]
    CatalyzedGhodiumAcid,
    /// RESOURCE_CATALYZED_GHODIUM_ALKALIDE: "XGHO2",
    #[serde(rename = "XGHO2")]
    CatalyzedGhodiumAlkalide,
}

basic_updatable!(ResourceType);

impl ResourceType {
    // created by replacing:
    // `s#/// [A-Z_]+: "(\w+)",\n            (\w+),#ResourceType::$2 => "$1",#g`
    // (original is the definition for the enum)

    /// Finds the in-game resource type string for this resource type.
    ///
    /// Example:
    ///
    /// ```
    /// # use screeps_api::websocket::resources::ResourceType;
    /// assert_eq!(ResourceType::Utrium.to_resource_string(), "U")
    /// ```
    pub fn to_resource_string(&self) -> &'static str {
        match *self {
            ResourceType::Energy => "energy",
            ResourceType::Power => "power",
            ResourceType::Hydrogen => "H",
            ResourceType::Oxygen => "O",
            ResourceType::Utrium => "U",
            ResourceType::Lemergium => "L",
            ResourceType::Keanium => "K",
            ResourceType::Zynthium => "Z",
            ResourceType::Catalyst => "X",
            ResourceType::Ghodium => "G",
            ResourceType::Hydroxide => "OH",
            ResourceType::ZynthiumKeanite => "ZK",
            ResourceType::UtriumLemergite => "UL",
            ResourceType::UtriumHydride => "UH",
            ResourceType::UtriumOxide => "UO",
            ResourceType::KeaniumHydride => "KH",
            ResourceType::KeaniumOxide => "KO",
            ResourceType::LemergiumHydride => "LH",
            ResourceType::LemergiumOxide => "LO",
            ResourceType::ZynthiumHydride => "ZH",
            ResourceType::ZynthiumOxide => "ZO",
            ResourceType::GhodiumHydride => "GH",
            ResourceType::GhodiumOxide => "GO",
            ResourceType::UtriumAcid => "UH2O",
            ResourceType::UtriumAlkalide => "UHO2",
            ResourceType::KeaniumAcid => "KH2O",
            ResourceType::KeaniumAlkalide => "KHO2",
            ResourceType::LemergiumAcid => "LH2O",
            ResourceType::LemergiumAlkalide => "LHO2",
            ResourceType::ZynthiumAcid => "ZH2O",
            ResourceType::ZynthiumAlkalide => "ZHO2",
            ResourceType::GhodiumAcid => "GH2O",
            ResourceType::GhodiumAlkalide => "GHO2",
            ResourceType::CatalyzedUtriumAcid => "XUH2O",
            ResourceType::CatalyzedUtriumAlkalide => "XUHO2",
            ResourceType::CatalyzedKeaniumAcid => "XKH2O",
            ResourceType::CatalyzedKeaniumAlkalide => "XKHO2",
            ResourceType::CatalyzedLemergiumAcid => "XLH2O",
            ResourceType::CatalyzedLemergiumAlkalide => "XLHO2",
            ResourceType::CatalyzedZynthiumAcid => "XZH2O",
            ResourceType::CatalyzedZynthiumAlkalide => "XZHO2",
            ResourceType::CatalyzedGhodiumAcid => "XGH2O",
            ResourceType::CatalyzedGhodiumAlkalide => "XGHO2",
        }
    }
}

pub(crate) mod null_is_default {
    use serde::de::{Deserialize, Deserializer};

    pub fn deserialize<'de, T, D>(d: D) -> Result<T, D::Error>
    where
        T: Default + Deserialize<'de>,
        D: Deserializer<'de>,
    {
        Deserialize::deserialize(d).map(|x: Option<T>| x.unwrap_or(T::default()))
    }
}

pub(crate) mod null_is_default_and_always_some {
    use serde::de::{Deserialize, Deserializer};

    pub fn deserialize<'de, T, D>(d: D) -> Result<Option<T>, D::Error>
    where
        T: Default + Deserialize<'de>,
        D: Deserializer<'de>,
    {
        Deserialize::deserialize(d)
            .map(|x: Option<T>| x.unwrap_or_default())
            .map(Some)
    }
}

/// The resources and amounts of each resource some game object holds.
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
#[serde(transparent)]
pub struct Store(pub HashMap<ResourceType, i32>);

impl Store {
    /// Iterate over the contents of this store.
    pub fn iter(&self) -> impl Iterator<Item = (ResourceType, i32)> + '_ {
        self.0.iter().map(|(k, v)| (*k, *v))
    }

    /// Get the amount of a specific resource in this store.
    pub fn get(&self, resource: ResourceType) -> i32 {
        self.0.get(&resource).copied().unwrap_or(0)
    }
}

impl Updatable for Store {
    type Update = Store;
    fn apply_update(&mut self, update: Self::Update) {
        self.0.extend(update.0);
    }

    fn create_from_update(update: Self::Update) -> Option<Self> {
        Some(update)
    }
}

#[cfg(test)]
macro_rules! store {
    ($($name:ident: $val:expr),*$(,)?) => (
        {
            let mut store = crate::websocket::types::room::resources::Store::default();

            $(
                store.0.insert(crate::websocket::types::room::resources::ResourceType::$name, $val);
            )*

            store
        }
    );
}

// creating this:
// ```python
// import fileinput
// last_line = None
//
// for line in fileinput.input():
//     if last_line is not None:
//         new_split = line.split('|')
//
//         print(last_line.strip() + '|Some(::websocket::resources::ResourceType::' + new_split[0] + ')')
//     last_line = line
// print(last_line.strip() + '|None')
// ```
<commit_msg>Remove two now-unused deserialize helpers<commit_after>//! Managing and parsing resource
use std::collections::HashMap;

use crate::websocket::room_object_macros::Updatable;

/// All possible resource identifiers in the game.
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ResourceType {
    /// RESOURCE_ENERGY: "energy",
    #[serde(rename = "energy")]
    Energy,
    /// RESOURCE_POWER: "power",
    #[serde(rename = "power")]
    Power,
    /// RESOURCE_HYDROGEN: "H",
    #[serde(rename = "H")]
    Hydrogen,
    /// RESOURCE_OXYGEN: "O",
    #[serde(rename = "O")]
    Oxygen,
    /// RESOURCE_UTRIUM: "U",
    #[serde(rename = "U")]
    Utrium,
    /// RESOURCE_LEMERGIUM: "L",
    #[serde(rename = "L")]
    Lemergium,
    /// RESOURCE_KEANIUM: "K",
    #[serde(rename = "K")]
    Keanium,
    /// RESOURCE_ZYNTHIUM: "Z",
    #[serde(rename = "Z")]
    Zynthium,
    /// RESOURCE_CATALYST: "X",
    #[serde(rename = "X")]
    Catalyst,
    /// RESOURCE_GHODIUM: "G",
    #[serde(rename = "G")]
    Ghodium,
    /// RESOURCE_HYDROXIDE: "OH",
    #[serde(rename = "OH")]
    Hydroxide,
    /// RESOURCE_ZYNTHIUM_KEANITE: "ZK",
    #[serde(rename = "ZK")]
    ZynthiumKeanite,
    /// RESOURCE_UTRIUM_LEMERGITE: "UL",
    #[serde(rename = "UL")]
    UtriumLemergite,
    /// RESOURCE_UTRIUM_HYDRIDE: "UH",
    #[serde(rename = "UH")]
    UtriumHydride,
    /// RESOURCE_UTRIUM_OXIDE: "UO",
    #[serde(rename = "UO")]
    UtriumOxide,
    /// RESOURCE_KEANIUM_HYDRIDE: "KH",
    #[serde(rename = "KH")]
    KeaniumHydride,
    /// RESOURCE_KEANIUM_OXIDE: "KO",
    #[serde(rename = "KO")]
    KeaniumOxide,
    /// RESOURCE_LEMERGIUM_HYDRIDE: "LH",
    #[serde(rename = "LH")]
    LemergiumHydride,
    /// RESOURCE_LEMERGIUM_OXIDE: "LO",
    #[serde(rename = "LO")]
    LemergiumOxide,
    /// RESOURCE_ZYNTHIUM_HYDRIDE: "ZH",
    #[serde(rename = "ZH")]
    ZynthiumHydride,
    /// RESOURCE_ZYNTHIUM_OXIDE: "ZO",
    #[serde(rename = "ZO")]
    ZynthiumOxide,
    /// RESOURCE_GHODIUM_HYDRIDE: "GH",
    #[serde(rename = "GH")]
    GhodiumHydride,
    /// RESOURCE_GHODIUM_OXIDE: "GO",
    #[serde(rename = "GO")]
    GhodiumOxide,
    /// RESOURCE_UTRIUM_ACID: "UH2O",
    #[serde(rename = "UH2O")]
    UtriumAcid,
    /// RESOURCE_UTRIUM_ALKALIDE: "UHO2",
    #[serde(rename = "UHO2")]
    UtriumAlkalide,
    /// RESOURCE_KEANIUM_ACID: "KH2O",
    #[serde(rename = "KH2O")]
    KeaniumAcid,
    /// RESOURCE_KEANIUM_ALKALIDE: "KHO2",
    #[serde(rename = "KHO2")]
    KeaniumAlkalide,
    /// RESOURCE_LEMERGIUM_ACID: "LH2O",
    #[serde(rename = "LH2O")]
    LemergiumAcid,
    /// RESOURCE_LEMERGIUM_ALKALIDE: "LHO2",
    #[serde(rename = "LHO2")]
    LemergiumAlkalide,
    /// RESOURCE_ZYNTHIUM_ACID: "ZH2O",
    #[serde(rename = "ZH2O")]
    ZynthiumAcid,
    /// RESOURCE_ZYNTHIUM_ALKALIDE: "ZHO2",
    #[serde(rename = "ZHO2")]
    ZynthiumAlkalide,
    /// RESOURCE_GHODIUM_ACID: "GH2O",
    #[serde(rename = "GH2O")]
    GhodiumAcid,
    /// RESOURCE_GHODIUM_ALKALIDE: "GHO2",
    #[serde(rename = "GHO2")]
    GhodiumAlkalide,
    /// RESOURCE_CATALYZED_UTRIUM_ACID: "XUH2O",
    #[serde(rename = "XUH2O")]
    CatalyzedUtriumAcid,
    /// RESOURCE_CATALYZED_UTRIUM_ALKALIDE: "XUHO2",
    #[serde(rename = "XUHO2")]
    CatalyzedUtriumAlkalide,
    /// RESOURCE_CATALYZED_KEANIUM_ACID: "XKH2O",
    #[serde(rename = "XKH2O")]
    CatalyzedKeaniumAcid,
    /// RESOURCE_CATALYZED_KEANIUM_ALKALIDE: "XKHO2",
    #[serde(rename = "XKHO2")]
    CatalyzedKeaniumAlkalide,
    /// RESOURCE_CATALYZED_LEMERGIUM_ACID: "XLH2O",
    #[serde(rename = "XLH2O")]
    CatalyzedLemergiumAcid,
    /// RESOURCE_CATALYZED_LEMERGIUM_ALKALIDE: "XLHO2",
    #[serde(rename = "XLHO2")]
    CatalyzedLemergiumAlkalide,
    /// RESOURCE_CATALYZED_ZYNTHIUM_ACID: "XZH2O",
    #[serde(rename = "XZH2O")]
    CatalyzedZynthiumAcid,
    /// RESOURCE_CATALYZED_ZYNTHIUM_ALKALIDE: "XZHO2",
    #[serde(rename = "XZHO2")]
    CatalyzedZynthiumAlkalide,
    /// RESOURCE_CATALYZED_GHODIUM_ACID: "XGH2O",
    #[serde(rename = "XGH2O")]
    CatalyzedGhodiumAcid,
    /// RESOURCE_CATALYZED_GHODIUM_ALKALIDE: "XGHO2",
    #[serde(rename = "XGHO2")]
    CatalyzedGhodiumAlkalide,
}

basic_updatable!(ResourceType);

impl ResourceType {
    // created by replacing:
    // `s#/// [A-Z_]+: "(\w+)",\n            (\w+),#ResourceType::$2 => "$1",#g`
    // (original is the definition for the enum)

    /// Finds the in-game resource type string for this resource type.
    ///
    /// Example:
    ///
    /// ```
    /// # use screeps_api::websocket::resources::ResourceType;
    /// assert_eq!(ResourceType::Utrium.to_resource_string(), "U")
    /// ```
    pub fn to_resource_string(&self) -> &'static str {
        match *self {
            ResourceType::Energy => "energy",
            ResourceType::Power => "power",
            ResourceType::Hydrogen => "H",
            ResourceType::Oxygen => "O",
            ResourceType::Utrium => "U",
            ResourceType::Lemergium => "L",
            ResourceType::Keanium => "K",
            ResourceType::Zynthium => "Z",
            ResourceType::Catalyst => "X",
            ResourceType::Ghodium => "G",
            ResourceType::Hydroxide => "OH",
            ResourceType::ZynthiumKeanite => "ZK",
            ResourceType::UtriumLemergite => "UL",
            ResourceType::UtriumHydride => "UH",
            ResourceType::UtriumOxide => "UO",
            ResourceType::KeaniumHydride => "KH",
            ResourceType::KeaniumOxide => "KO",
            ResourceType::LemergiumHydride => "LH",
            ResourceType::LemergiumOxide => "LO",
            ResourceType::ZynthiumHydride => "ZH",
            ResourceType::ZynthiumOxide => "ZO",
            ResourceType::GhodiumHydride => "GH",
            ResourceType::GhodiumOxide => "GO",
            ResourceType::UtriumAcid => "UH2O",
            ResourceType::UtriumAlkalide => "UHO2",
            ResourceType::KeaniumAcid => "KH2O",
            ResourceType::KeaniumAlkalide => "KHO2",
            ResourceType::LemergiumAcid => "LH2O",
            ResourceType::LemergiumAlkalide => "LHO2",
            ResourceType::ZynthiumAcid => "ZH2O",
            ResourceType::ZynthiumAlkalide => "ZHO2",
            ResourceType::GhodiumAcid => "GH2O",
            ResourceType::GhodiumAlkalide => "GHO2",
            ResourceType::CatalyzedUtriumAcid => "XUH2O",
            ResourceType::CatalyzedUtriumAlkalide => "XUHO2",
            ResourceType::CatalyzedKeaniumAcid => "XKH2O",
            ResourceType::CatalyzedKeaniumAlkalide => "XKHO2",
            ResourceType::CatalyzedLemergiumAcid => "XLH2O",
            ResourceType::CatalyzedLemergiumAlkalide => "XLHO2",
            ResourceType::CatalyzedZynthiumAcid => "XZH2O",
            ResourceType::CatalyzedZynthiumAlkalide => "XZHO2",
            ResourceType::CatalyzedGhodiumAcid => "XGH2O",
            ResourceType::CatalyzedGhodiumAlkalide => "XGHO2",
        }
    }
}

/// The resources and amounts of each resource some game object holds.
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
#[serde(transparent)]
pub struct Store(pub HashMap<ResourceType, i32>);

impl Store {
    /// Iterate over the contents of this store.
    pub fn iter(&self) -> impl Iterator<Item = (ResourceType, i32)> + '_ {
        self.0.iter().map(|(k, v)| (*k, *v))
    }

    /// Get the amount of a specific resource in this store.
    pub fn get(&self, resource: ResourceType) -> i32 {
        self.0.get(&resource).copied().unwrap_or(0)
    }
}

impl Updatable for Store {
    type Update = Store;
    fn apply_update(&mut self, update: Self::Update) {
        self.0.extend(update.0);
    }

    fn create_from_update(update: Self::Update) -> Option<Self> {
        Some(update)
    }
}

#[cfg(test)]
macro_rules! store {
    ($($name:ident: $val:expr),*$(,)?) => (
        {
            let mut store = crate::websocket::types::room::resources::Store::default();

            $(
                store.0.insert(crate::websocket::types::room::resources::ResourceType::$name, $val);
            )*

            store
        }
    );
}

// creating this:
// ```python
// import fileinput
// last_line = None
//
// for line in fileinput.input():
//     if last_line is not None:
//         new_split = line.split('|')
//
//         print(last_line.strip() + '|Some(::websocket::resources::ResourceType::' + new_split[0] + ')')
//     last_line = line
// print(last_line.strip() + '|None')
// ```
<|endoftext|>


<commit_before>use std::cell::Cell;

use cairo::MatrixTrait;
use cssparser;
use nalgebra::Vector3;

use attributes::Attribute;
use error::NodeError;
use filters::context::FilterContext;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, RsvgNode};
use parsers;
use property_bag::PropertyBag;
use util::clamp;

/// A light source node (`feDistantLight`, `fePointLight` or `feSpotLight`).
#[derive(Clone)]
pub enum LightSource {
    Distant {
        azimuth: Cell<f64>,
        elevation: Cell<f64>,
    },
    Point {
        x: Cell<f64>,
        y: Cell<f64>,
        z: Cell<f64>,
    },
    Spot {
        x: Cell<f64>,
        y: Cell<f64>,
        z: Cell<f64>,
        points_at_x: Cell<f64>,
        points_at_y: Cell<f64>,
        points_at_z: Cell<f64>,
        specular_exponent: Cell<f64>,
        limiting_cone_angle: Cell<Option<f64>>,
    },
}

/// A light source node with affine transformations applied.
pub enum TransformedLightSource {
    Distant {
        azimuth: f64,
        elevation: f64,
    },
    Point {
        x: f64,
        y: f64,
        z: f64,
    },
    Spot {
        x: f64,
        y: f64,
        z: f64,
        points_at_x: f64,
        points_at_y: f64,
        points_at_z: f64,
        specular_exponent: f64,
        limiting_cone_angle: Option<f64>,
    },
}

impl LightSource {
    /// Constructs a new `feDistantLight` with empty properties.
    #[inline]
    pub fn new_distant_light() -> LightSource {
        LightSource::Distant {
            azimuth: Cell::new(0.0),
            elevation: Cell::new(0.0),
        }
    }

    /// Constructs a new `fePointLight` with empty properties.
    #[inline]
    pub fn new_point_light() -> LightSource {
        LightSource::Point {
            x: Cell::new(0.0),
            y: Cell::new(0.0),
            z: Cell::new(0.0),
        }
    }

    /// Constructs a new `feSpotLight` with empty properties.
    #[inline]
    pub fn new_spot_light() -> LightSource {
        LightSource::Spot {
            x: Cell::new(0.0),
            y: Cell::new(0.0),
            z: Cell::new(0.0),
            points_at_x: Cell::new(0.0),
            points_at_y: Cell::new(0.0),
            points_at_z: Cell::new(0.0),
            specular_exponent: Cell::new(0.0),
            limiting_cone_angle: Cell::new(None),
        }
    }

    /// Returns a `TransformedLightSource` according to the given `FilterContext`.
    #[inline]
    pub fn transform(&self, ctx: &FilterContext) -> TransformedLightSource {
        match self {
            LightSource::Distant { azimuth, elevation } => TransformedLightSource::Distant {
                azimuth: azimuth.get(),
                elevation: elevation.get(),
            },
            LightSource::Point { x, y, z } => {
                let (x, y) = ctx.paffine().transform_point(x.get(), y.get());
                let z = ctx.transform_dist(z.get());

                TransformedLightSource::Point { x, y, z }
            }
            LightSource::Spot {
                x,
                y,
                z,
                points_at_x,
                points_at_y,
                points_at_z,
                specular_exponent,
                limiting_cone_angle,
            } => {
                let (x, y) = ctx.paffine().transform_point(x.get(), y.get());
                let z = ctx.transform_dist(z.get());
                let (points_at_x, points_at_y) = ctx
                    .paffine()
                    .transform_point(points_at_x.get(), points_at_y.get());
                let points_at_z = ctx.transform_dist(points_at_z.get());

                TransformedLightSource::Spot {
                    x,
                    y,
                    z,
                    points_at_x,
                    points_at_y,
                    points_at_z,
                    specular_exponent: specular_exponent.get(),
                    limiting_cone_angle: limiting_cone_angle.get(),
                }
            }
        }
    }
}

impl TransformedLightSource {
    /// Returns the unit (or null) vector from the image sample to the light.
    #[inline]
    pub fn vector(&self, x: f64, y: f64, z: f64) -> Vector3<f64> {
        match self {
            TransformedLightSource::Distant { azimuth, elevation } => {
                let azimuth = azimuth.to_radians();
                let elevation = elevation.to_radians();
                Vector3::new(
                    azimuth.cos() * elevation.cos(),
                    azimuth.sin() * elevation.cos(),
                    elevation.sin(),
                )
            }
            TransformedLightSource::Point {
                x: light_x,
                y: light_y,
                z: light_z,
            }
            | TransformedLightSource::Spot {
                x: light_x,
                y: light_y,
                z: light_z,
                ..
            } => {
                let mut v = Vector3::new(light_x - x, light_y - y, light_z - z);
                let _ = v.try_normalize_mut(0.0);
                v
            }
        }
    }

    /// Returns the color of the light.
    #[inline]
    pub fn color(
        &self,
        lighting_color: cssparser::RGBA,
        light_vector: Vector3<f64>,
    ) -> cssparser::RGBA {
        match self {
            TransformedLightSource::Spot {
                x: light_x,
                y: light_y,
                z: light_z,
                points_at_x,
                points_at_y,
                points_at_z,
                specular_exponent,
                limiting_cone_angle,
                ..
            } => {
                let mut s = Vector3::new(
                    points_at_x - light_x,
                    points_at_y - light_y,
                    points_at_z - light_z,
                );
                if s.try_normalize_mut(0.0).is_none() {
                    return cssparser::RGBA::transparent();
                }

                let minus_l_dot_s = -light_vector.dot(&s);
                if minus_l_dot_s <= 0.0 {
                    return cssparser::RGBA::transparent();
                }

                if let Some(limiting_cone_angle) = limiting_cone_angle {
                    if minus_l_dot_s < limiting_cone_angle.to_radians().cos() {
                        return cssparser::RGBA::transparent();
                    }
                }

                let factor = minus_l_dot_s.powf(*specular_exponent);
                let compute = |x| clamp(f64::from(x) * factor, 0.0, 255.0).round() as u8;

                cssparser::RGBA {
                    red: compute(lighting_color.red),
                    green: compute(lighting_color.green),
                    blue: compute(lighting_color.blue),
                    alpha: 255,
                }
            }
            _ => lighting_color,
        }
    }
}

impl NodeTrait for LightSource {
    fn set_atts(
        &self,
        _node: &RsvgNode,
        _handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match self {
                LightSource::Distant {
                    ref azimuth,
                    ref elevation,
                } => match attr {
                    Attribute::Azimuth => azimuth.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Elevation => elevation.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    _ => (),
                },
                LightSource::Point {
                    ref x,
                    ref y,
                    ref z,
                } => match attr {
                    Attribute::X => x.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Y => y.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Z => z.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    _ => (),
                },
                LightSource::Spot {
                    ref x,
                    ref y,
                    ref z,
                    ref points_at_x,
                    ref points_at_y,
                    ref points_at_z,
                    ref specular_exponent,
                    ref limiting_cone_angle,
                } => match attr {
                    Attribute::X => x.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Y => y.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Z => z.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::PointsAtX => points_at_x.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::PointsAtY => points_at_y.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::PointsAtZ => points_at_z.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::SpecularExponent => specular_exponent.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::LimitingConeAngle => limiting_cone_angle.set(Some(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    )),
                    _ => (),
                },
            }
        }

        Ok(())
    }
}
<commit_msg>Precompute direction in TransformedLightSource<commit_after>use std::cell::Cell;

use cairo::MatrixTrait;
use cssparser;
use nalgebra::Vector3;

use attributes::Attribute;
use error::NodeError;
use filters::context::FilterContext;
use handle::RsvgHandle;
use node::{NodeResult, NodeTrait, RsvgNode};
use parsers;
use property_bag::PropertyBag;
use util::clamp;

/// A light source node (`feDistantLight`, `fePointLight` or `feSpotLight`).
#[derive(Clone)]
pub enum LightSource {
    Distant {
        azimuth: Cell<f64>,
        elevation: Cell<f64>,
    },
    Point {
        x: Cell<f64>,
        y: Cell<f64>,
        z: Cell<f64>,
    },
    Spot {
        x: Cell<f64>,
        y: Cell<f64>,
        z: Cell<f64>,
        points_at_x: Cell<f64>,
        points_at_y: Cell<f64>,
        points_at_z: Cell<f64>,
        specular_exponent: Cell<f64>,
        limiting_cone_angle: Cell<Option<f64>>,
    },
}

/// A light source node with affine transformations applied.
pub enum TransformedLightSource {
    Distant {
        azimuth: f64,
        elevation: f64,
    },
    Point {
        origin: Vector3<f64>,
    },
    Spot {
        origin: Vector3<f64>,
        direction: Vector3<f64>,
        specular_exponent: f64,
        limiting_cone_angle: Option<f64>,
    },
}

impl LightSource {
    /// Constructs a new `feDistantLight` with empty properties.
    #[inline]
    pub fn new_distant_light() -> LightSource {
        LightSource::Distant {
            azimuth: Cell::new(0.0),
            elevation: Cell::new(0.0),
        }
    }

    /// Constructs a new `fePointLight` with empty properties.
    #[inline]
    pub fn new_point_light() -> LightSource {
        LightSource::Point {
            x: Cell::new(0.0),
            y: Cell::new(0.0),
            z: Cell::new(0.0),
        }
    }

    /// Constructs a new `feSpotLight` with empty properties.
    #[inline]
    pub fn new_spot_light() -> LightSource {
        LightSource::Spot {
            x: Cell::new(0.0),
            y: Cell::new(0.0),
            z: Cell::new(0.0),
            points_at_x: Cell::new(0.0),
            points_at_y: Cell::new(0.0),
            points_at_z: Cell::new(0.0),
            specular_exponent: Cell::new(0.0),
            limiting_cone_angle: Cell::new(None),
        }
    }

    /// Returns a `TransformedLightSource` according to the given `FilterContext`.
    #[inline]
    pub fn transform(&self, ctx: &FilterContext) -> TransformedLightSource {
        match self {
            LightSource::Distant { azimuth, elevation } => TransformedLightSource::Distant {
                azimuth: azimuth.get(),
                elevation: elevation.get(),
            },
            LightSource::Point { x, y, z } => {
                let (x, y) = ctx.paffine().transform_point(x.get(), y.get());
                let z = ctx.transform_dist(z.get());

                TransformedLightSource::Point {
                    origin: Vector3::new(x, y, z),
                }
            }
            LightSource::Spot {
                x,
                y,
                z,
                points_at_x,
                points_at_y,
                points_at_z,
                specular_exponent,
                limiting_cone_angle,
            } => {
                let (x, y) = ctx.paffine().transform_point(x.get(), y.get());
                let z = ctx.transform_dist(z.get());
                let (points_at_x, points_at_y) = ctx
                    .paffine()
                    .transform_point(points_at_x.get(), points_at_y.get());
                let points_at_z = ctx.transform_dist(points_at_z.get());

                let origin = Vector3::new(x, y, z);
                let mut direction = Vector3::new(points_at_x, points_at_y, points_at_z) - origin;
                let _ = direction.try_normalize_mut(0.0);

                TransformedLightSource::Spot {
                    origin,
                    direction,
                    specular_exponent: specular_exponent.get(),
                    limiting_cone_angle: limiting_cone_angle.get(),
                }
            }
        }
    }
}

impl TransformedLightSource {
    /// Returns the unit (or null) vector from the image sample to the light.
    #[inline]
    pub fn vector(&self, x: f64, y: f64, z: f64) -> Vector3<f64> {
        match self {
            TransformedLightSource::Distant { azimuth, elevation } => {
                let azimuth = azimuth.to_radians();
                let elevation = elevation.to_radians();
                Vector3::new(
                    azimuth.cos() * elevation.cos(),
                    azimuth.sin() * elevation.cos(),
                    elevation.sin(),
                )
            }
            TransformedLightSource::Point { origin }
            | TransformedLightSource::Spot { origin, .. } => {
                let mut v = origin - Vector3::new(x, y, z);
                let _ = v.try_normalize_mut(0.0);
                v
            }
        }
    }

    /// Returns the color of the light.
    #[inline]
    pub fn color(
        &self,
        lighting_color: cssparser::RGBA,
        light_vector: Vector3<f64>,
    ) -> cssparser::RGBA {
        match self {
            TransformedLightSource::Spot {
                direction,
                specular_exponent,
                limiting_cone_angle,
                ..
            } => {
                let minus_l_dot_s = -light_vector.dot(&direction);
                if minus_l_dot_s <= 0.0 {
                    return cssparser::RGBA::transparent();
                }

                if let Some(limiting_cone_angle) = limiting_cone_angle {
                    if minus_l_dot_s < limiting_cone_angle.to_radians().cos() {
                        return cssparser::RGBA::transparent();
                    }
                }

                let factor = minus_l_dot_s.powf(*specular_exponent);
                let compute = |x| (clamp(f64::from(x) * factor, 0.0, 255.0) + 0.5) as u8;

                cssparser::RGBA {
                    red: compute(lighting_color.red),
                    green: compute(lighting_color.green),
                    blue: compute(lighting_color.blue),
                    alpha: 255,
                }
            }
            _ => lighting_color,
        }
    }
}

impl NodeTrait for LightSource {
    fn set_atts(
        &self,
        _node: &RsvgNode,
        _handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match self {
                LightSource::Distant {
                    ref azimuth,
                    ref elevation,
                } => match attr {
                    Attribute::Azimuth => azimuth.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Elevation => elevation.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    _ => (),
                },
                LightSource::Point {
                    ref x,
                    ref y,
                    ref z,
                } => match attr {
                    Attribute::X => x.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Y => y.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Z => z.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    _ => (),
                },
                LightSource::Spot {
                    ref x,
                    ref y,
                    ref z,
                    ref points_at_x,
                    ref points_at_y,
                    ref points_at_z,
                    ref specular_exponent,
                    ref limiting_cone_angle,
                } => match attr {
                    Attribute::X => x.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Y => y.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::Z => z.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::PointsAtX => points_at_x.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::PointsAtY => points_at_y.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::PointsAtZ => points_at_z.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::SpecularExponent => specular_exponent.set(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    ),
                    Attribute::LimitingConeAngle => limiting_cone_angle.set(Some(
                        parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?,
                    )),
                    _ => (),
                },
            }
        }

        Ok(())
    }
}
<|endoftext|>