<commit_before>use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg};
use rustc_hir::{GenericArg, Local, Mutability, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

use if_chain::if_chain;
use rustc_errors::Applicability;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `&Option<&T>`.
    ///
    /// **Why is this bad?** Since `&` is Copy, it's useless to have a
    /// reference on `Option<&T>`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,ignore
    /// // example code where clippy issues a warning
    /// let x: &Option<&u32> = &Some(&0u32);
    /// ```
    /// Use instead:
    /// ```rust,ignore
    /// // example code which does not raise clippy warning
    /// let x: Option<&u32> = Some(&0u32);
    /// ```
    pub REF_OPTION_REF,
    style,
    "use `Option<&T>` instead of `&Option<&T>`"
}

declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);

impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
        if_chain! {
            if let Some(ref ty) = local.ty;
            if let TyKind::Rptr(_, ref mut_ty) = ty.kind;
            if mut_ty.mutbl == Mutability::Not;
            if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ;
            if let Some(def_id) = cx.typeck_results().qpath_res(qpath, local.hir_id).opt_def_id();
            if match_def_path(cx, def_id, &paths::OPTION);
            if let Some(ref params) = last_path_segment(qpath).args ;
            if !params.parenthesized;
            if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
                GenericArg::Type(inner_ty) => Some(inner_ty),
                _ => None,
            });
            if let TyKind::Rptr(_, _) = inner_ty.kind;

            then {
                span_lint_and_sugg(
                    cx,
                    REF_OPTION_REF,
                    ty.span,
                    "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>",
                    "try",
                    format!("Option<{}>", &snippet(cx, inner_ty.span, "..")),
                    Applicability::MachineApplicable,
                );
            }
        }
    }
}
<commit_msg>clippy_lint: Add 'ref_option_ref' refactor code<commit_after>use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg};
use rustc_hir::{GenericArg, Local, Mutability, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

use if_chain::if_chain;
use rustc_errors::Applicability;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `&Option<&T>`.
    ///
    /// **Why is this bad?** Since `&` is Copy, it's useless to have a
    /// reference on `Option<&T>`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,ignore
    /// // example code where clippy issues a warning
    /// let x: &Option<&u32> = &Some(&0u32);
    /// ```
    /// Use instead:
    /// ```rust,ignore
    /// // example code which does not raise clippy warning
    /// let x: Option<&u32> = Some(&0u32);
    /// ```
    pub REF_OPTION_REF,
    style,
    "use `Option<&T>` instead of `&Option<&T>`"
}

declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);

impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {

        if let Some(ref ty) = local.ty {
            self.check_ref_option_ref(cx, ty);
        }
    }
}

impl RefOptionRef {
    fn check_ref_option_ref(&self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
        if_chain! {
            if let TyKind::Rptr(_, ref mut_ty) = ty.kind;
            if mut_ty.mutbl == Mutability::Not;
            if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ;
            if let Some(def_id) = cx.typeck_results().qpath_res(qpath, ty.hir_id).opt_def_id();
            if match_def_path(cx, def_id, &paths::OPTION);
            if let Some(ref params) = last_path_segment(qpath).args ;
            if !params.parenthesized;
            if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
                GenericArg::Type(inner_ty) => Some(inner_ty),
                _ => None,
            });
            if let TyKind::Rptr(_, _) = inner_ty.kind;

            then {
                span_lint_and_sugg(
                    cx,
                    REF_OPTION_REF,
                    ty.span,
                    "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>",
                    "try",
                    format!("Option<{}>", &snippet(cx, inner_ty.span, "..")),
                    Applicability::Unspecified,
                );
            }
        }
    }
}<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#![crate_name = "webrender_traits"]
#![crate_type = "rlib"]
#![deny(unsafe_code)]

use euclid::Size2D;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

/// This trait is used as a bridge between the different GL clients
/// in Servo that handles WebRender ExternalImages and the WebRender
/// ExternalImageHandler API.
//
/// This trait is used to notify lock/unlock messages and get the
/// required info that WR needs.
pub trait WebrenderExternalImageApi {
    fn lock(&mut self, id: u64) -> (u32, Size2D<i32>);
    fn unlock(&mut self, id: u64);
}

/// Type of Webrender External Image Handler.
pub enum WebrenderImageHandlerType {
    WebGL,
    Media,
}

/// List of Webrender external images to be shared among all external image
/// consumers (WebGL, Media).
/// It ensures that external image identifiers are unique.
pub struct WebrenderExternalImageRegistry {
    /// Map of all generated external images.
    external_images: HashMap<webrender_api::ExternalImageId, WebrenderImageHandlerType>,
    /// Id generator for the next external image identifier.
    next_image_id: u64,
}

impl WebrenderExternalImageRegistry {
    pub fn new() -> Self {
        Self {
            external_images: HashMap::new(),
            next_image_id: 0,
        }
    }

    pub fn next_id(
        &mut self,
        handler_type: WebrenderImageHandlerType,
    ) -> webrender_api::ExternalImageId {
        self.next_image_id += 1;
        let key = webrender_api::ExternalImageId(self.next_image_id);
        self.external_images.insert(key, handler_type);
        key
    }

    pub fn remove(&mut self, key: &webrender_api::ExternalImageId) {
        self.external_images.remove(key);
    }

    pub fn get(&self, key: &webrender_api::ExternalImageId) -> Option<&WebrenderImageHandlerType> {
        self.external_images.get(key)
    }
}

/// WebRender External Image Handler implementation.
pub struct WebrenderExternalImageHandlers {
    /// WebGL handler.
    webgl_handler: Option<Box<dyn WebrenderExternalImageApi>>,
    /// Media player handler.
    media_handler: Option<Box<dyn WebrenderExternalImageApi>>,
    /// Webrender external images.
    external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
}

impl WebrenderExternalImageHandlers {
    pub fn new() -> (Self, Arc<Mutex<WebrenderExternalImageRegistry>>) {
        let external_images = Arc::new(Mutex::new(WebrenderExternalImageRegistry::new()));
        (
            Self {
                webgl_handler: None,
                media_handler: None,
                external_images: external_images.clone(),
            },
            external_images,
        )
    }

    pub fn set_handler(
        &mut self,
        handler: Box<dyn WebrenderExternalImageApi>,
        handler_type: WebrenderImageHandlerType,
    ) {
        match handler_type {
            WebrenderImageHandlerType::WebGL => self.webgl_handler = Some(handler),
            WebrenderImageHandlerType::Media => self.media_handler = Some(handler),
        }
    }
}

impl webrender::ExternalImageHandler for WebrenderExternalImageHandlers {
    /// Lock the external image. Then, WR could start to read the
    /// image content.
    /// The WR client should not change the image content until the
    /// unlock() call.
    fn lock(
        &mut self,
        key: webrender_api::ExternalImageId,
        _channel_index: u8,
        _rendering: webrender_api::ImageRendering,
    ) -> webrender::ExternalImage {
        if let Some(handler_type) = self.external_images.lock().unwrap().get(&key) {
            let (texture_id, size) = match handler_type {
                WebrenderImageHandlerType::WebGL => {
                    self.webgl_handler.as_mut().unwrap().lock(key.0)
                },
                WebrenderImageHandlerType::Media => {
                    self.media_handler.as_mut().unwrap().lock(key.0)
                },
            };
            webrender::ExternalImage {
                uv: webrender_api::TexelRect::new(0.0, 0.0, size.width as f32, size.height as f32),
                source: webrender::ExternalImageSource::NativeTexture(texture_id),
            }
        } else {
            unreachable!()
        }
    }

    /// Unlock the external image. The WR should not read the image
    /// content after this call.
    fn unlock(&mut self, key: webrender_api::ExternalImageId, _channel_index: u8) {
        if let Some(handler_type) = self.external_images.lock().unwrap().get(&key) {
            match handler_type {
                WebrenderImageHandlerType::WebGL => {
                    self.webgl_handler.as_mut().unwrap().unlock(key.0)
                },
                WebrenderImageHandlerType::Media => {
                    self.media_handler.as_mut().unwrap().unlock(key.0)
                },
            };
        } else {
            unreachable!();
        }
    }
}
<commit_msg>Differentiate texel space coordinates for webgl and media<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#![crate_name = "webrender_traits"]
#![crate_type = "rlib"]
#![deny(unsafe_code)]

use euclid::Size2D;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

/// This trait is used as a bridge between the different GL clients
/// in Servo that handles WebRender ExternalImages and the WebRender
/// ExternalImageHandler API.
//
/// This trait is used to notify lock/unlock messages and get the
/// required info that WR needs.
pub trait WebrenderExternalImageApi {
    fn lock(&mut self, id: u64) -> (u32, Size2D<i32>);
    fn unlock(&mut self, id: u64);
}

/// Type of Webrender External Image Handler.
pub enum WebrenderImageHandlerType {
    WebGL,
    Media,
}

/// List of Webrender external images to be shared among all external image
/// consumers (WebGL, Media).
/// It ensures that external image identifiers are unique.
pub struct WebrenderExternalImageRegistry {
    /// Map of all generated external images.
    external_images: HashMap<webrender_api::ExternalImageId, WebrenderImageHandlerType>,
    /// Id generator for the next external image identifier.
    next_image_id: u64,
}

impl WebrenderExternalImageRegistry {
    pub fn new() -> Self {
        Self {
            external_images: HashMap::new(),
            next_image_id: 0,
        }
    }

    pub fn next_id(
        &mut self,
        handler_type: WebrenderImageHandlerType,
    ) -> webrender_api::ExternalImageId {
        self.next_image_id += 1;
        let key = webrender_api::ExternalImageId(self.next_image_id);
        self.external_images.insert(key, handler_type);
        key
    }

    pub fn remove(&mut self, key: &webrender_api::ExternalImageId) {
        self.external_images.remove(key);
    }

    pub fn get(&self, key: &webrender_api::ExternalImageId) -> Option<&WebrenderImageHandlerType> {
        self.external_images.get(key)
    }
}

/// WebRender External Image Handler implementation.
pub struct WebrenderExternalImageHandlers {
    /// WebGL handler.
    webgl_handler: Option<Box<dyn WebrenderExternalImageApi>>,
    /// Media player handler.
    media_handler: Option<Box<dyn WebrenderExternalImageApi>>,
    /// Webrender external images.
    external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
}

impl WebrenderExternalImageHandlers {
    pub fn new() -> (Self, Arc<Mutex<WebrenderExternalImageRegistry>>) {
        let external_images = Arc::new(Mutex::new(WebrenderExternalImageRegistry::new()));
        (
            Self {
                webgl_handler: None,
                media_handler: None,
                external_images: external_images.clone(),
            },
            external_images,
        )
    }

    pub fn set_handler(
        &mut self,
        handler: Box<dyn WebrenderExternalImageApi>,
        handler_type: WebrenderImageHandlerType,
    ) {
        match handler_type {
            WebrenderImageHandlerType::WebGL => self.webgl_handler = Some(handler),
            WebrenderImageHandlerType::Media => self.media_handler = Some(handler),
        }
    }
}

impl webrender::ExternalImageHandler for WebrenderExternalImageHandlers {
    /// Lock the external image. Then, WR could start to read the
    /// image content.
    /// The WR client should not change the image content until the
    /// unlock() call.
    fn lock(
        &mut self,
        key: webrender_api::ExternalImageId,
        _channel_index: u8,
        _rendering: webrender_api::ImageRendering,
    ) -> webrender::ExternalImage {
        if let Some(handler_type) = self.external_images.lock().unwrap().get(&key) {
            let (texture_id, uv) = match handler_type {
                WebrenderImageHandlerType::WebGL => {
                    let (texture_id, size) = self.webgl_handler.as_mut().unwrap().lock(key.0);
                    (
                        texture_id,
                        webrender_api::TexelRect::new(
                            0.0,
                            size.height as f32,
                            size.width as f32,
                            0.0,
                        ),
                    )
                },
                WebrenderImageHandlerType::Media => {
                    let (texture_id, size) = self.media_handler.as_mut().unwrap().lock(key.0);
                    (
                        texture_id,
                        webrender_api::TexelRect::new(
                            0.0,
                            0.0,
                            size.width as f32,
                            size.height as f32,
                        ),
                    )
                },
            };
            webrender::ExternalImage {
                uv,
                source: webrender::ExternalImageSource::NativeTexture(texture_id),
            }
        } else {
            unreachable!()
        }
    }

    /// Unlock the external image. The WR should not read the image
    /// content after this call.
    fn unlock(&mut self, key: webrender_api::ExternalImageId, _channel_index: u8) {
        if let Some(handler_type) = self.external_images.lock().unwrap().get(&key) {
            match handler_type {
                WebrenderImageHandlerType::WebGL => {
                    self.webgl_handler.as_mut().unwrap().unlock(key.0)
                },
                WebrenderImageHandlerType::Media => {
                    self.media_handler.as_mut().unwrap().unlock(key.0)
                },
            };
        } else {
            unreachable!();
        }
    }
}
<|endoftext|>


<commit_before>use super::ir::*;
use super::module_decls::*;

use crate::graph;

use std::collections::HashMap;

pub struct Compiler<'graph> {
    signal_exprs: HashMap<&'graph graph::Signal<'graph>, Expr>,
}

impl<'graph> Compiler<'graph> {
    pub fn new() -> Compiler<'graph> {
        Compiler {
            signal_exprs: HashMap::new(),
        }
    }

    pub fn compile_signal(
        &mut self,
        signal: &'graph graph::Signal<'graph>,
        module_decls: &ModuleDecls<'graph>,
        a: &mut AssignmentContext,
    ) -> Expr {
        enum Frame<'graph> {
            Enter(&'graph graph::Signal<'graph>),
            Leave(&'graph graph::Signal<'graph>),
        }

        let mut frames = Vec::new();
        frames.push(Frame::Enter(signal));

        let mut results = Vec::new();

        while let Some(frame) = frames.pop() {
            if let Some(expr) = match frame {
                Frame::Enter(signal) => {
                    if let Some(expr) = self.signal_exprs.get(&signal) {
                        results.push(expr.clone());
                        continue;
                    }

                    match signal.data {
                        graph::SignalData::Lit {
                            ref value,
                            bit_width,
                        } => Some(Expr::from_constant(value, bit_width)),

                        graph::SignalData::Input { ref name, .. } => {
                            Some(Expr::Ref { name: name.clone() })
                        }

                        graph::SignalData::Reg { .. } => Some(Expr::Ref {
                            name: module_decls.regs[&signal].value_name.clone(),
                        }),

                        graph::SignalData::UnOp { source, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(source));
                            None
                        }
                        graph::SignalData::SimpleBinOp { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }
                        graph::SignalData::AdditiveBinOp { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }
                        graph::SignalData::ComparisonBinOp { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }
                        graph::SignalData::ShiftBinOp { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }

                        graph::SignalData::Mul { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }
                        graph::SignalData::MulSigned { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }

                        graph::SignalData::Bits { source, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(source));
                            None
                        }

                        graph::SignalData::Repeat { source, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(source));
                            None
                        }
                        graph::SignalData::Concat { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }

                        graph::SignalData::Mux {
                            cond,
                            when_true,
                            when_false,
                            ..
                        } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(cond));
                            frames.push(Frame::Enter(when_true));
                            frames.push(Frame::Enter(when_false));
                            None
                        }

                        graph::SignalData::InstanceOutput {
                            instance, ref name, ..
                        } => {
                            let instance_decls = &module_decls.instances[&instance];
                            Some(Expr::Ref {
                                name: instance_decls.output_names[name].clone(),
                            })
                        }

                        graph::SignalData::MemReadPortOutput {
                            mem,
                            address,
                            enable,
                        } => {
                            let mem = &module_decls.mems[&mem];
                            let read_signal_names = &mem.read_signal_names[&(address, enable)];
                            Some(Expr::Ref {
                                name: read_signal_names.value_name.clone(),
                            })
                        }
                    }
                }
                Frame::Leave(signal) => {
                    match signal.data {
                        graph::SignalData::Lit { .. } => unreachable!(),

                        graph::SignalData::Input { .. } => unreachable!(),

                        graph::SignalData::Reg { .. } => unreachable!(),

                        graph::SignalData::UnOp { op, bit_width, .. } => {
                            let source = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::UnOp {
                                    source: Box::new(source),
                                    op: match op {
                                        graph::UnOp::Not => UnOp::Not,
                                    },
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::SimpleBinOp { op, bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: match op {
                                        graph::SimpleBinOp::BitAnd => BinOp::BitAnd,
                                        graph::SimpleBinOp::BitOr => BinOp::BitOr,
                                        graph::SimpleBinOp::BitXor => BinOp::BitXor,
                                    },
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::AdditiveBinOp { op, bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: match op {
                                        graph::AdditiveBinOp::Add => BinOp::Add,
                                        graph::AdditiveBinOp::Sub => BinOp::Sub,
                                    },
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::ComparisonBinOp { op, .. } => {
                            let bit_width = signal.bit_width();
                            let mut lhs = results.pop().unwrap();
                            let mut rhs = results.pop().unwrap();
                            match op {
                                graph::ComparisonBinOp::GreaterThanEqualSigned
                                | graph::ComparisonBinOp::GreaterThanSigned
                                | graph::ComparisonBinOp::LessThanEqualSigned
                                | graph::ComparisonBinOp::LessThanSigned => {
                                    lhs = Expr::Signed {
                                        source: Box::new(lhs),
                                    };
                                    rhs = Expr::Signed {
                                        source: Box::new(rhs),
                                    };
                                }
                                _ => (),
                            }
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: match op {
                                        graph::ComparisonBinOp::Equal => BinOp::Equal,
                                        graph::ComparisonBinOp::NotEqual => BinOp::NotEqual,
                                        graph::ComparisonBinOp::LessThan
                                        | graph::ComparisonBinOp::LessThanSigned => BinOp::LessThan,
                                        graph::ComparisonBinOp::LessThanEqual
                                        | graph::ComparisonBinOp::LessThanEqualSigned => {
                                            BinOp::LessThanEqual
                                        }
                                        graph::ComparisonBinOp::GreaterThan
                                        | graph::ComparisonBinOp::GreaterThanSigned => {
                                            BinOp::GreaterThan
                                        }
                                        graph::ComparisonBinOp::GreaterThanEqual
                                        | graph::ComparisonBinOp::GreaterThanEqualSigned => {
                                            BinOp::GreaterThanEqual
                                        }
                                    },
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::ShiftBinOp { op, bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: match op {
                                        graph::ShiftBinOp::Shl => BinOp::Shl,
                                        graph::ShiftBinOp::Shr => BinOp::Shr,
                                        graph::ShiftBinOp::ShrArithmetic => BinOp::ShrArithmetic,
                                    },
                                },
                                bit_width,
                            ))
                        }

                        graph::SignalData::Mul { bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: BinOp::Mul,
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::MulSigned { bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            let lhs = Expr::Signed {
                                source: Box::new(lhs),
                            };
                            let rhs = Expr::Signed {
                                source: Box::new(rhs),
                            };
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: BinOp::Mul,
                                },
                                bit_width,
                            ))
                        }

                        graph::SignalData::Bits {
                            range_high,
                            range_low,
                            ..
                        } => {
                            let bit_width = signal.bit_width();
                            let source = results.pop().unwrap();
                            // Verilog doesn't allow indexing scalars
                            Some(if bit_width == 1 {
                                source
                            } else {
                                a.gen_temp(
                                    Expr::Bits {
                                        source: Box::new(source),
                                        range_high,
                                        range_low,
                                    },
                                    bit_width,
                                )
                            })
                        }

                        graph::SignalData::Repeat {
                            count, bit_width, ..
                        } => {
                            let source = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::Repeat {
                                    source: Box::new(source),
                                    count,
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::Concat { bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::Concat {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                },
                                bit_width,
                            ))
                        }

                        graph::SignalData::Mux { bit_width, .. } => {
                            let cond = results.pop().unwrap();
                            let when_true = results.pop().unwrap();
                            let when_false = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::Ternary {
                                    cond: Box::new(cond),
                                    when_true: Box::new(when_true),
                                    when_false: Box::new(when_false),
                                },
                                bit_width,
                            ))
                        }

                        graph::SignalData::InstanceOutput { .. } => unreachable!(),

                        graph::SignalData::MemReadPortOutput { .. } => unreachable!(),
                    }
                }
            } {
                self.signal_exprs.insert(signal, expr.clone());
                results.push(expr);
            }
        }

        results.pop().unwrap()
    }
}
<commit_msg>Fix incorrect bits handling for verilog gen<commit_after>use super::ir::*;
use super::module_decls::*;

use crate::graph;

use std::collections::HashMap;

pub struct Compiler<'graph> {
    signal_exprs: HashMap<&'graph graph::Signal<'graph>, Expr>,
}

impl<'graph> Compiler<'graph> {
    pub fn new() -> Compiler<'graph> {
        Compiler {
            signal_exprs: HashMap::new(),
        }
    }

    pub fn compile_signal(
        &mut self,
        signal: &'graph graph::Signal<'graph>,
        module_decls: &ModuleDecls<'graph>,
        a: &mut AssignmentContext,
    ) -> Expr {
        enum Frame<'graph> {
            Enter(&'graph graph::Signal<'graph>),
            Leave(&'graph graph::Signal<'graph>),
        }

        let mut frames = Vec::new();
        frames.push(Frame::Enter(signal));

        let mut results = Vec::new();

        while let Some(frame) = frames.pop() {
            if let Some(expr) = match frame {
                Frame::Enter(signal) => {
                    if let Some(expr) = self.signal_exprs.get(&signal) {
                        results.push(expr.clone());
                        continue;
                    }

                    match signal.data {
                        graph::SignalData::Lit {
                            ref value,
                            bit_width,
                        } => Some(Expr::from_constant(value, bit_width)),

                        graph::SignalData::Input { ref name, .. } => {
                            Some(Expr::Ref { name: name.clone() })
                        }

                        graph::SignalData::Reg { .. } => Some(Expr::Ref {
                            name: module_decls.regs[&signal].value_name.clone(),
                        }),

                        graph::SignalData::UnOp { source, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(source));
                            None
                        }
                        graph::SignalData::SimpleBinOp { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }
                        graph::SignalData::AdditiveBinOp { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }
                        graph::SignalData::ComparisonBinOp { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }
                        graph::SignalData::ShiftBinOp { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }

                        graph::SignalData::Mul { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }
                        graph::SignalData::MulSigned { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }

                        graph::SignalData::Bits { source, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(source));
                            None
                        }

                        graph::SignalData::Repeat { source, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(source));
                            None
                        }
                        graph::SignalData::Concat { lhs, rhs, .. } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(lhs));
                            frames.push(Frame::Enter(rhs));
                            None
                        }

                        graph::SignalData::Mux {
                            cond,
                            when_true,
                            when_false,
                            ..
                        } => {
                            frames.push(Frame::Leave(signal));
                            frames.push(Frame::Enter(cond));
                            frames.push(Frame::Enter(when_true));
                            frames.push(Frame::Enter(when_false));
                            None
                        }

                        graph::SignalData::InstanceOutput {
                            instance, ref name, ..
                        } => {
                            let instance_decls = &module_decls.instances[&instance];
                            Some(Expr::Ref {
                                name: instance_decls.output_names[name].clone(),
                            })
                        }

                        graph::SignalData::MemReadPortOutput {
                            mem,
                            address,
                            enable,
                        } => {
                            let mem = &module_decls.mems[&mem];
                            let read_signal_names = &mem.read_signal_names[&(address, enable)];
                            Some(Expr::Ref {
                                name: read_signal_names.value_name.clone(),
                            })
                        }
                    }
                }
                Frame::Leave(signal) => {
                    match signal.data {
                        graph::SignalData::Lit { .. } => unreachable!(),

                        graph::SignalData::Input { .. } => unreachable!(),

                        graph::SignalData::Reg { .. } => unreachable!(),

                        graph::SignalData::UnOp { op, bit_width, .. } => {
                            let source = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::UnOp {
                                    source: Box::new(source),
                                    op: match op {
                                        graph::UnOp::Not => UnOp::Not,
                                    },
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::SimpleBinOp { op, bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: match op {
                                        graph::SimpleBinOp::BitAnd => BinOp::BitAnd,
                                        graph::SimpleBinOp::BitOr => BinOp::BitOr,
                                        graph::SimpleBinOp::BitXor => BinOp::BitXor,
                                    },
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::AdditiveBinOp { op, bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: match op {
                                        graph::AdditiveBinOp::Add => BinOp::Add,
                                        graph::AdditiveBinOp::Sub => BinOp::Sub,
                                    },
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::ComparisonBinOp { op, .. } => {
                            let bit_width = signal.bit_width();
                            let mut lhs = results.pop().unwrap();
                            let mut rhs = results.pop().unwrap();
                            match op {
                                graph::ComparisonBinOp::GreaterThanEqualSigned
                                | graph::ComparisonBinOp::GreaterThanSigned
                                | graph::ComparisonBinOp::LessThanEqualSigned
                                | graph::ComparisonBinOp::LessThanSigned => {
                                    lhs = Expr::Signed {
                                        source: Box::new(lhs),
                                    };
                                    rhs = Expr::Signed {
                                        source: Box::new(rhs),
                                    };
                                }
                                _ => (),
                            }
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: match op {
                                        graph::ComparisonBinOp::Equal => BinOp::Equal,
                                        graph::ComparisonBinOp::NotEqual => BinOp::NotEqual,
                                        graph::ComparisonBinOp::LessThan
                                        | graph::ComparisonBinOp::LessThanSigned => BinOp::LessThan,
                                        graph::ComparisonBinOp::LessThanEqual
                                        | graph::ComparisonBinOp::LessThanEqualSigned => {
                                            BinOp::LessThanEqual
                                        }
                                        graph::ComparisonBinOp::GreaterThan
                                        | graph::ComparisonBinOp::GreaterThanSigned => {
                                            BinOp::GreaterThan
                                        }
                                        graph::ComparisonBinOp::GreaterThanEqual
                                        | graph::ComparisonBinOp::GreaterThanEqualSigned => {
                                            BinOp::GreaterThanEqual
                                        }
                                    },
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::ShiftBinOp { op, bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: match op {
                                        graph::ShiftBinOp::Shl => BinOp::Shl,
                                        graph::ShiftBinOp::Shr => BinOp::Shr,
                                        graph::ShiftBinOp::ShrArithmetic => BinOp::ShrArithmetic,
                                    },
                                },
                                bit_width,
                            ))
                        }

                        graph::SignalData::Mul { bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: BinOp::Mul,
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::MulSigned { bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            let lhs = Expr::Signed {
                                source: Box::new(lhs),
                            };
                            let rhs = Expr::Signed {
                                source: Box::new(rhs),
                            };
                            Some(a.gen_temp(
                                Expr::BinOp {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                    op: BinOp::Mul,
                                },
                                bit_width,
                            ))
                        }

                        graph::SignalData::Bits {
                            source,
                            range_high,
                            range_low,
                        } => {
                            let source_bit_width = source.bit_width();
                            let bit_width = signal.bit_width();
                            let source = results.pop().unwrap();
                            // Verilog doesn't allow indexing scalars
                            Some(if source_bit_width == 1 {
                                source
                            } else {
                                a.gen_temp(
                                    Expr::Bits {
                                        source: Box::new(source),
                                        range_high,
                                        range_low,
                                    },
                                    bit_width,
                                )
                            })
                        }

                        graph::SignalData::Repeat {
                            count, bit_width, ..
                        } => {
                            let source = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::Repeat {
                                    source: Box::new(source),
                                    count,
                                },
                                bit_width,
                            ))
                        }
                        graph::SignalData::Concat { bit_width, .. } => {
                            let lhs = results.pop().unwrap();
                            let rhs = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::Concat {
                                    lhs: Box::new(lhs),
                                    rhs: Box::new(rhs),
                                },
                                bit_width,
                            ))
                        }

                        graph::SignalData::Mux { bit_width, .. } => {
                            let cond = results.pop().unwrap();
                            let when_true = results.pop().unwrap();
                            let when_false = results.pop().unwrap();
                            Some(a.gen_temp(
                                Expr::Ternary {
                                    cond: Box::new(cond),
                                    when_true: Box::new(when_true),
                                    when_false: Box::new(when_false),
                                },
                                bit_width,
                            ))
                        }

                        graph::SignalData::InstanceOutput { .. } => unreachable!(),

                        graph::SignalData::MemReadPortOutput { .. } => unreachable!(),
                    }
                }
            } {
                self.signal_exprs.insert(signal, expr.clone());
                results.push(expr);
            }
        }

        results.pop().unwrap()
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! The Rust parser and macro expander.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![crate_type = "dylib"]
#![crate_type = "rlib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/nightly/")]

#![feature(rustc_private)]

extern crate arena;
extern crate fmt_macros;
extern crate term;
extern crate libc;
#[macro_use] extern crate log;
#[macro_use] #[no_link] extern crate bitflags as rustc_bitflags;

extern crate rustc_serialize; // used by deriving
extern crate rustc_serialize as serialize;
extern crate unicode_xid;

// A variant of 'try!' that panics on Err(FatalError). This is used as a
// crutch on the way towards a non-panic!-prone parser. It should be used
// for fatal parsing errors; eventually we plan to convert all code using
// panictry to just use normal try
macro_rules! panictry {
    ($e:expr) => ({
        use std::result::Result::{Ok, Err};
        use diagnostic::FatalError;
        match $e {
            Ok(e) => e,
            Err(FatalError) => panic!(FatalError)
        }
    })
}

pub mod util {
    pub mod interner;
    #[cfg(test)]
    pub mod parser_testing;
    pub mod small_vector;
}

pub mod diagnostics {
    pub mod macros;
    pub mod plugin;
    pub mod registry;
}

pub mod syntax {
    pub use ext;
    pub use parse;
    pub use ast;
}

pub mod abi;
pub mod ast;
pub mod ast_util;
pub mod attr;
pub mod codemap;
pub mod config;
pub mod diagnostic;
pub mod feature_gate;
pub mod fold;
pub mod owned_slice;
pub mod parse;
pub mod ptr;
pub mod show_span;
pub mod std_inject;
pub mod str;
pub mod test;
pub mod visit;

pub mod print {
    pub mod pp;
    pub mod pprust;
}

pub mod ext {
    pub mod asm;
    pub mod base;
    pub mod build;
    pub mod cfg;
    pub mod concat;
    pub mod concat_idents;
    pub mod deriving;
    pub mod env;
    pub mod expand;
    pub mod format;
    pub mod log_syntax;
    pub mod mtwt;
    pub mod quote;
    pub mod source_util;
    pub mod trace_macros;

    pub mod tt {
        pub mod transcribe;
        pub mod macro_parser;
        pub mod macro_rules;
    }
}
<commit_msg>Remove arena<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! The Rust parser and macro expander.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![crate_type = "dylib"]
#![crate_type = "rlib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/nightly/")]

#![feature(rustc_private)]

extern crate fmt_macros;
extern crate term;
extern crate libc;
#[macro_use] extern crate log;
#[macro_use] #[no_link] extern crate bitflags as rustc_bitflags;

extern crate rustc_serialize; // used by deriving
extern crate rustc_serialize as serialize;
extern crate unicode_xid;

// A variant of 'try!' that panics on Err(FatalError). This is used as a
// crutch on the way towards a non-panic!-prone parser. It should be used
// for fatal parsing errors; eventually we plan to convert all code using
// panictry to just use normal try
macro_rules! panictry {
    ($e:expr) => ({
        use std::result::Result::{Ok, Err};
        use diagnostic::FatalError;
        match $e {
            Ok(e) => e,
            Err(FatalError) => panic!(FatalError)
        }
    })
}

pub mod util {
    pub mod interner;
    #[cfg(test)]
    pub mod parser_testing;
    pub mod small_vector;
}

pub mod diagnostics {
    pub mod macros;
    pub mod plugin;
    pub mod registry;
}

pub mod syntax {
    pub use ext;
    pub use parse;
    pub use ast;
}

pub mod abi;
pub mod ast;
pub mod ast_util;
pub mod attr;
pub mod codemap;
pub mod config;
pub mod diagnostic;
pub mod feature_gate;
pub mod fold;
pub mod owned_slice;
pub mod parse;
pub mod ptr;
pub mod show_span;
pub mod std_inject;
pub mod str;
pub mod test;
pub mod visit;

pub mod print {
    pub mod pp;
    pub mod pprust;
}

pub mod ext {
    pub mod asm;
    pub mod base;
    pub mod build;
    pub mod cfg;
    pub mod concat;
    pub mod concat_idents;
    pub mod deriving;
    pub mod env;
    pub mod expand;
    pub mod format;
    pub mod log_syntax;
    pub mod mtwt;
    pub mod quote;
    pub mod source_util;
    pub mod trace_macros;

    pub mod tt {
        pub mod transcribe;
        pub mod macro_parser;
        pub mod macro_rules;
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use imgui_sys::{self, ImVec4};

use model::{Color, FieldCoord, HexCoord};
use vec2::Vec2;

const SQRT_3: f32 = 1.732_050_807_568_877_f32;

// #f3e4cf
const FIELD_WHITE: [f32; 4] = [0.9529, 0.8941, 0.8118, 1.0];
// #998578
const FIELD_BLACK: [f32; 4] = [0.6, 0.5216, 0.4706, 1.0];
// #ffffff
const PIECE_WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
// #000000
const PIECE_OUTLINE: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
// #757575
const PIECE_BLACK: [f32; 4] = [0.4588, 0.4588, 0.4588, 1.0];

macro_rules! im_color {
    ($v:expr) => (imgui_sys::igColorConvertFloat4ToU32(ImVec4::from($v)))
}

pub fn draw_hex(coord: &HexCoord, origin: Vec2, size: f32) {
    for i in 0..6 {
        draw_field(&coord.to_field(i), origin, size);
    }
}

fn draw_field(coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let color = match coord.color() {
            Color::White => im_color!(FIELD_WHITE),
            Color::Black => im_color!(FIELD_BLACK),
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
    }
}

pub fn highlight_field(color: [f32; 4], coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let highlight = im_color!(color);

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(
            draw_list,
            v1.into(),
            v2.into(),
            v3.into(),
            highlight,
        );
    }
}

pub fn highlight_field_dot(color: [f32; 4], coord: &FieldCoord, origin: Vec2, size: f32) {
    let center = field_center(coord, origin, size);
    unsafe {
        let highlight = im_color!(color);

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddCircleFilled(
            draw_list,
            center.into(),
            size / (4.0 * SQRT_3),
            highlight,
            15,
        );
    }
}

pub fn draw_piece(coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center = field_center(coord, origin, size);

    const SCALE: f32 = 0.7;

    let v1 = center + (v1 - center) * SCALE;
    let v2 = center + (v2 - center) * SCALE;
    let v3 = center + (v3 - center) * SCALE;

    // Linear equation derived by human testing and regression
    let outline_size = 0.032 * size - 0.335;

    unsafe {
        let color = match coord.color() {
            Color::White => im_color!(PIECE_WHITE),
            Color::Black => im_color!(PIECE_BLACK),
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
        imgui_sys::ImDrawList_AddTriangle(
            draw_list,
            v1.into(),
            v2.into(),
            v3.into(),
            im_color!(PIECE_OUTLINE),
            outline_size,
        );
    }
}

fn field_center(coord: &FieldCoord, origin: Vec2, size: f32) -> Vec2 {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center_x = (v1.x + v2.x + v3.x) / 3.0;
    let min_y = (v1.y).min(v2.y).min(v3.y);
    let center_y = match coord.color() {
        Color::White => min_y + size / SQRT_3,
        Color::Black => min_y + size / (2.0 * SQRT_3),
    };

    Vec2::new(center_x, center_y)
}

fn field_vertexes(coord: &FieldCoord, origin: Vec2, size: f32) -> (Vec2, Vec2, Vec2) {
    let center = hex_to_pixel(&coord.to_hex(), origin, size);
    let height = size * SQRT_3 / 2.0;

    let west = Vec2::new(-size, 0.0);
    let east = Vec2::new(size, 0.0);

    let northwest = Vec2::new(-size / 2.0, -height);
    let southwest = Vec2::new(-size / 2.0, height);
    let northeast = Vec2::new(size / 2.0, -height);
    let southeast = Vec2::new(size / 2.0, height);

    let v1;
    let v2;
    match coord.f() {
        0 => {
            v1 = center + northwest;
            v2 = center + northeast;
        }
        1 => {
            v1 = center + northeast;
            v2 = center + east;
        }
        2 => {
            v1 = center + east;
            v2 = center + southeast;
        }
        3 => {
            v1 = center + southeast;
            v2 = center + southwest;
        }
        4 => {
            v1 = center + southwest;
            v2 = center + west;
        }
        5 => {
            v1 = center + west;
            v2 = center + northwest;
        }
        _ => unreachable!(),
    };

    (center, v1, v2)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#hex-to-pixel
fn hex_to_pixel(coord: &HexCoord, origin: Vec2, size: f32) -> Vec2 {
    let x = f32::from(coord.x());
    let y = f32::from(coord.y());

    let p = Vec2::new(size * (3.0 / 2.0) * x, size * -SQRT_3 * (x / 2.0 + y));

    origin + p
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#pixel-to-hex
pub fn pixel_to_field(p: Vec2, origin: Vec2, size: f32) -> Option<FieldCoord> {
    let v = p - origin;

    let q = v.x * (2.0 / 3.0) / size;
    let r = (-v.x - SQRT_3 * v.y) / (size * 3.0);

    if let Some(hex) = round_hex_coord(q, r) {
        /*
           To find the field, we subtract the converted hex coordinates (q, r) from the rounded hex
           coordinates (hex) to get the fractional part of the coordinates. Here is a diagram of a
           single hex, with the fractional coordinates of each of its vertexes in the hex coordinate
           system:

                (-1/3, 2/3) _______ (1/3, 1/3)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
            (-2/3, 1/3) (----(0,0)----) (2/3, -1/3)
                         \    / \    /
                          \  /   \  /
              (-1/3, -1/3) \/_____\/ (1/3, -2/3)

           Suppose our converted coordinates were (q, r) = (1.333, 0.583). Our rounded coordinates
           are hex = (1, 1), and so our fractional coordinates are (0.333, -0.417).

           We now define three linear equations that will split this hexagon into its six fields:

                            _______ y = x (/)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
                        (------X------) y = -x/2 (-)
                         \    / \    /
                          \  /   \  /
                           \/_____\/ y = -2x (\)

           Using these three equations as linear inequalities, we can check any pair of fractional
           coordinates and find which field it is in. (As a reminder, fields are numbered clockwise
           starting from 0 at the top.)

           For our example (0.333, -0.417), we see that:
               y + x/2 >= 0, so our field is above (-), and is either 5, 0, or 1.
               y +  2x >= 0, so our field is to the right of (\), and is either 0 or 1.
               y -   x <  0, so our field is to the right of (/), and is 1.
        */
        let x_diff = q - f32::from(hex.x());
        let y_diff = r - f32::from(hex.y());
        let mut i = 0;

        if y_diff + x_diff / 2.0 >= 0.0 {
            i |= 0b100;
        }
        if y_diff + x_diff * 2.0 >= 0.0 {
            i |= 0b010;
        }
        if y_diff - x_diff >= 0.0 {
            i |= 0b001;
        }

        Some(hex.to_field(match i {
            0 => 3,
            1 => 4,
            2 => 2,
            5 => 5,
            6 => 1,
            7 => 0,
            _ => unreachable!(),
        }))
    } else {
        None
    }
}

// Algorithm from http://www.redblobgames.com/grids/hexagons/#rounding
fn round_hex_coord(x: f32, y: f32) -> Option<HexCoord> {
    let z = -x - y;

    let mut rx = x.round();
    let mut ry = y.round();
    let rz = z.round();

    let x_diff = (rx - x).abs();
    let y_diff = (ry - y).abs();
    let z_diff = (rz - z).abs();

    if x_diff > y_diff && x_diff > z_diff {
        rx = -ry - rz;
    } else if y_diff > z_diff {
        ry = -rx - rz;
    }

    let rx = rx as i8;
    let ry = ry as i8;

    HexCoord::try_new(rx, ry)
}
<commit_msg>Replace if let with Option::and_then<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use imgui_sys::{self, ImVec4};

use model::{Color, FieldCoord, HexCoord};
use vec2::Vec2;

const SQRT_3: f32 = 1.732_050_807_568_877_f32;

// #f3e4cf
const FIELD_WHITE: [f32; 4] = [0.9529, 0.8941, 0.8118, 1.0];
// #998578
const FIELD_BLACK: [f32; 4] = [0.6, 0.5216, 0.4706, 1.0];
// #ffffff
const PIECE_WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
// #000000
const PIECE_OUTLINE: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
// #757575
const PIECE_BLACK: [f32; 4] = [0.4588, 0.4588, 0.4588, 1.0];

macro_rules! im_color {
    ($v:expr) => (imgui_sys::igColorConvertFloat4ToU32(ImVec4::from($v)))
}

pub fn draw_hex(coord: &HexCoord, origin: Vec2, size: f32) {
    for i in 0..6 {
        draw_field(&coord.to_field(i), origin, size);
    }
}

fn draw_field(coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let color = match coord.color() {
            Color::White => im_color!(FIELD_WHITE),
            Color::Black => im_color!(FIELD_BLACK),
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
    }
}

pub fn highlight_field(color: [f32; 4], coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let highlight = im_color!(color);

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(
            draw_list,
            v1.into(),
            v2.into(),
            v3.into(),
            highlight,
        );
    }
}

pub fn highlight_field_dot(color: [f32; 4], coord: &FieldCoord, origin: Vec2, size: f32) {
    let center = field_center(coord, origin, size);
    unsafe {
        let highlight = im_color!(color);

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddCircleFilled(
            draw_list,
            center.into(),
            size / (4.0 * SQRT_3),
            highlight,
            15,
        );
    }
}

pub fn draw_piece(coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center = field_center(coord, origin, size);

    const SCALE: f32 = 0.7;

    let v1 = center + (v1 - center) * SCALE;
    let v2 = center + (v2 - center) * SCALE;
    let v3 = center + (v3 - center) * SCALE;

    // Linear equation derived by human testing and regression
    let outline_size = 0.032 * size - 0.335;

    unsafe {
        let color = match coord.color() {
            Color::White => im_color!(PIECE_WHITE),
            Color::Black => im_color!(PIECE_BLACK),
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
        imgui_sys::ImDrawList_AddTriangle(
            draw_list,
            v1.into(),
            v2.into(),
            v3.into(),
            im_color!(PIECE_OUTLINE),
            outline_size,
        );
    }
}

fn field_center(coord: &FieldCoord, origin: Vec2, size: f32) -> Vec2 {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center_x = (v1.x + v2.x + v3.x) / 3.0;
    let min_y = (v1.y).min(v2.y).min(v3.y);
    let center_y = match coord.color() {
        Color::White => min_y + size / SQRT_3,
        Color::Black => min_y + size / (2.0 * SQRT_3),
    };

    Vec2::new(center_x, center_y)
}

fn field_vertexes(coord: &FieldCoord, origin: Vec2, size: f32) -> (Vec2, Vec2, Vec2) {
    let center = hex_to_pixel(&coord.to_hex(), origin, size);
    let height = size * SQRT_3 / 2.0;

    let west = Vec2::new(-size, 0.0);
    let east = Vec2::new(size, 0.0);

    let northwest = Vec2::new(-size / 2.0, -height);
    let southwest = Vec2::new(-size / 2.0, height);
    let northeast = Vec2::new(size / 2.0, -height);
    let southeast = Vec2::new(size / 2.0, height);

    let v1;
    let v2;
    match coord.f() {
        0 => {
            v1 = center + northwest;
            v2 = center + northeast;
        }
        1 => {
            v1 = center + northeast;
            v2 = center + east;
        }
        2 => {
            v1 = center + east;
            v2 = center + southeast;
        }
        3 => {
            v1 = center + southeast;
            v2 = center + southwest;
        }
        4 => {
            v1 = center + southwest;
            v2 = center + west;
        }
        5 => {
            v1 = center + west;
            v2 = center + northwest;
        }
        _ => unreachable!(),
    };

    (center, v1, v2)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#hex-to-pixel
fn hex_to_pixel(coord: &HexCoord, origin: Vec2, size: f32) -> Vec2 {
    let x = f32::from(coord.x());
    let y = f32::from(coord.y());

    let p = Vec2::new(size * (3.0 / 2.0) * x, size * -SQRT_3 * (x / 2.0 + y));

    origin + p
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#pixel-to-hex
pub fn pixel_to_field(p: Vec2, origin: Vec2, size: f32) -> Option<FieldCoord> {
    let v = p - origin;

    let q = v.x * (2.0 / 3.0) / size;
    let r = (-v.x - SQRT_3 * v.y) / (size * 3.0);

    round_hex_coord(q, r).and_then(|hex| {
        /*
           To find the field, we subtract the converted hex coordinates (q, r) from the rounded hex
           coordinates (hex) to get the fractional part of the coordinates. Here is a diagram of a
           single hex, with the fractional coordinates of each of its vertexes in the hex coordinate
           system:

                (-1/3, 2/3) _______ (1/3, 1/3)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
            (-2/3, 1/3) (----(0,0)----) (2/3, -1/3)
                         \    / \    /
                          \  /   \  /
              (-1/3, -1/3) \/_____\/ (1/3, -2/3)

           Suppose our converted coordinates were (q, r) = (1.333, 0.583). Our rounded coordinates
           are hex = (1, 1), and so our fractional coordinates are (0.333, -0.417).

           We now define three linear equations that will split this hexagon into its six fields:

                            _______ y = x (/)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
                        (------X------) y = -x/2 (-)
                         \    / \    /
                          \  /   \  /
                           \/_____\/ y = -2x (\)

           Using these three equations as linear inequalities, we can check any pair of fractional
           coordinates and find which field it is in. (As a reminder, fields are numbered clockwise
           starting from 0 at the top.)

           For our example (0.333, -0.417), we see that:
               y + x/2 >= 0, so our field is above (-), and is either 5, 0, or 1.
               y +  2x >= 0, so our field is to the right of (\), and is either 0 or 1.
               y -   x <  0, so our field is to the right of (/), and is 1.
        */
        let x_diff = q - f32::from(hex.x());
        let y_diff = r - f32::from(hex.y());
        let mut i = 0;

        if y_diff + x_diff / 2.0 >= 0.0 {
            i |= 0b100;
        }
        if y_diff + x_diff * 2.0 >= 0.0 {
            i |= 0b010;
        }
        if y_diff - x_diff >= 0.0 {
            i |= 0b001;
        }

        Some(hex.to_field(match i {
            0 => 3,
            1 => 4,
            2 => 2,
            5 => 5,
            6 => 1,
            7 => 0,
            _ => unreachable!(),
        }))
    })
}

// Algorithm from http://www.redblobgames.com/grids/hexagons/#rounding
fn round_hex_coord(x: f32, y: f32) -> Option<HexCoord> {
    let z = -x - y;

    let mut rx = x.round();
    let mut ry = y.round();
    let rz = z.round();

    let x_diff = (rx - x).abs();
    let y_diff = (ry - y).abs();
    let z_diff = (rz - z).abs();

    if x_diff > y_diff && x_diff > z_diff {
        rx = -ry - rz;
    } else if y_diff > z_diff {
        ry = -rx - rz;
    }

    let rx = rx as i8;
    let ry = ry as i8;

    HexCoord::try_new(rx, ry)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use parsing::parse;
use style::parser::Parse;
use style::values::specified::basic_shape::*;

// Ensure that basic-shape sub-functions parse as both basic shapes
// and their individual components
macro_rules! assert_roundtrip_basicshape {
    ($fun:expr, $input:expr, $output:expr) => {
        assert_roundtrip!($fun, $input, $output);
        assert_roundtrip!(BasicShape::parse, $input, $output);
    }
}

macro_rules! assert_border_radius_values {
    ($input:expr; $tlw:expr, $trw:expr, $brw:expr, $blw:expr ;
                  $tlh:expr, $trh:expr, $brh:expr, $blh:expr) => {
        let input = parse(BorderRadius::parse, $input)
                          .expect(&format!("Failed parsing {} as border radius",
                                  $input));
        assert_eq!(::cssparser::ToCss::to_css_string(&input.top_left.0.width), $tlw);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.top_right.0.width), $trw);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.bottom_right.0.width), $brw);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.bottom_left.0.width), $blw);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.top_left.0.height), $tlh);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.top_right.0.height), $trh);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.bottom_right.0.height), $brh);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.bottom_left.0.height), $blh);
    }
}

#[test]
fn test_inset() {
    // these are actually wrong, we should be serializing to the minimum possible result
    // the advantage of being wrong is that the roundtrip test actually suffices
    // for testing the intermediate state
    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px)", "inset(10px 10px 10px 10px)");
    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px 20%)", "inset(10px 20% 10px 20%)");

    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px)",
                                                   "inset(10px 10px 10px 10px round 10px)");
    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px 20px 30px 40px)",
                                                   "inset(10px 10px 10px 10px round 10px 20px 30px 40px)");
    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px 10px 10px 10px round 10px 20px 30px 40px \
                                                    / 1px 2px 3px 4px)",
                                                   "inset(10px 10px 10px 10px round 10px 20px 30px 40px \
                                                    / 1px 2px 3px 4px)");
}

#[test]
fn test_border_radius() {
    assert_border_radius_values!("10px";
                                 "10px", "10px", "10px", "10px" ;
                                 "10px", "10px", "10px", "10px");
    assert_border_radius_values!("10px 20px";
                                 "10px", "20px", "10px", "20px" ;
                                 "10px", "20px", "10px", "20px");
    assert_border_radius_values!("10px 20px 30px";
                                 "10px", "20px", "30px", "20px" ;
                                 "10px", "20px", "30px", "20px");
    assert_border_radius_values!("10px 20px 30px 40px";
                                 "10px", "20px", "30px", "40px" ;
                                 "10px", "20px", "30px", "40px");
    assert_border_radius_values!("10% / 20px";
                                 "10%", "10%", "10%", "10%" ;
                                 "20px", "20px", "20px", "20px");
    assert_border_radius_values!("10px / 20px 30px";
                                 "10px", "10px", "10px", "10px" ;
                                 "20px", "30px", "20px", "30px");
    assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
                                 "10px", "20px", "30px", "40px" ;
                                 "1px", "2px", "3px", "4px");
}

#[test]
fn test_circle() {
    assert_roundtrip_basicshape!(Circle::parse, "circle(at center)", "circle(at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle()", "circle(at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at left bottom)", "circle(at 0% 100%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at bottom left)", "circle(at 0% 100%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at top left)", "circle(at 0% 0%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at center left)", "circle(at 0% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at left center)", "circle(at 0% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at top center)", "circle(at 50% 0%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at center top)", "circle(at 50% 0%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at 40% top)", "circle(at 40% 0%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at 10px 100px)", "circle(at 10px 100px)");
    // closest-side is omitted, because it is the default
    assert_roundtrip_basicshape!(Circle::parse, "circle(closest-side at center)", "circle(at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(farthest-side at center)",
                                                "circle(farthest-side at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(20px at center)", "circle(20px at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(calc(1px + 50%) at center)",
                                                "circle(calc(1px + 50%) at 50% 50%)");

    assert!(parse(Circle::parse, "circle(at top 40%)").is_err());
}

#[test]
fn test_ellipse() {
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(at center)", "ellipse(at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse()", "ellipse(at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(at left bottom)", "ellipse(at 0% 100%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(at bottom left)", "ellipse(at 0% 100%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(at 10px 100px)", "ellipse(at 10px 100px)");
    // closest-side is omitted, because it is the default
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(closest-side closest-side at center)",
                                                 "ellipse(at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(farthest-side closest-side at center)",
                                                 "ellipse(farthest-side closest-side at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(20px 10% at center)", "ellipse(20px 10% at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(calc(1px + 50%) 10px at center)",
                                                 "ellipse(calc(1px + 50%) 10px at 50% 50%)");
}

#[test]
fn test_polygon() {
    // surprisingly, polygons are only required to have at least one vertex,
    // not at least 3
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(10px 10px)", "polygon(10px 10px)");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(10px 10px, 10px 10px)", "polygon(10px 10px, 10px 10px)");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(nonzero, 10px 10px, 10px 10px)",
                                                 "polygon(10px 10px, 10px 10px)");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(evenodd, 10px 10px, 10px 10px)",
                                                 "polygon(evenodd, 10px 10px, 10px 10px)");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(evenodd, 10px 10px, 10px calc(10px + 50%))",
                                                 "polygon(evenodd, 10px 10px, 10px calc(10px + 50%))");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(evenodd, 10px 10px, 10px 10px, 10px 10px, 10px 10px, 10px \
                                                  10px, 10px 10px, 10px 10px, 10px 10px, 10px 10px, 10px 10px, \
                                                  10px 10px, 10px 10px, 10px 10px)",
                                                 "polygon(evenodd, 10px 10px, 10px 10px, 10px 10px, 10px 10px, 10px \
                                                  10px, 10px 10px, 10px 10px, 10px 10px, 10px 10px, 10px 10px, \
                                                  10px 10px, 10px 10px, 10px 10px)");

    assert!(parse(Polygon::parse, "polygon()").is_err());
}
<commit_msg>Tests for 3+ value positions in basic shapes<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use parsing::parse;
use style::parser::Parse;
use style::values::specified::basic_shape::*;

// Ensure that basic-shape sub-functions parse as both basic shapes
// and their individual components
macro_rules! assert_roundtrip_basicshape {
    ($fun:expr, $input:expr, $output:expr) => {
        assert_roundtrip!($fun, $input, $output);
        assert_roundtrip!(BasicShape::parse, $input, $output);
    }
}

macro_rules! assert_border_radius_values {
    ($input:expr; $tlw:expr, $trw:expr, $brw:expr, $blw:expr ;
                  $tlh:expr, $trh:expr, $brh:expr, $blh:expr) => {
        let input = parse(BorderRadius::parse, $input)
                          .expect(&format!("Failed parsing {} as border radius",
                                  $input));
        assert_eq!(::cssparser::ToCss::to_css_string(&input.top_left.0.width), $tlw);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.top_right.0.width), $trw);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.bottom_right.0.width), $brw);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.bottom_left.0.width), $blw);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.top_left.0.height), $tlh);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.top_right.0.height), $trh);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.bottom_right.0.height), $brh);
        assert_eq!(::cssparser::ToCss::to_css_string(&input.bottom_left.0.height), $blh);
    }
}

#[test]
fn test_inset() {
    // these are actually wrong, we should be serializing to the minimum possible result
    // the advantage of being wrong is that the roundtrip test actually suffices
    // for testing the intermediate state
    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px)", "inset(10px 10px 10px 10px)");
    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px 20%)", "inset(10px 20% 10px 20%)");

    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px)",
                                                   "inset(10px 10px 10px 10px round 10px)");
    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px round 10px 20px 30px 40px)",
                                                   "inset(10px 10px 10px 10px round 10px 20px 30px 40px)");
    assert_roundtrip_basicshape!(InsetRect::parse, "inset(10px 10px 10px 10px round 10px 20px 30px 40px \
                                                    / 1px 2px 3px 4px)",
                                                   "inset(10px 10px 10px 10px round 10px 20px 30px 40px \
                                                    / 1px 2px 3px 4px)");
}

#[test]
fn test_border_radius() {
    assert_border_radius_values!("10px";
                                 "10px", "10px", "10px", "10px" ;
                                 "10px", "10px", "10px", "10px");
    assert_border_radius_values!("10px 20px";
                                 "10px", "20px", "10px", "20px" ;
                                 "10px", "20px", "10px", "20px");
    assert_border_radius_values!("10px 20px 30px";
                                 "10px", "20px", "30px", "20px" ;
                                 "10px", "20px", "30px", "20px");
    assert_border_radius_values!("10px 20px 30px 40px";
                                 "10px", "20px", "30px", "40px" ;
                                 "10px", "20px", "30px", "40px");
    assert_border_radius_values!("10% / 20px";
                                 "10%", "10%", "10%", "10%" ;
                                 "20px", "20px", "20px", "20px");
    assert_border_radius_values!("10px / 20px 30px";
                                 "10px", "10px", "10px", "10px" ;
                                 "20px", "30px", "20px", "30px");
    assert_border_radius_values!("10px 20px 30px 40px / 1px 2px 3px 4px";
                                 "10px", "20px", "30px", "40px" ;
                                 "1px", "2px", "3px", "4px");
}

#[test]
fn test_circle() {
    assert_roundtrip_basicshape!(Circle::parse, "circle(at center)", "circle(at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle()", "circle(at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at left bottom)", "circle(at 0% 100%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at bottom left)", "circle(at 0% 100%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at top left)", "circle(at 0% 0%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at center left)", "circle(at 0% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at left center)", "circle(at 0% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at top center)", "circle(at 50% 0%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at center top)", "circle(at 50% 0%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at 40% top)", "circle(at 40% 0%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at 10px 100px)", "circle(at 10px 100px)");
    // closest-side is omitted, because it is the default
    assert_roundtrip_basicshape!(Circle::parse, "circle(closest-side at center)", "circle(at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(farthest-side at center)",
                                                "circle(farthest-side at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(20px at center)", "circle(20px at 50% 50%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(calc(1px + 50%) at center)",
                                                "circle(calc(1px + 50%) at 50% 50%)");

    assert_roundtrip_basicshape!(Circle::parse, "circle(at right 5px bottom 10px)",
                                                "circle(at right 5px bottom 10px)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at bottom 5px right 10px)",
                                                "circle(at right 10px bottom 5px)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at right 5% top 0px)",
                                                "circle(at 95% 0%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at right 5% bottom 0px)",
                                                "circle(at 95% 100%)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at right 5% bottom 1px)",
                                                "circle(at right 5% bottom 1px)");
    assert_roundtrip_basicshape!(Circle::parse, "circle(at 5% bottom 1px)",
                                                "circle(at left 5% bottom 1px)");

    assert!(parse(Circle::parse, "circle(at top 40%)").is_err());
}

#[test]
fn test_ellipse() {
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(at center)", "ellipse(at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse()", "ellipse(at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(at left bottom)", "ellipse(at 0% 100%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(at bottom left)", "ellipse(at 0% 100%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(at 10px 100px)", "ellipse(at 10px 100px)");
    // closest-side is omitted, because it is the default
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(closest-side closest-side at center)",
                                                 "ellipse(at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(farthest-side closest-side at center)",
                                                 "ellipse(farthest-side closest-side at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(20px 10% at center)", "ellipse(20px 10% at 50% 50%)");
    assert_roundtrip_basicshape!(Ellipse::parse, "ellipse(calc(1px + 50%) 10px at center)",
                                                 "ellipse(calc(1px + 50%) 10px at 50% 50%)");
}

#[test]
fn test_polygon() {
    // surprisingly, polygons are only required to have at least one vertex,
    // not at least 3
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(10px 10px)", "polygon(10px 10px)");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(10px 10px, 10px 10px)", "polygon(10px 10px, 10px 10px)");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(nonzero, 10px 10px, 10px 10px)",
                                                 "polygon(10px 10px, 10px 10px)");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(evenodd, 10px 10px, 10px 10px)",
                                                 "polygon(evenodd, 10px 10px, 10px 10px)");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(evenodd, 10px 10px, 10px calc(10px + 50%))",
                                                 "polygon(evenodd, 10px 10px, 10px calc(10px + 50%))");
    assert_roundtrip_basicshape!(Polygon::parse, "polygon(evenodd, 10px 10px, 10px 10px, 10px 10px, 10px 10px, 10px \
                                                  10px, 10px 10px, 10px 10px, 10px 10px, 10px 10px, 10px 10px, \
                                                  10px 10px, 10px 10px, 10px 10px)",
                                                 "polygon(evenodd, 10px 10px, 10px 10px, 10px 10px, 10px 10px, 10px \
                                                  10px, 10px 10px, 10px 10px, 10px 10px, 10px 10px, 10px 10px, \
                                                  10px 10px, 10px 10px, 10px 10px)");

    assert!(parse(Polygon::parse, "polygon()").is_err());
}
<|endoftext|>