<commit_before>//! FIXME: write short doc here

use hir::{AssocItem, FieldSource, HasSource, ModuleSource};
use ra_db::{FileId, SourceDatabase};
use ra_syntax::{
    ast::{self, DocCommentsOwner},
    match_ast, AstNode, AstPtr, SmolStr,
    SyntaxKind::{self, NAME},
    SyntaxNode, TextRange,
};

use super::short_label::ShortLabel;
use crate::{db::RootDatabase, FileSymbol};

/// `NavigationTarget` represents and element in the editor's UI which you can
/// click on to navigate to a particular piece of code.
///
/// Typically, a `NavigationTarget` corresponds to some element in the source
/// code, like a function or a struct, but this is not strictly required.
#[derive(Debug, Clone)]
pub struct NavigationTarget {
    file_id: FileId,
    name: SmolStr,
    kind: SyntaxKind,
    full_range: TextRange,
    focus_range: Option<TextRange>,
    container_name: Option<SmolStr>,
    description: Option<String>,
    docs: Option<String>,
}

fn find_range_from_node(
    db: &RootDatabase,
    src: hir::HirFileId,
    node: &SyntaxNode,
) -> (FileId, TextRange) {
    let text_range = node.text_range();
    let (file_id, text_range) = src
        .parent_expansion(db)
        .and_then(|(files, expansion_info)| expansion_info.find_range(text_range, files))
        .unwrap_or((src, text_range));

    (file_id.original_file(db), text_range)
}

impl NavigationTarget {
    /// When `focus_range` is specified, returns it. otherwise
    /// returns `full_range`
    pub fn range(&self) -> TextRange {
        self.focus_range.unwrap_or(self.full_range)
    }

    pub fn name(&self) -> &SmolStr {
        &self.name
    }

    pub fn container_name(&self) -> Option<&SmolStr> {
        self.container_name.as_ref()
    }

    pub fn kind(&self) -> SyntaxKind {
        self.kind
    }

    pub fn file_id(&self) -> FileId {
        self.file_id
    }

    pub fn full_range(&self) -> TextRange {
        self.full_range
    }

    pub fn docs(&self) -> Option<&str> {
        self.docs.as_ref().map(String::as_str)
    }

    pub fn description(&self) -> Option<&str> {
        self.description.as_ref().map(String::as_str)
    }

    /// A "most interesting" range withing the `full_range`.
    ///
    /// Typically, `full_range` is the whole syntax node,
    /// including doc comments, and `focus_range` is the range of the identifier.
    pub fn focus_range(&self) -> Option<TextRange> {
        self.focus_range
    }

    pub(crate) fn from_bind_pat(
        db: &RootDatabase,
        file_id: FileId,
        pat: &ast::BindPat,
    ) -> NavigationTarget {
        NavigationTarget::from_named(db, file_id.into(), pat, None, None)
    }

    pub(crate) fn from_symbol(db: &RootDatabase, symbol: FileSymbol) -> NavigationTarget {
        NavigationTarget {
            file_id: symbol.file_id,
            name: symbol.name.clone(),
            kind: symbol.ptr.kind(),
            full_range: symbol.ptr.range(),
            focus_range: symbol.name_range,
            container_name: symbol.container_name.clone(),
            description: description_from_symbol(db, &symbol),
            docs: docs_from_symbol(db, &symbol),
        }
    }

    pub(crate) fn from_pat(
        db: &RootDatabase,
        file_id: FileId,
        pat: AstPtr<ast::BindPat>,
    ) -> NavigationTarget {
        let parse = db.parse(file_id);
        let pat = pat.to_node(parse.tree().syntax());
        NavigationTarget::from_bind_pat(db, file_id, &pat)
    }

    pub(crate) fn from_self_param(
        file_id: FileId,
        par: AstPtr<ast::SelfParam>,
    ) -> NavigationTarget {
        let (name, full_range) = ("self".into(), par.syntax_node_ptr().range());

        NavigationTarget {
            file_id,
            name,
            full_range,
            focus_range: None,
            kind: NAME,
            container_name: None,
            description: None, //< No document node for SelfParam
            docs: None,        //< No document node for SelfParam
        }
    }

    pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
        let src = module.definition_source(db);
        let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();

        match src.ast {
            ModuleSource::SourceFile(node) => {
                let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());

                NavigationTarget::from_syntax(
                    file_id,
                    name,
                    None,
                    text_range,
                    node.syntax(),
                    None,
                    None,
                )
            }
            ModuleSource::Module(node) => {
                let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());

                NavigationTarget::from_syntax(
                    file_id,
                    name,
                    None,
                    text_range,
                    node.syntax(),
                    node.doc_comment_text(),
                    node.short_label(),
                )
            }
        }
    }

    pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
        let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
        if let Some(src) = module.declaration_source(db) {
            let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
            return NavigationTarget::from_syntax(
                file_id,
                name,
                None,
                text_range,
                src.ast.syntax(),
                src.ast.doc_comment_text(),
                src.ast.short_label(),
            );
        }
        NavigationTarget::from_module(db, module)
    }

    pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget {
        let src = field.source(db);
        match src.ast {
            FieldSource::Named(it) => NavigationTarget::from_named(
                db,
                src.file_id,
                &it,
                it.doc_comment_text(),
                it.short_label(),
            ),
            FieldSource::Pos(it) => {
                let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax());
                NavigationTarget::from_syntax(
                    file_id,
                    "".into(),
                    None,
                    text_range,
                    it.syntax(),
                    None,
                    None,
                )
            }
        }
    }

    pub(crate) fn from_def_source<A, D>(db: &RootDatabase, def: D) -> NavigationTarget
    where
        D: HasSource<Ast = A>,
        A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
    {
        let src = def.source(db);
        NavigationTarget::from_named(
            db,
            src.file_id,
            &src.ast,
            src.ast.doc_comment_text(),
            src.ast.short_label(),
        )
    }

    pub(crate) fn from_adt_def(db: &RootDatabase, adt_def: hir::Adt) -> NavigationTarget {
        match adt_def {
            hir::Adt::Struct(it) => NavigationTarget::from_def_source(db, it),
            hir::Adt::Union(it) => NavigationTarget::from_def_source(db, it),
            hir::Adt::Enum(it) => NavigationTarget::from_def_source(db, it),
        }
    }

    pub(crate) fn from_def(
        db: &RootDatabase,
        module_def: hir::ModuleDef,
    ) -> Option<NavigationTarget> {
        let nav = match module_def {
            hir::ModuleDef::Module(module) => NavigationTarget::from_module(db, module),
            hir::ModuleDef::Function(func) => NavigationTarget::from_def_source(db, func),
            hir::ModuleDef::Adt(it) => NavigationTarget::from_adt_def(db, it),
            hir::ModuleDef::Const(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::Static(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::EnumVariant(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::Trait(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::TypeAlias(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::BuiltinType(..) => {
                return None;
            }
        };
        Some(nav)
    }

    pub(crate) fn from_impl_block(
        db: &RootDatabase,
        impl_block: hir::ImplBlock,
    ) -> NavigationTarget {
        let src = impl_block.source(db);
        let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());

        NavigationTarget::from_syntax(
            file_id,
            "impl".into(),
            None,
            text_range,
            src.ast.syntax(),
            None,
            None,
        )
    }

    pub(crate) fn from_assoc_item(
        db: &RootDatabase,
        assoc_item: hir::AssocItem,
    ) -> NavigationTarget {
        match assoc_item {
            AssocItem::Function(it) => NavigationTarget::from_def_source(db, it),
            AssocItem::Const(it) => NavigationTarget::from_def_source(db, it),
            AssocItem::TypeAlias(it) => NavigationTarget::from_def_source(db, it),
        }
    }

    pub(crate) fn from_macro_def(db: &RootDatabase, macro_call: hir::MacroDef) -> NavigationTarget {
        let src = macro_call.source(db);
        log::debug!("nav target {:#?}", src.ast.syntax());
        NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None)
    }

    #[cfg(test)]
    pub(crate) fn assert_match(&self, expected: &str) {
        let actual = self.debug_render();
        test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
    }

    #[cfg(test)]
    pub(crate) fn debug_render(&self) -> String {
        let mut buf = format!(
            "{} {:?} {:?} {:?}",
            self.name(),
            self.kind(),
            self.file_id(),
            self.full_range()
        );
        if let Some(focus_range) = self.focus_range() {
            buf.push_str(&format!(" {:?}", focus_range))
        }
        if let Some(container_name) = self.container_name() {
            buf.push_str(&format!(" {}", container_name))
        }
        buf
    }

    /// Allows `NavigationTarget` to be created from a `NameOwner`
    pub(crate) fn from_named(
        db: &RootDatabase,
        file_id: hir::HirFileId,
        node: &impl ast::NameOwner,
        docs: Option<String>,
        description: Option<String>,
    ) -> NavigationTarget {
        //FIXME: use `_` instead of empty string
        let name = node.name().map(|it| it.text().clone()).unwrap_or_default();

        let focus_range = node.name().map(|it| find_range_from_node(db, file_id, it.syntax()).1);

        let (file_id, full_range) = find_range_from_node(db, file_id, node.syntax());

        NavigationTarget::from_syntax(
            file_id,
            name,
            focus_range,
            full_range,
            node.syntax(),
            docs,
            description,
        )
    }

    fn from_syntax(
        file_id: FileId,
        name: SmolStr,
        focus_range: Option<TextRange>,
        full_range: TextRange,
        node: &SyntaxNode,
        docs: Option<String>,
        description: Option<String>,
    ) -> NavigationTarget {
        NavigationTarget {
            file_id,
            name,
            kind: node.kind(),
            full_range,
            focus_range,
            container_name: None,
            description,
            docs,
        }
    }
}

pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
    let parse = db.parse(symbol.file_id);
    let node = symbol.ptr.to_node(parse.tree().syntax());

    match_ast! {
        match node {
            ast::FnDef(it) => { it.doc_comment_text() },
            ast::StructDef(it) => { it.doc_comment_text() },
            ast::EnumDef(it) => { it.doc_comment_text() },
            ast::TraitDef(it) => { it.doc_comment_text() },
            ast::Module(it) => { it.doc_comment_text() },
            ast::TypeAliasDef(it) => { it.doc_comment_text() },
            ast::ConstDef(it) => { it.doc_comment_text() },
            ast::StaticDef(it) => { it.doc_comment_text() },
            ast::RecordFieldDef(it) => { it.doc_comment_text() },
            ast::EnumVariant(it) => { it.doc_comment_text() },
            ast::MacroCall(it) => { it.doc_comment_text() },
            _ => None,
        }
    }
}

/// Get a description of a symbol.
///
/// e.g. `struct Name`, `enum Name`, `fn Name`
pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
    let parse = db.parse(symbol.file_id);
    let node = symbol.ptr.to_node(parse.tree().syntax());

    match_ast! {
        match node {
            ast::FnDef(it) => { it.short_label() },
            ast::StructDef(it) => { it.short_label() },
            ast::EnumDef(it) => { it.short_label() },
            ast::TraitDef(it) => { it.short_label() },
            ast::Module(it) => { it.short_label() },
            ast::TypeAliasDef(it) => { it.short_label() },
            ast::ConstDef(it) => { it.short_label() },
            ast::StaticDef(it) => { it.short_label() },
            ast::RecordFieldDef(it) => { it.short_label() },
            ast::EnumVariant(it) => { it.short_label() },
            _ => None,
        }
    }
}
<commit_msg>Add note for recurseive macro generated code<commit_after>//! FIXME: write short doc here

use hir::{AssocItem, FieldSource, HasSource, ModuleSource};
use ra_db::{FileId, SourceDatabase};
use ra_syntax::{
    ast::{self, DocCommentsOwner},
    match_ast, AstNode, AstPtr, SmolStr,
    SyntaxKind::{self, NAME},
    SyntaxNode, TextRange,
};

use super::short_label::ShortLabel;
use crate::{db::RootDatabase, FileSymbol};

/// `NavigationTarget` represents and element in the editor's UI which you can
/// click on to navigate to a particular piece of code.
///
/// Typically, a `NavigationTarget` corresponds to some element in the source
/// code, like a function or a struct, but this is not strictly required.
#[derive(Debug, Clone)]
pub struct NavigationTarget {
    file_id: FileId,
    name: SmolStr,
    kind: SyntaxKind,
    full_range: TextRange,
    focus_range: Option<TextRange>,
    container_name: Option<SmolStr>,
    description: Option<String>,
    docs: Option<String>,
}

fn find_range_from_node(
    db: &RootDatabase,
    src: hir::HirFileId,
    node: &SyntaxNode,
) -> (FileId, TextRange) {
    let text_range = node.text_range();
    let (file_id, text_range) = src
        .parent_expansion(db)
        .and_then(|(files, expansion_info)| expansion_info.find_range(text_range, files))
        .unwrap_or((src, text_range));

    // FIXME: handle recursive macro generated macro
    (file_id.original_file(db), text_range)
}

impl NavigationTarget {
    /// When `focus_range` is specified, returns it. otherwise
    /// returns `full_range`
    pub fn range(&self) -> TextRange {
        self.focus_range.unwrap_or(self.full_range)
    }

    pub fn name(&self) -> &SmolStr {
        &self.name
    }

    pub fn container_name(&self) -> Option<&SmolStr> {
        self.container_name.as_ref()
    }

    pub fn kind(&self) -> SyntaxKind {
        self.kind
    }

    pub fn file_id(&self) -> FileId {
        self.file_id
    }

    pub fn full_range(&self) -> TextRange {
        self.full_range
    }

    pub fn docs(&self) -> Option<&str> {
        self.docs.as_ref().map(String::as_str)
    }

    pub fn description(&self) -> Option<&str> {
        self.description.as_ref().map(String::as_str)
    }

    /// A "most interesting" range withing the `full_range`.
    ///
    /// Typically, `full_range` is the whole syntax node,
    /// including doc comments, and `focus_range` is the range of the identifier.
    pub fn focus_range(&self) -> Option<TextRange> {
        self.focus_range
    }

    pub(crate) fn from_bind_pat(
        db: &RootDatabase,
        file_id: FileId,
        pat: &ast::BindPat,
    ) -> NavigationTarget {
        NavigationTarget::from_named(db, file_id.into(), pat, None, None)
    }

    pub(crate) fn from_symbol(db: &RootDatabase, symbol: FileSymbol) -> NavigationTarget {
        NavigationTarget {
            file_id: symbol.file_id,
            name: symbol.name.clone(),
            kind: symbol.ptr.kind(),
            full_range: symbol.ptr.range(),
            focus_range: symbol.name_range,
            container_name: symbol.container_name.clone(),
            description: description_from_symbol(db, &symbol),
            docs: docs_from_symbol(db, &symbol),
        }
    }

    pub(crate) fn from_pat(
        db: &RootDatabase,
        file_id: FileId,
        pat: AstPtr<ast::BindPat>,
    ) -> NavigationTarget {
        let parse = db.parse(file_id);
        let pat = pat.to_node(parse.tree().syntax());
        NavigationTarget::from_bind_pat(db, file_id, &pat)
    }

    pub(crate) fn from_self_param(
        file_id: FileId,
        par: AstPtr<ast::SelfParam>,
    ) -> NavigationTarget {
        let (name, full_range) = ("self".into(), par.syntax_node_ptr().range());

        NavigationTarget {
            file_id,
            name,
            full_range,
            focus_range: None,
            kind: NAME,
            container_name: None,
            description: None, //< No document node for SelfParam
            docs: None,        //< No document node for SelfParam
        }
    }

    pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
        let src = module.definition_source(db);
        let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();

        match src.ast {
            ModuleSource::SourceFile(node) => {
                let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());

                NavigationTarget::from_syntax(
                    file_id,
                    name,
                    None,
                    text_range,
                    node.syntax(),
                    None,
                    None,
                )
            }
            ModuleSource::Module(node) => {
                let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());

                NavigationTarget::from_syntax(
                    file_id,
                    name,
                    None,
                    text_range,
                    node.syntax(),
                    node.doc_comment_text(),
                    node.short_label(),
                )
            }
        }
    }

    pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
        let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
        if let Some(src) = module.declaration_source(db) {
            let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
            return NavigationTarget::from_syntax(
                file_id,
                name,
                None,
                text_range,
                src.ast.syntax(),
                src.ast.doc_comment_text(),
                src.ast.short_label(),
            );
        }
        NavigationTarget::from_module(db, module)
    }

    pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget {
        let src = field.source(db);
        match src.ast {
            FieldSource::Named(it) => NavigationTarget::from_named(
                db,
                src.file_id,
                &it,
                it.doc_comment_text(),
                it.short_label(),
            ),
            FieldSource::Pos(it) => {
                let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax());
                NavigationTarget::from_syntax(
                    file_id,
                    "".into(),
                    None,
                    text_range,
                    it.syntax(),
                    None,
                    None,
                )
            }
        }
    }

    pub(crate) fn from_def_source<A, D>(db: &RootDatabase, def: D) -> NavigationTarget
    where
        D: HasSource<Ast = A>,
        A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
    {
        let src = def.source(db);
        NavigationTarget::from_named(
            db,
            src.file_id,
            &src.ast,
            src.ast.doc_comment_text(),
            src.ast.short_label(),
        )
    }

    pub(crate) fn from_adt_def(db: &RootDatabase, adt_def: hir::Adt) -> NavigationTarget {
        match adt_def {
            hir::Adt::Struct(it) => NavigationTarget::from_def_source(db, it),
            hir::Adt::Union(it) => NavigationTarget::from_def_source(db, it),
            hir::Adt::Enum(it) => NavigationTarget::from_def_source(db, it),
        }
    }

    pub(crate) fn from_def(
        db: &RootDatabase,
        module_def: hir::ModuleDef,
    ) -> Option<NavigationTarget> {
        let nav = match module_def {
            hir::ModuleDef::Module(module) => NavigationTarget::from_module(db, module),
            hir::ModuleDef::Function(func) => NavigationTarget::from_def_source(db, func),
            hir::ModuleDef::Adt(it) => NavigationTarget::from_adt_def(db, it),
            hir::ModuleDef::Const(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::Static(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::EnumVariant(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::Trait(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::TypeAlias(it) => NavigationTarget::from_def_source(db, it),
            hir::ModuleDef::BuiltinType(..) => {
                return None;
            }
        };
        Some(nav)
    }

    pub(crate) fn from_impl_block(
        db: &RootDatabase,
        impl_block: hir::ImplBlock,
    ) -> NavigationTarget {
        let src = impl_block.source(db);
        let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());

        NavigationTarget::from_syntax(
            file_id,
            "impl".into(),
            None,
            text_range,
            src.ast.syntax(),
            None,
            None,
        )
    }

    pub(crate) fn from_assoc_item(
        db: &RootDatabase,
        assoc_item: hir::AssocItem,
    ) -> NavigationTarget {
        match assoc_item {
            AssocItem::Function(it) => NavigationTarget::from_def_source(db, it),
            AssocItem::Const(it) => NavigationTarget::from_def_source(db, it),
            AssocItem::TypeAlias(it) => NavigationTarget::from_def_source(db, it),
        }
    }

    pub(crate) fn from_macro_def(db: &RootDatabase, macro_call: hir::MacroDef) -> NavigationTarget {
        let src = macro_call.source(db);
        log::debug!("nav target {:#?}", src.ast.syntax());
        NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None)
    }

    #[cfg(test)]
    pub(crate) fn assert_match(&self, expected: &str) {
        let actual = self.debug_render();
        test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
    }

    #[cfg(test)]
    pub(crate) fn debug_render(&self) -> String {
        let mut buf = format!(
            "{} {:?} {:?} {:?}",
            self.name(),
            self.kind(),
            self.file_id(),
            self.full_range()
        );
        if let Some(focus_range) = self.focus_range() {
            buf.push_str(&format!(" {:?}", focus_range))
        }
        if let Some(container_name) = self.container_name() {
            buf.push_str(&format!(" {}", container_name))
        }
        buf
    }

    /// Allows `NavigationTarget` to be created from a `NameOwner`
    pub(crate) fn from_named(
        db: &RootDatabase,
        file_id: hir::HirFileId,
        node: &impl ast::NameOwner,
        docs: Option<String>,
        description: Option<String>,
    ) -> NavigationTarget {
        //FIXME: use `_` instead of empty string
        let name = node.name().map(|it| it.text().clone()).unwrap_or_default();

        let focus_range = node.name().map(|it| find_range_from_node(db, file_id, it.syntax()).1);

        let (file_id, full_range) = find_range_from_node(db, file_id, node.syntax());

        NavigationTarget::from_syntax(
            file_id,
            name,
            focus_range,
            full_range,
            node.syntax(),
            docs,
            description,
        )
    }

    fn from_syntax(
        file_id: FileId,
        name: SmolStr,
        focus_range: Option<TextRange>,
        full_range: TextRange,
        node: &SyntaxNode,
        docs: Option<String>,
        description: Option<String>,
    ) -> NavigationTarget {
        NavigationTarget {
            file_id,
            name,
            kind: node.kind(),
            full_range,
            focus_range,
            container_name: None,
            description,
            docs,
        }
    }
}

pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
    let parse = db.parse(symbol.file_id);
    let node = symbol.ptr.to_node(parse.tree().syntax());

    match_ast! {
        match node {
            ast::FnDef(it) => { it.doc_comment_text() },
            ast::StructDef(it) => { it.doc_comment_text() },
            ast::EnumDef(it) => { it.doc_comment_text() },
            ast::TraitDef(it) => { it.doc_comment_text() },
            ast::Module(it) => { it.doc_comment_text() },
            ast::TypeAliasDef(it) => { it.doc_comment_text() },
            ast::ConstDef(it) => { it.doc_comment_text() },
            ast::StaticDef(it) => { it.doc_comment_text() },
            ast::RecordFieldDef(it) => { it.doc_comment_text() },
            ast::EnumVariant(it) => { it.doc_comment_text() },
            ast::MacroCall(it) => { it.doc_comment_text() },
            _ => None,
        }
    }
}

/// Get a description of a symbol.
///
/// e.g. `struct Name`, `enum Name`, `fn Name`
pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
    let parse = db.parse(symbol.file_id);
    let node = symbol.ptr.to_node(parse.tree().syntax());

    match_ast! {
        match node {
            ast::FnDef(it) => { it.short_label() },
            ast::StructDef(it) => { it.short_label() },
            ast::EnumDef(it) => { it.short_label() },
            ast::TraitDef(it) => { it.short_label() },
            ast::Module(it) => { it.short_label() },
            ast::TypeAliasDef(it) => { it.short_label() },
            ast::ConstDef(it) => { it.short_label() },
            ast::StaticDef(it) => { it.short_label() },
            ast::RecordFieldDef(it) => { it.short_label() },
            ast::EnumVariant(it) => { it.short_label() },
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use super::context::Context;
use super::vertex_data::{VertexData, VertexBytes, VertexAttribBinder};
use super::index_data::{IndexDatum};
use super::buffer::{Buffer, BufferBinding, ArrayBufferBinding};
use super::types::DrawingMode;

pub struct VertexBuffer<T: VertexData> {
    pub attrib_binder: Option<T::Binder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: VertexData> VertexBuffer<T> {
    pub fn build_attrib_binder(&self)
        -> <T::Binder as VertexAttribBinder>::Builder
    {
        T::build_attrib_binder()
    }

    pub fn bind(&self, gl_buffer: &ArrayBufferBinding) -> Result<(), ()> {
        match self.attrib_binder {
            Some(ref binder) => {
                binder.bind(gl_buffer);
                Ok(())
            },
            None => { Err(()) }
        }
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData>
    where T: 'a, T::Binder: 'a
{
    gl_buffer: ArrayBufferBinding<'a>,
    vbo: &'a mut VertexBuffer<T>
}

impl<'a, T: VertexData> VertexBufferBinding<'a, T>
    where T: 'a, T::Binder: 'a
{
    pub fn new(gl_buffer: ArrayBufferBinding<'a>, vbo: &'a mut VertexBuffer<T>)
        -> Self
    {
        VertexBufferBinding {
            gl_buffer: gl_buffer,
            vbo: vbo
        }
    }

    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: VertexBytes
    {
        self.vbo.count = data.len();
        self.gl_buffer.buffer_bytes(data.vertex_bytes(), usage);
    }

    pub fn draw_arrays_range(&self,
                             mode: DrawingMode,
                             start: u32,
                             length: usize)
    {
        debug_assert!((start as usize) + length <= self.vbo.count);

        unsafe {
            self.gl_buffer.draw_arrays_range(mode, start, length);
        }
    }

    pub fn draw_arrays(&self, mode: DrawingMode) {
        unsafe {
            self.gl_buffer.draw_arrays_range(mode, 0, self.vbo.count);
        }
    }
}

impl Context {
    pub fn new_vertex_buffer<T: VertexData>(&self) -> VertexBuffer<T> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = vbo.build_attrib_binder()
                            $(.$field_name($field_attrib))*
                            .unwrap($gl);
            vbo.attrib_binder = Some(binder)
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            let vbo = $vbo;

            let gl_buffer = bind_array_buffer!($gl, vbo.buffer_mut());
            vbo.bind(&gl_buffer).unwrap();
            $crate::VertexBufferBinding::new(gl_buffer, vbo)
        }
    }
}
<commit_msg>Add `Context.new_index_buffer()`<commit_after>use std::marker::PhantomData;
use super::context::Context;
use super::vertex_data::{VertexData, VertexBytes, VertexAttribBinder};
use super::index_data::{IndexDatum};
use super::buffer::{Buffer, BufferBinding, ArrayBufferBinding};
use super::types::DrawingMode;

pub struct VertexBuffer<T: VertexData> {
    pub attrib_binder: Option<T::Binder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: VertexData> VertexBuffer<T> {
    pub fn build_attrib_binder(&self)
        -> <T::Binder as VertexAttribBinder>::Builder
    {
        T::build_attrib_binder()
    }

    pub fn bind(&self, gl_buffer: &ArrayBufferBinding) -> Result<(), ()> {
        match self.attrib_binder {
            Some(ref binder) => {
                binder.bind(gl_buffer);
                Ok(())
            },
            None => { Err(()) }
        }
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData>
    where T: 'a, T::Binder: 'a
{
    gl_buffer: ArrayBufferBinding<'a>,
    vbo: &'a mut VertexBuffer<T>
}

impl<'a, T: VertexData> VertexBufferBinding<'a, T>
    where T: 'a, T::Binder: 'a
{
    pub fn new(gl_buffer: ArrayBufferBinding<'a>, vbo: &'a mut VertexBuffer<T>)
        -> Self
    {
        VertexBufferBinding {
            gl_buffer: gl_buffer,
            vbo: vbo
        }
    }

    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: VertexBytes
    {
        self.vbo.count = data.len();
        self.gl_buffer.buffer_bytes(data.vertex_bytes(), usage);
    }

    pub fn draw_arrays_range(&self,
                             mode: DrawingMode,
                             start: u32,
                             length: usize)
    {
        debug_assert!((start as usize) + length <= self.vbo.count);

        unsafe {
            self.gl_buffer.draw_arrays_range(mode, start, length);
        }
    }

    pub fn draw_arrays(&self, mode: DrawingMode) {
        unsafe {
            self.gl_buffer.draw_arrays_range(mode, 0, self.vbo.count);
        }
    }
}

impl Context {
    pub fn new_vertex_buffer<T: VertexData>(&self) -> VertexBuffer<T> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl Context {
    pub fn new_index_buffer<T: IndexDatum>(&self) -> IndexBuffer<T> {
        IndexBuffer {
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = vbo.build_attrib_binder()
                            $(.$field_name($field_attrib))*
                            .unwrap($gl);
            vbo.attrib_binder = Some(binder)
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            let vbo = $vbo;

            let gl_buffer = bind_array_buffer!($gl, vbo.buffer_mut());
            vbo.bind(&gl_buffer).unwrap();
            $crate::VertexBufferBinding::new(gl_buffer, vbo)
        }
    }
}
<|endoftext|>


<commit_before>use super::TimerUpdateWidget;
use text::{Attributes, Text};

use std::time::Duration;

use chrono::prelude::*;

pub struct Clock {
    update_interval: Duration,
    attr: Attributes,
}

impl Clock {
    pub fn new(attr: Attributes) -> Clock {
        Clock {
            update_interval: Duration::from_secs(1),
            attr: attr,
        }
    }
}

impl TimerUpdateWidget for Clock {
    fn update_interval(&self) -> Duration {
        self.update_interval
    }

    fn tick(&self) -> Vec<Text> {
        let current_time = Local::now().format("%Y-%m-%d %a %I:%M %p").to_string();
        vec![Text {
                 attr: self.attr.clone(),
                 text: current_time,
                 stretch: false,
             }]
    }
}
<commit_msg>Display seconds as it makes it easier to debug.<commit_after>use super::TimerUpdateWidget;
use text::{Attributes, Text};

use std::time::Duration;

use chrono::prelude::*;

pub struct Clock {
    update_interval: Duration,
    attr: Attributes,
}

impl Clock {
    pub fn new(attr: Attributes) -> Clock {
        Clock {
            update_interval: Duration::from_secs(1),
            attr: attr,
        }
    }
}

impl TimerUpdateWidget for Clock {
    fn update_interval(&self) -> Duration {
        self.update_interval
    }

    fn tick(&self) -> Vec<Text> {
        let current_time = Local::now().format("%Y-%m-%d %a %I:%M:%S %p").to_string();
        vec![Text {
                 attr: self.attr.clone(),
                 text: current_time,
                 stretch: false,
             }]
    }
}
<|endoftext|>


<commit_before>use std::ffi::OsStr;
use std::fs;
use std::process::{Command, Output};

fn run_example<S: AsRef<str>>(name: S, args: &[&str]) -> Output {
    let mut all_args = vec![
        "run",
        "--example",
        name.as_ref(),
        "--features",
        "yaml unstable",
        "--",
    ];
    all_args.extend_from_slice(args);

    Command::new(env!("CARGO"))
        .args(all_args)
        .output()
        .expect("failed to run example")
}

#[test]
fn examples_are_functional() {
    let example_paths = fs::read_dir("examples")
        .expect("couldn't read examples directory")
        .map(|result| result.expect("couldn't get directory entry").path())
        .filter(|path| path.is_file() && path.extension().and_then(OsStr::to_str) == Some("rs"));

    let mut example_count = 0;
    for path in example_paths {
        example_count += 1;

        let example_name = path
            .file_stem()
            .and_then(OsStr::to_str)
            .expect("unable to determine example name");

        let help_output = run_example(example_name, &["--help"]);
        assert!(
            help_output.status.success(),
            "{} --help exited with nonzero",
            example_name,
        );
        assert!(
            !help_output.stdout.is_empty(),
            "{} --help had no output",
            example_name,
        );
    }
    assert!(example_count > 0);
}
<commit_msg>Exclude tests/examples.rs while checking coverage.<commit_after>#![cfg(not(tarpaulin))]

use std::ffi::OsStr;
use std::fs;
use std::process::{Command, Output};

fn run_example<S: AsRef<str>>(name: S, args: &[&str]) -> Output {
    let mut all_args = vec![
        "run",
        "--example",
        name.as_ref(),
        "--features",
        "yaml unstable",
        "--",
    ];
    all_args.extend_from_slice(args);

    Command::new(env!("CARGO"))
        .args(all_args)
        .output()
        .expect("failed to run example")
}

#[test]
fn examples_are_functional() {
    let example_paths = fs::read_dir("examples")
        .expect("couldn't read examples directory")
        .map(|result| result.expect("couldn't get directory entry").path())
        .filter(|path| path.is_file() && path.extension().and_then(OsStr::to_str) == Some("rs"));

    let mut example_count = 0;
    for path in example_paths {
        example_count += 1;

        let example_name = path
            .file_stem()
            .and_then(OsStr::to_str)
            .expect("unable to determine example name");

        let help_output = run_example(example_name, &["--help"]);
        assert!(
            help_output.status.success(),
            "{} --help exited with nonzero",
            example_name,
        );
        assert!(
            !help_output.stdout.is_empty(),
            "{} --help had no output",
            example_name,
        );
    }
    assert!(example_count > 0);
}
<|endoftext|>


<commit_before>use dicom::core::smallvec;
use dicom::{core::dicom_value, dictionary_std::tags};
use dicom::{
    core::{DataElement, PrimitiveValue, VR},
    encoding::transfer_syntax,
    object::{mem::InMemDicomObject, open_file, StandardDataDictionary},
    transfer_syntax::TransferSyntaxRegistry,
};
use dicom_ul::pdu::Pdu;
use dicom_ul::{
    association::ClientAssociationOptions,
    pdu::{PDataValue, PDataValueType},
};
use smallvec::smallvec;
use std::{io::Write, path::PathBuf};
use structopt::StructOpt;
use transfer_syntax::TransferSyntaxIndex;

/// DICOM C-STORE SCU
#[derive(Debug, StructOpt)]
struct App {
    /// socket address to STORE SCP (example: "127.0.0.1:104")
    addr: String,
    /// the DICOM file to store
    file: PathBuf,
    /// verbose mode
    #[structopt(short = "v", long = "verbose")]
    verbose: bool,
    /// the C-STORE message ID
    #[structopt(short = "m", long = "message-id", default_value = "1")]
    message_id: u16,
    /// the calling AE title
    #[structopt(long = "calling-ae-title", default_value = "STORE-SCU")]
    calling_ae_title: String,
    /// the called AE title
    #[structopt(long = "called-ae-title", default_value = "ANY-SCP")]
    called_ae_title: String,
    /// the maximum PDU length
    #[structopt(long = "max-pdu-length", default_value = "16384")]
    max_pdu_length: u32,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let App {
        addr,
        file,
        verbose,
        message_id,
        calling_ae_title,
        called_ae_title,
        max_pdu_length,
    } = App::from_args();

    if verbose {
        println!("Opening file '{}'...", file.display());
    }

    let dicom_file = open_file(file)?;

    let meta = dicom_file.meta();

    let storage_sop_class_uid = &meta.media_storage_sop_class_uid;
    let storage_sop_instance_uid = &meta.media_storage_sop_instance_uid;
    let transfer_syntax = &meta.transfer_syntax.trim_end_matches("\0");

    let file_ts_desc = TransferSyntaxRegistry
        .get(transfer_syntax)
        .ok_or("Unsupported file transfer syntax")?;

    if verbose {
        println!("Establishing association with '{}'...", &addr);
    }

    let mut scu = ClientAssociationOptions::new()
        .with_abstract_syntax(storage_sop_class_uid.to_string())
        .with_transfer_syntax(transfer_syntax.to_string())
        .calling_ae_title(calling_ae_title)
        .called_ae_title(called_ae_title)
        .max_pdu_length(max_pdu_length)
        .establish(addr)?;

    if verbose {
        println!("Association established");
    }

    // TODO(#106) transfer syntax conversion is currently not supported
    let pc_selected = if let Some(pc) = scu.presentation_contexts().iter().find(|pc| {
        // Check support for this transfer syntax.
        // If it is the same as the file, we're good.
        // Otherwise, uncompressed data set encoding
        // and native pixel data is required on both ends.
        let ts = dbg!(&pc.transfer_syntax);
        ts == transfer_syntax
            || TransferSyntaxRegistry
                .get(&pc.transfer_syntax)
                .filter(|ts| file_ts_desc.is_codec_free() && ts.is_codec_free())
                .map(|_| true)
                .unwrap_or(false)
    }) {
        pc.clone()
    } else {
        eprintln!("Could not choose a transfer syntax");
        let _ = scu.abort();
        std::process::exit(-2);
    };

    let ts = TransferSyntaxRegistry
        .get(&pc_selected.transfer_syntax)
        .ok_or_else(|| "Poorly negotiated transfer syntax")?;

    if verbose {
        println!("Transfer Syntax: {}", ts.name());
    }

    let cmd = store_req_command(
        &storage_sop_class_uid,
        &storage_sop_instance_uid,
        message_id,
    );

    let mut cmd_data = Vec::with_capacity(128);
    cmd.write_dataset_with_ts(
        &mut cmd_data,
        &dicom::transfer_syntax::entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
    )?;

    let mut object_data = Vec::with_capacity(1024);
    dicom_file.write_dataset_with_ts(&mut object_data, &ts)?;

    let nbytes = cmd_data.len() + object_data.len();

    if verbose {
        println!("Sending payload (~ {} Kb)...", nbytes / 1024);
    }

    if nbytes < max_pdu_length as usize - 100 {
        let pdu = Pdu::PData {
            data: vec![
                PDataValue {
                    presentation_context_id: pc_selected.id,
                    value_type: PDataValueType::Command,
                    is_last: true,
                    data: cmd_data,
                },
                PDataValue {
                    presentation_context_id: pc_selected.id,
                    value_type: PDataValueType::Data,
                    is_last: true,
                    data: object_data,
                },
            ],
        };

        scu.send(&pdu)?;
    } else {
        let pdu = Pdu::PData {
            data: vec![PDataValue {
                presentation_context_id: pc_selected.id,
                value_type: PDataValueType::Command,
                is_last: true,
                data: cmd_data,
            }],
        };

        scu.send(&pdu)?;

        scu.send_pdata(pc_selected.id).write_all(&object_data)?;
    }

    if verbose {
        println!("Awaiting response...");
    }

    let rsp_pdu = scu.receive()?;

    match rsp_pdu {
        Pdu::PData { data } => {
            let data_value = &data[0];

            let cmd_obj = InMemDicomObject::read_dataset_with_ts(
                &data_value.data[..],
                &dicom::transfer_syntax::entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
            )?;
            if verbose {
                println!("Response: {:?}", cmd_obj);
            }
            let status = cmd_obj.element(tags::STATUS)?.to_int::<u16>()?;
            if status == 0 {
                println!("Sucessfully stored instance `{}`", storage_sop_instance_uid);
            } else {
                println!(
                    "Failed to store instance '{}' (status code {})",
                    storage_sop_instance_uid, status
                );
            }

            scu.release()?;
        }

        pdu @ Pdu::Unknown { .. }
        | pdu @ Pdu::AssociationRQ { .. }
        | pdu @ Pdu::AssociationAC { .. }
        | pdu @ Pdu::AssociationRJ { .. }
        | pdu @ Pdu::ReleaseRQ
        | pdu @ Pdu::ReleaseRP
        | pdu @ Pdu::AbortRQ { .. } => {
            eprintln!("Unexpected SCP response: {:?}", pdu);
            let _ = scu.abort();
            std::process::exit(-2);
        }
    }

    Ok(())
}

fn store_req_command(
    storage_sop_class_uid: &str,
    storage_sop_instance_uid: &str,
    message_id: u16,
) -> InMemDicomObject<StandardDataDictionary> {
    let mut obj = InMemDicomObject::create_empty();

    // group length
    obj.put(DataElement::new(
        tags::COMMAND_GROUP_LENGTH,
        VR::UL,
        PrimitiveValue::from(
            12 + 8
                + even_len(storage_sop_class_uid.len())
                + 10
                + 10
                + 10
                + 10
                + 8
                + even_len(storage_sop_instance_uid.len()),
        ),
    ));

    // SOP Class UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_CLASS_UID,
        VR::UI,
        dicom_value!(Str, storage_sop_class_uid),
    ));

    // command field
    obj.put(DataElement::new(
        tags::COMMAND_FIELD,
        VR::US,
        dicom_value!(U16, [0x0001]),
    ));

    // message ID
    obj.put(DataElement::new(
        tags::MESSAGE_ID,
        VR::US,
        dicom_value!(U16, [message_id]),
    ));

    //priority
    obj.put(DataElement::new(
        tags::PRIORITY,
        VR::US,
        dicom_value!(U16, [0x0000]),
    ));

    // data set type
    obj.put(DataElement::new(
        tags::COMMAND_DATA_SET_TYPE,
        VR::US,
        dicom_value!(U16, [0x0000]),
    ));

    // affected SOP Instance UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_INSTANCE_UID,
        VR::UI,
        dicom_value!(Str, storage_sop_instance_uid),
    ));

    obj
}

fn even_len(l: usize) -> u32 {
    ((l + 1) & !1) as u32
}

#[cfg(test)]
mod tests {
    use super::even_len;
    #[test]
    fn test_even_len() {
        assert_eq!(even_len(0), 0);
        assert_eq!(even_len(1), 2);
        assert_eq!(even_len(2), 2);
        assert_eq!(even_len(3), 4);
        assert_eq!(even_len(4), 4);
        assert_eq!(even_len(5), 6);
    }
}
<commit_msg>Remove dbg! call<commit_after>use dicom::core::smallvec;
use dicom::{core::dicom_value, dictionary_std::tags};
use dicom::{
    core::{DataElement, PrimitiveValue, VR},
    encoding::transfer_syntax,
    object::{mem::InMemDicomObject, open_file, StandardDataDictionary},
    transfer_syntax::TransferSyntaxRegistry,
};
use dicom_ul::pdu::Pdu;
use dicom_ul::{
    association::ClientAssociationOptions,
    pdu::{PDataValue, PDataValueType},
};
use smallvec::smallvec;
use std::{io::Write, path::PathBuf};
use structopt::StructOpt;
use transfer_syntax::TransferSyntaxIndex;

/// DICOM C-STORE SCU
#[derive(Debug, StructOpt)]
struct App {
    /// socket address to STORE SCP (example: "127.0.0.1:104")
    addr: String,
    /// the DICOM file to store
    file: PathBuf,
    /// verbose mode
    #[structopt(short = "v", long = "verbose")]
    verbose: bool,
    /// the C-STORE message ID
    #[structopt(short = "m", long = "message-id", default_value = "1")]
    message_id: u16,
    /// the calling AE title
    #[structopt(long = "calling-ae-title", default_value = "STORE-SCU")]
    calling_ae_title: String,
    /// the called AE title
    #[structopt(long = "called-ae-title", default_value = "ANY-SCP")]
    called_ae_title: String,
    /// the maximum PDU length
    #[structopt(long = "max-pdu-length", default_value = "16384")]
    max_pdu_length: u32,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let App {
        addr,
        file,
        verbose,
        message_id,
        calling_ae_title,
        called_ae_title,
        max_pdu_length,
    } = App::from_args();

    if verbose {
        println!("Opening file '{}'...", file.display());
    }

    let dicom_file = open_file(file)?;

    let meta = dicom_file.meta();

    let storage_sop_class_uid = &meta.media_storage_sop_class_uid;
    let storage_sop_instance_uid = &meta.media_storage_sop_instance_uid;
    let transfer_syntax = &meta.transfer_syntax.trim_end_matches("\0");

    let file_ts_desc = TransferSyntaxRegistry
        .get(transfer_syntax)
        .ok_or("Unsupported file transfer syntax")?;

    if verbose {
        println!("Establishing association with '{}'...", &addr);
    }

    let mut scu = ClientAssociationOptions::new()
        .with_abstract_syntax(storage_sop_class_uid.to_string())
        .with_transfer_syntax(transfer_syntax.to_string())
        .calling_ae_title(calling_ae_title)
        .called_ae_title(called_ae_title)
        .max_pdu_length(max_pdu_length)
        .establish(addr)?;

    if verbose {
        println!("Association established");
    }

    // TODO(#106) transfer syntax conversion is currently not supported
    let pc_selected = if let Some(pc) = scu.presentation_contexts().iter().find(|pc| {
        // Check support for this transfer syntax.
        // If it is the same as the file, we're good.
        // Otherwise, uncompressed data set encoding
        // and native pixel data is required on both ends.
        let ts = &pc.transfer_syntax;
        ts == transfer_syntax
            || TransferSyntaxRegistry
                .get(&pc.transfer_syntax)
                .filter(|ts| file_ts_desc.is_codec_free() && ts.is_codec_free())
                .map(|_| true)
                .unwrap_or(false)
    }) {
        pc.clone()
    } else {
        eprintln!("Could not choose a transfer syntax");
        let _ = scu.abort();
        std::process::exit(-2);
    };

    let ts = TransferSyntaxRegistry
        .get(&pc_selected.transfer_syntax)
        .ok_or_else(|| "Poorly negotiated transfer syntax")?;

    if verbose {
        println!("Transfer Syntax: {}", ts.name());
    }

    let cmd = store_req_command(
        &storage_sop_class_uid,
        &storage_sop_instance_uid,
        message_id,
    );

    let mut cmd_data = Vec::with_capacity(128);
    cmd.write_dataset_with_ts(
        &mut cmd_data,
        &dicom::transfer_syntax::entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
    )?;

    let mut object_data = Vec::with_capacity(1024);
    dicom_file.write_dataset_with_ts(&mut object_data, &ts)?;

    let nbytes = cmd_data.len() + object_data.len();

    if verbose {
        println!("Sending payload (~ {} Kb)...", nbytes / 1024);
    }

    if nbytes < max_pdu_length as usize - 100 {
        let pdu = Pdu::PData {
            data: vec![
                PDataValue {
                    presentation_context_id: pc_selected.id,
                    value_type: PDataValueType::Command,
                    is_last: true,
                    data: cmd_data,
                },
                PDataValue {
                    presentation_context_id: pc_selected.id,
                    value_type: PDataValueType::Data,
                    is_last: true,
                    data: object_data,
                },
            ],
        };

        scu.send(&pdu)?;
    } else {
        let pdu = Pdu::PData {
            data: vec![PDataValue {
                presentation_context_id: pc_selected.id,
                value_type: PDataValueType::Command,
                is_last: true,
                data: cmd_data,
            }],
        };

        scu.send(&pdu)?;

        scu.send_pdata(pc_selected.id).write_all(&object_data)?;
    }

    if verbose {
        println!("Awaiting response...");
    }

    let rsp_pdu = scu.receive()?;

    match rsp_pdu {
        Pdu::PData { data } => {
            let data_value = &data[0];

            let cmd_obj = InMemDicomObject::read_dataset_with_ts(
                &data_value.data[..],
                &dicom::transfer_syntax::entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
            )?;
            if verbose {
                println!("Response: {:?}", cmd_obj);
            }
            let status = cmd_obj.element(tags::STATUS)?.to_int::<u16>()?;
            if status == 0 {
                println!("Sucessfully stored instance `{}`", storage_sop_instance_uid);
            } else {
                println!(
                    "Failed to store instance '{}' (status code {})",
                    storage_sop_instance_uid, status
                );
            }

            scu.release()?;
        }

        pdu @ Pdu::Unknown { .. }
        | pdu @ Pdu::AssociationRQ { .. }
        | pdu @ Pdu::AssociationAC { .. }
        | pdu @ Pdu::AssociationRJ { .. }
        | pdu @ Pdu::ReleaseRQ
        | pdu @ Pdu::ReleaseRP
        | pdu @ Pdu::AbortRQ { .. } => {
            eprintln!("Unexpected SCP response: {:?}", pdu);
            let _ = scu.abort();
            std::process::exit(-2);
        }
    }

    Ok(())
}

fn store_req_command(
    storage_sop_class_uid: &str,
    storage_sop_instance_uid: &str,
    message_id: u16,
) -> InMemDicomObject<StandardDataDictionary> {
    let mut obj = InMemDicomObject::create_empty();

    // group length
    obj.put(DataElement::new(
        tags::COMMAND_GROUP_LENGTH,
        VR::UL,
        PrimitiveValue::from(
            12 + 8
                + even_len(storage_sop_class_uid.len())
                + 10
                + 10
                + 10
                + 10
                + 8
                + even_len(storage_sop_instance_uid.len()),
        ),
    ));

    // SOP Class UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_CLASS_UID,
        VR::UI,
        dicom_value!(Str, storage_sop_class_uid),
    ));

    // command field
    obj.put(DataElement::new(
        tags::COMMAND_FIELD,
        VR::US,
        dicom_value!(U16, [0x0001]),
    ));

    // message ID
    obj.put(DataElement::new(
        tags::MESSAGE_ID,
        VR::US,
        dicom_value!(U16, [message_id]),
    ));

    //priority
    obj.put(DataElement::new(
        tags::PRIORITY,
        VR::US,
        dicom_value!(U16, [0x0000]),
    ));

    // data set type
    obj.put(DataElement::new(
        tags::COMMAND_DATA_SET_TYPE,
        VR::US,
        dicom_value!(U16, [0x0000]),
    ));

    // affected SOP Instance UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_INSTANCE_UID,
        VR::UI,
        dicom_value!(Str, storage_sop_instance_uid),
    ));

    obj
}

fn even_len(l: usize) -> u32 {
    ((l + 1) & !1) as u32
}

#[cfg(test)]
mod tests {
    use super::even_len;
    #[test]
    fn test_even_len() {
        assert_eq!(even_len(0), 0);
        assert_eq!(even_len(1), 2);
        assert_eq!(even_len(2), 2);
        assert_eq!(even_len(3), 4);
        assert_eq!(even_len(4), 4);
        assert_eq!(even_len(5), 6);
    }
}
<|endoftext|>


<commit_before>//! The general point of the optimizations provided here is to simplify something like:
//!
//! ```rust
//! match x {
//!     Ok(x) => Ok(x),
//!     Err(x) => Err(x)
//! }
//! ```
//!
//! into just `x`.

use crate::transform::{simplify, MirPass, MirSource};
use itertools::Itertools as _;
use rustc::mir::*;
use rustc::ty::{Ty, TyCtxt};
use rustc_target::abi::VariantIdx;

/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move.
///
/// This is done by transforming basic blocks where the statements match:
///
/// ```rust
/// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY );
/// ((_LOCAL_0 as Variant).FIELD: TY) = move _LOCAL_TMP;
/// discriminant(_LOCAL_0) = VAR_IDX;
/// ```
///
/// into:
///
/// ```rust
/// _LOCAL_0 = move _LOCAL_1
/// ```
pub struct SimplifyArmIdentity;

impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
    fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
        for bb in basic_blocks {
            // Need 3 statements:
            let (s0, s1, s2) = match &mut *bb.statements {
                [s0, s1, s2] => (s0, s1, s2),
                _ => continue,
            };

            // Pattern match on the form we want:
            let (local_tmp_s0, local_1, vf_s0) = match match_get_variant_field(s0) {
                None => continue,
                Some(x) => x,
            };
            let (local_tmp_s1, local_0, vf_s1) = match match_set_variant_field(s1) {
                None => continue,
                Some(x) => x,
            };
            if local_tmp_s0 != local_tmp_s1
                // The field-and-variant information match up.
                || vf_s0 != vf_s1
                // Source and target locals have the same type.
                // FIXME(Centril | oli-obk): possibly relax to same layout?
                || local_decls[local_0].ty != local_decls[local_1].ty
                // We're setting the discriminant of `local_0` to this variant.
                || Some((local_0, vf_s0.var_idx)) != match_set_discr(s2)
            {
                continue;
            }

            // Right shape; transform!
            match &mut s0.kind {
                StatementKind::Assign(box (place, rvalue)) => {
                    *place = local_0.into();
                    *rvalue = Rvalue::Use(Operand::Move(local_1.into()));
                }
                _ => unreachable!(),
            }
            s1.make_nop();
            s2.make_nop();
        }
    }
}

/// Match on:
/// ```rust
/// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY);
/// ```
fn match_get_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> {
    match &stmt.kind {
        StatementKind::Assign(box (place_into, rvalue_from)) => match rvalue_from {
            Rvalue::Use(Operand::Copy(pf)) | Rvalue::Use(Operand::Move(pf)) => {
                let local_into = place_into.as_local()?;
                let (local_from, vf) = match_variant_field_place(&pf)?;
                Some((local_into, local_from, vf))
            }
            _ => None,
        },
        _ => None,
    }
}

/// Match on:
/// ```rust
/// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO;
/// ```
fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> {
    match &stmt.kind {
        StatementKind::Assign(box (place_from, rvalue_into)) => match rvalue_into {
            Rvalue::Use(Operand::Move(place_into)) => {
                let local_into = place_into.as_local()?;
                let (local_from, vf) = match_variant_field_place(&place_from)?;
                Some((local_into, local_from, vf))
            }
            _ => None,
        },
        _ => None,
    }
}

/// Match on:
/// ```rust
/// discriminant(_LOCAL_TO_SET) = VAR_IDX;
/// ```
fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)> {
    match &stmt.kind {
        StatementKind::SetDiscriminant { place, variant_index } => {
            Some((place.as_local()?, *variant_index))
        }
        _ => None,
    }
}

#[derive(PartialEq)]
struct VarField<'tcx> {
    field: Field,
    field_ty: Ty<'tcx>,
    var_idx: VariantIdx,
}

/// Match on `((_LOCAL as Variant).FIELD: TY)`.
fn match_variant_field_place<'tcx>(place: &Place<'tcx>) -> Option<(Local, VarField<'tcx>)> {
    match place.as_ref() {
        PlaceRef {
            local,
            projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)],
        } => Some((local, VarField { field, field_ty: ty, var_idx })),
        _ => None,
    }
}

/// Simplifies `SwitchInt(_) -> [targets]`,
/// where all the `targets` have the same form,
/// into `goto -> target_first`.
pub struct SimplifyBranchSame;

impl<'tcx> MirPass<'tcx> for SimplifyBranchSame {
    fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
        let mut did_remove_blocks = false;
        let bbs = body.basic_blocks_mut();
        for bb_idx in bbs.indices() {
            let targets = match &bbs[bb_idx].terminator().kind {
                TerminatorKind::SwitchInt { targets, .. } => targets,
                _ => continue,
            };

            let mut iter_bbs_reachable = targets
                .iter()
                .map(|idx| (*idx, &bbs[*idx]))
                .filter(|(_, bb)| {
                    // Reaching `unreachable` is UB so assume it doesn't happen.
                    bb.terminator().kind != TerminatorKind::Unreachable
                    // But `asm!(...)` could abort the program,
                    // so we cannot assume that the `unreachable` terminator itself is reachable.
                    // FIXME(Centril): use a normalization pass instead of a check.
                    || bb.statements.iter().any(|stmt| match stmt.kind {
                        StatementKind::InlineAsm(..) => true,
                        _ => false,
                    })
                })
                .peekable();

            // We want to `goto -> bb_first`.
            let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(targets[0]);

            // All successor basic blocks should have the exact same form.
            let all_successors_equivalent =
                iter_bbs_reachable.map(|(_, bb)| bb).tuple_windows().all(|(bb_l, bb_r)| {
                    bb_l.is_cleanup == bb_r.is_cleanup
                        && bb_l.terminator().kind == bb_r.terminator().kind
                        && bb_l.statements.iter().eq_by(&bb_r.statements, |x, y| x.kind == y.kind)
                });

            if all_successors_equivalent {
                // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`.
                bbs[bb_idx].terminator_mut().kind = TerminatorKind::Goto { target: bb_first };
                did_remove_blocks = true;
            }
        }

        if did_remove_blocks {
            // We have dead blocks now, so remove those.
            simplify::remove_dead_blocks(body);
        }
    }
}
<commit_msg>Rollup merge of #69051 - Centril:st-fixes, r=eddyb<commit_after>//! The general point of the optimizations provided here is to simplify something like:
//!
//! ```rust
//! match x {
//!     Ok(x) => Ok(x),
//!     Err(x) => Err(x)
//! }
//! ```
//!
//! into just `x`.

use crate::transform::{simplify, MirPass, MirSource};
use itertools::Itertools as _;
use rustc::mir::*;
use rustc::ty::{Ty, TyCtxt};
use rustc_target::abi::VariantIdx;

/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move.
///
/// This is done by transforming basic blocks where the statements match:
///
/// ```rust
/// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY );
/// ((_LOCAL_0 as Variant).FIELD: TY) = move _LOCAL_TMP;
/// discriminant(_LOCAL_0) = VAR_IDX;
/// ```
///
/// into:
///
/// ```rust
/// _LOCAL_0 = move _LOCAL_1
/// ```
pub struct SimplifyArmIdentity;

impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
    fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
        for bb in basic_blocks {
            // Need 3 statements:
            let (s0, s1, s2) = match &mut *bb.statements {
                [s0, s1, s2] => (s0, s1, s2),
                _ => continue,
            };

            // Pattern match on the form we want:
            let (local_tmp_s0, local_1, vf_s0) = match match_get_variant_field(s0) {
                None => continue,
                Some(x) => x,
            };
            let (local_tmp_s1, local_0, vf_s1) = match match_set_variant_field(s1) {
                None => continue,
                Some(x) => x,
            };
            if local_tmp_s0 != local_tmp_s1
                // Avoid moving into ourselves.
                || local_0 == local_1
                // The field-and-variant information match up.
                || vf_s0 != vf_s1
                // Source and target locals have the same type.
                // FIXME(Centril | oli-obk): possibly relax to same layout?
                || local_decls[local_0].ty != local_decls[local_1].ty
                // We're setting the discriminant of `local_0` to this variant.
                || Some((local_0, vf_s0.var_idx)) != match_set_discr(s2)
            {
                continue;
            }

            // Right shape; transform!
            s0.source_info = s2.source_info;
            match &mut s0.kind {
                StatementKind::Assign(box (place, rvalue)) => {
                    *place = local_0.into();
                    *rvalue = Rvalue::Use(Operand::Move(local_1.into()));
                }
                _ => unreachable!(),
            }
            s1.make_nop();
            s2.make_nop();
        }
    }
}

/// Match on:
/// ```rust
/// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY);
/// ```
fn match_get_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> {
    match &stmt.kind {
        StatementKind::Assign(box (place_into, rvalue_from)) => match rvalue_from {
            Rvalue::Use(Operand::Copy(pf)) | Rvalue::Use(Operand::Move(pf)) => {
                let local_into = place_into.as_local()?;
                let (local_from, vf) = match_variant_field_place(&pf)?;
                Some((local_into, local_from, vf))
            }
            _ => None,
        },
        _ => None,
    }
}

/// Match on:
/// ```rust
/// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO;
/// ```
fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> {
    match &stmt.kind {
        StatementKind::Assign(box (place_from, rvalue_into)) => match rvalue_into {
            Rvalue::Use(Operand::Move(place_into)) => {
                let local_into = place_into.as_local()?;
                let (local_from, vf) = match_variant_field_place(&place_from)?;
                Some((local_into, local_from, vf))
            }
            _ => None,
        },
        _ => None,
    }
}

/// Match on:
/// ```rust
/// discriminant(_LOCAL_TO_SET) = VAR_IDX;
/// ```
fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)> {
    match &stmt.kind {
        StatementKind::SetDiscriminant { place, variant_index } => {
            Some((place.as_local()?, *variant_index))
        }
        _ => None,
    }
}

#[derive(PartialEq)]
struct VarField<'tcx> {
    field: Field,
    field_ty: Ty<'tcx>,
    var_idx: VariantIdx,
}

/// Match on `((_LOCAL as Variant).FIELD: TY)`.
fn match_variant_field_place<'tcx>(place: &Place<'tcx>) -> Option<(Local, VarField<'tcx>)> {
    match place.as_ref() {
        PlaceRef {
            local,
            projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)],
        } => Some((local, VarField { field, field_ty: ty, var_idx })),
        _ => None,
    }
}

/// Simplifies `SwitchInt(_) -> [targets]`,
/// where all the `targets` have the same form,
/// into `goto -> target_first`.
pub struct SimplifyBranchSame;

impl<'tcx> MirPass<'tcx> for SimplifyBranchSame {
    fn run_pass(&self, _: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
        let mut did_remove_blocks = false;
        let bbs = body.basic_blocks_mut();
        for bb_idx in bbs.indices() {
            let targets = match &bbs[bb_idx].terminator().kind {
                TerminatorKind::SwitchInt { targets, .. } => targets,
                _ => continue,
            };

            let mut iter_bbs_reachable = targets
                .iter()
                .map(|idx| (*idx, &bbs[*idx]))
                .filter(|(_, bb)| {
                    // Reaching `unreachable` is UB so assume it doesn't happen.
                    bb.terminator().kind != TerminatorKind::Unreachable
                    // But `asm!(...)` could abort the program,
                    // so we cannot assume that the `unreachable` terminator itself is reachable.
                    // FIXME(Centril): use a normalization pass instead of a check.
                    || bb.statements.iter().any(|stmt| match stmt.kind {
                        StatementKind::InlineAsm(..) => true,
                        _ => false,
                    })
                })
                .peekable();

            // We want to `goto -> bb_first`.
            let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(targets[0]);

            // All successor basic blocks should have the exact same form.
            let all_successors_equivalent =
                iter_bbs_reachable.map(|(_, bb)| bb).tuple_windows().all(|(bb_l, bb_r)| {
                    bb_l.is_cleanup == bb_r.is_cleanup
                        && bb_l.terminator().kind == bb_r.terminator().kind
                        && bb_l.statements.iter().eq_by(&bb_r.statements, |x, y| x.kind == y.kind)
                });

            if all_successors_equivalent {
                // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`.
                bbs[bb_idx].terminator_mut().kind = TerminatorKind::Goto { target: bb_first };
                did_remove_blocks = true;
            }
        }

        if did_remove_blocks {
            // We have dead blocks now, so remove those.
            simplify::remove_dead_blocks(body);
        }
    }
}
<|endoftext|>


<commit_before>//! An implementation of something like dual contouring.
//! Dual contouring examines the voxel grid and looks for edges that cross between different material types.
//! If at least one of the materials is non-opaque, polygons are generated by using vertex data from the adjacent voxels.
//! See the SIGGRAPH paper for more details, but note that this probably isn't precisely a dual contouring implementation.

mod voxel {
  pub use voxel_data::impls::surface_vertex::T::*;
  pub use voxel_data::impls::surface_vertex::*;
}

#[allow(missing_docs)]
pub mod material {
  /// This trait provides the functionality required from materials for this algorithm.
  pub trait T : Eq {
    /// Is this material opaque?
    fn is_opaque(&self) -> bool;
  }
}

#[allow(missing_docs)]
pub mod polygon {
  use cgmath::{Point3, Vector3};

  /// The polygon type produced by this algorithm.
  pub struct T<Material> {
    #[allow(missing_docs)]
    pub vertices: [Point3<f32>; 3],
    #[allow(missing_docs)]
    pub normals: [Vector3<f32>; 3],
    #[allow(missing_docs)]
    pub material: Material,
  }
}

#[allow(missing_docs)]
pub mod voxel_storage {
  use cgmath::{Point3, Vector3};
  use voxel_data;

  use super::material;

  #[allow(missing_docs)]
  pub struct VoxelData {
    pub bounds: voxel_data::bounds::T,
    pub vertex: Point3<f32>,
    pub normal: Vector3<f32>,
  }

  /// The voxel storage interface required by dual contouring.
  pub trait T<Material> where Material: material::T {
    #[allow(missing_docs)]
    fn get_material(&mut self, voxel: &voxel_data::bounds::T) -> Option<Material>;
    /// Get the data for the given voxel. This function may also return data from a larger encompassing voxel.
    fn get_voxel_data(&mut self, voxel: &voxel_data::bounds::T) -> Option<VoxelData>;
  }
}

#[allow(missing_docs)]
pub mod edge {
  use cgmath::{Aabb, Point, Point3, Vector, Vector3};
  use voxel_data;

  use super::{voxel_storage, polygon, material};

  #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
  pub enum Direction { X, Y, Z }

  #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  #[allow(missing_docs)]
  pub struct T {
    pub low_corner: Point3<i32>,
    pub lg_size: i16,
    pub direction: Direction,
  }

  fn corner_bounds(edge: &T) -> [voxel_data::bounds::T; 2] {
    let p0 = edge.low_corner;
    let d =
      match edge.direction {
        Direction::X => Vector3::new(1, 0, 0),
        Direction::Y => Vector3::new(0, 1, 0),
        Direction::Z => Vector3::new(0, 0, 1),
      };
    let p1 = p0.add_v(&d);
    [
      voxel_data::bounds::new(p0.x, p0.y, p0.z, edge.lg_size),
      voxel_data::bounds::new(p1.x, p1.y, p1.z, edge.lg_size),
    ]
  }

  fn neighbors(edge: &T) -> [voxel_data::bounds::T; 4] {
    let (v1, v2) =
      match edge.direction {
        Direction::X => (Vector3::new(0, 0, -1), Vector3::new(0, -1, 0)),
        Direction::Y => (Vector3::new(-1, 0, 0), Vector3::new(0, 0, -1)),
        Direction::Z => (Vector3::new(0, -1, 0), Vector3::new(-1, 0, 0)),
      };
    let make_bounds = |p: Point3<i32>| voxel_data::bounds::new(p.x, p.y, p.z, edge.lg_size);
    [
      make_bounds(edge.low_corner),
      make_bounds(edge.low_corner.add_v(&v1)),
      make_bounds(edge.low_corner.add_v(&v1).add_v(&v2)),
      make_bounds(edge.low_corner.add_v(&v2)),
    ]
  }

  enum Crossing<Material> {
    Undefined,
    None,
    LowInside(Material),
    HighInside(Material),
  }

  fn crossing<Material, Voxels>(
    voxels: &mut Voxels,
    edge: &T,
  ) -> Crossing<Material> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
  {
    let [b0, b1] = corner_bounds(edge);
    let material =
      match voxels.get_material(&b0) {
        None => return Crossing::Undefined,
        Some(m) => m,
      };
    let neighbor_material =
      match voxels.get_material(&b1) {
        None => return Crossing::Undefined,
        Some(m) => m,
      };
    if material == neighbor_material {
      Crossing::None
    } else if material.is_opaque() {
      Crossing::LowInside(material)
    } else {
      Crossing::HighInside(neighbor_material)
    }
  }

  fn resolve_voxels<'a, Material, Voxels, It>(
    voxels: &mut Voxels,
    bounds: It,
  ) -> Result<Vec<(Point3<f32>, Vector3<f32>)>, ()> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
    It: Iterator<Item=&'a voxel_data::bounds::T>,
  {
    let mut resolved_voxel_data = Vec::new();
    let mut resolved_bounds: Vec<voxel_data::bounds::T> = Vec::new();

    'resolve_loop: for bounds in bounds {
      for resolved_bounds in &resolved_bounds {
        if resolved_bounds.contains(&bounds) {
          continue 'resolve_loop;
        }
      }

      let voxel_data =
        match voxels.get_voxel_data(&bounds) {
          None => return Err(()),
          Some(d) => d,
        };
      resolved_bounds.push(voxel_data.bounds);
      resolved_voxel_data.push((voxel_data.vertex, voxel_data.normal));
    }

    Ok(resolved_voxel_data)
  }

  /// Run dual contouring on a single edge
  // TODO: Return an iterator
  pub fn extract<Material, Voxels, OnPolygon>(
    voxels: &mut Voxels,
    edge: &T,
    mut poly: OnPolygon,
  ) -> Result<(), ()> where
    Material: material::T + Clone,
    Voxels: voxel_storage::T<Material>,
    OnPolygon: FnMut(polygon::T<Material>),
  {
    let (material, vertices_and_normals) =
      match crossing(voxels, edge) {
        Crossing::Undefined => return Err(()),
        Crossing::None => return Ok(()),
        Crossing::HighInside(material) => {
          (material, try!(resolve_voxels(voxels, neighbors(&edge).iter())))
        },
        Crossing::LowInside(material) => {
          (material, try!(resolve_voxels(voxels, neighbors(&edge).iter().rev())))
        }
      };

    if vertices_and_normals.len() == 3 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      poly(
        polygon::T {
          vertices: [v0, v1, v2],
          normals: [n0, n1, n2],
          material: material,
        }
      );
    } else if vertices_and_normals.len() == 4 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      let (v3, n3) = vertices_and_normals[3];
      let v_center =
        v0.add_v(&v1.to_vec()).add_v(&v2.to_vec()).add_v(&v3.to_vec()).div_s(4.0);
      let n_center =
        n0.add_v(&n1).add_v(&n2).add_v(&n3).div_s(4.0);
      poly(
        polygon::T {
          vertices: [v0, v1, v_center],
          normals: [n0, n1, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v1, v2, v_center],
          normals: [n1, n2, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v2, v3, v_center],
          normals: [n2, n3, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v3, v0, v_center],
          normals: [n3, n0, n_center],
          material: material.clone(),
        }
      );
    } else {
      panic!("Edge has an unexpected number of neighbors: {}", vertices_and_normals.len());
    }

    Ok(())
  }
}
<commit_msg>bugfix: polys were generated in transition between two opaque materials<commit_after>//! An implementation of something like dual contouring.
//! Dual contouring examines the voxel grid and looks for edges that cross between different material types.
//! If at least one of the materials is non-opaque, polygons are generated by using vertex data from the adjacent voxels.
//! See the SIGGRAPH paper for more details, but note that this probably isn't precisely a dual contouring implementation.

mod voxel {
  pub use voxel_data::impls::surface_vertex::T::*;
  pub use voxel_data::impls::surface_vertex::*;
}

#[allow(missing_docs)]
pub mod material {
  /// This trait provides the functionality required from materials for this algorithm.
  pub trait T : Eq {
    /// Is this material opaque?
    fn is_opaque(&self) -> bool;
  }
}

#[allow(missing_docs)]
pub mod polygon {
  use cgmath::{Point3, Vector3};

  /// The polygon type produced by this algorithm.
  pub struct T<Material> {
    #[allow(missing_docs)]
    pub vertices: [Point3<f32>; 3],
    #[allow(missing_docs)]
    pub normals: [Vector3<f32>; 3],
    #[allow(missing_docs)]
    pub material: Material,
  }
}

#[allow(missing_docs)]
pub mod voxel_storage {
  use cgmath::{Point3, Vector3};
  use voxel_data;

  use super::material;

  #[allow(missing_docs)]
  pub struct VoxelData {
    pub bounds: voxel_data::bounds::T,
    pub vertex: Point3<f32>,
    pub normal: Vector3<f32>,
  }

  /// The voxel storage interface required by dual contouring.
  pub trait T<Material> where Material: material::T {
    #[allow(missing_docs)]
    fn get_material(&mut self, voxel: &voxel_data::bounds::T) -> Option<Material>;
    /// Get the data for the given voxel. This function may also return data from a larger encompassing voxel.
    fn get_voxel_data(&mut self, voxel: &voxel_data::bounds::T) -> Option<VoxelData>;
  }
}

#[allow(missing_docs)]
pub mod edge {
  use cgmath::{Aabb, Point, Point3, Vector, Vector3};
  use voxel_data;

  use super::{voxel_storage, polygon, material};

  #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
  pub enum Direction { X, Y, Z }

  #[derive(Debug, Clone, PartialEq, Eq, Hash)]
  #[allow(missing_docs)]
  pub struct T {
    pub low_corner: Point3<i32>,
    pub lg_size: i16,
    pub direction: Direction,
  }

  fn corner_bounds(edge: &T) -> [voxel_data::bounds::T; 2] {
    let p0 = edge.low_corner;
    let d =
      match edge.direction {
        Direction::X => Vector3::new(1, 0, 0),
        Direction::Y => Vector3::new(0, 1, 0),
        Direction::Z => Vector3::new(0, 0, 1),
      };
    let p1 = p0.add_v(&d);
    [
      voxel_data::bounds::new(p0.x, p0.y, p0.z, edge.lg_size),
      voxel_data::bounds::new(p1.x, p1.y, p1.z, edge.lg_size),
    ]
  }

  fn neighbors(edge: &T) -> [voxel_data::bounds::T; 4] {
    let (v1, v2) =
      match edge.direction {
        Direction::X => (Vector3::new(0, 0, -1), Vector3::new(0, -1, 0)),
        Direction::Y => (Vector3::new(-1, 0, 0), Vector3::new(0, 0, -1)),
        Direction::Z => (Vector3::new(0, -1, 0), Vector3::new(-1, 0, 0)),
      };
    let make_bounds = |p: Point3<i32>| voxel_data::bounds::new(p.x, p.y, p.z, edge.lg_size);
    [
      make_bounds(edge.low_corner),
      make_bounds(edge.low_corner.add_v(&v1)),
      make_bounds(edge.low_corner.add_v(&v1).add_v(&v2)),
      make_bounds(edge.low_corner.add_v(&v2)),
    ]
  }

  enum Crossing<Material> {
    Undefined,
    None,
    LowInside(Material),
    HighInside(Material),
  }

  fn crossing<Material, Voxels>(
    voxels: &mut Voxels,
    edge: &T,
  ) -> Crossing<Material> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
  {
    let [b0, b1] = corner_bounds(edge);
    let material =
      match voxels.get_material(&b0) {
        None => return Crossing::Undefined,
        Some(m) => m,
      };
    let neighbor_material =
      match voxels.get_material(&b1) {
        None => return Crossing::Undefined,
        Some(m) => m,
      };
    if (material == neighbor_material) ||
       (material.is_opaque() && neighbor_material.is_opaque()) {
      Crossing::None
    } else if material.is_opaque() {
      Crossing::LowInside(material)
    } else {
      Crossing::HighInside(neighbor_material)
    }
  }

  fn resolve_voxels<'a, Material, Voxels, It>(
    voxels: &mut Voxels,
    bounds: It,
  ) -> Result<Vec<(Point3<f32>, Vector3<f32>)>, ()> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
    It: Iterator<Item=&'a voxel_data::bounds::T>,
  {
    let mut resolved_voxel_data = Vec::new();
    let mut resolved_bounds: Vec<voxel_data::bounds::T> = Vec::new();

    'resolve_loop: for bounds in bounds {
      for resolved_bounds in &resolved_bounds {
        if resolved_bounds.contains(&bounds) {
          continue 'resolve_loop;
        }
      }

      let voxel_data =
        match voxels.get_voxel_data(&bounds) {
          None => return Err(()),
          Some(d) => d,
        };
      resolved_bounds.push(voxel_data.bounds);
      resolved_voxel_data.push((voxel_data.vertex, voxel_data.normal));
    }

    Ok(resolved_voxel_data)
  }

  /// Run dual contouring on a single edge
  // TODO: Return an iterator
  pub fn extract<Material, Voxels, OnPolygon>(
    voxels: &mut Voxels,
    edge: &T,
    mut poly: OnPolygon,
  ) -> Result<(), ()> where
    Material: material::T + Clone,
    Voxels: voxel_storage::T<Material>,
    OnPolygon: FnMut(polygon::T<Material>),
  {
    let (material, vertices_and_normals) =
      match crossing(voxels, edge) {
        Crossing::Undefined => return Err(()),
        Crossing::None => return Ok(()),
        Crossing::HighInside(material) => {
          (material, try!(resolve_voxels(voxels, neighbors(&edge).iter())))
        },
        Crossing::LowInside(material) => {
          (material, try!(resolve_voxels(voxels, neighbors(&edge).iter().rev())))
        }
      };

    if vertices_and_normals.len() == 3 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      poly(
        polygon::T {
          vertices: [v0, v1, v2],
          normals: [n0, n1, n2],
          material: material,
        }
      );
    } else if vertices_and_normals.len() == 4 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      let (v3, n3) = vertices_and_normals[3];
      let v_center =
        v0.add_v(&v1.to_vec()).add_v(&v2.to_vec()).add_v(&v3.to_vec()).div_s(4.0);
      let n_center =
        n0.add_v(&n1).add_v(&n2).add_v(&n3).div_s(4.0);
      poly(
        polygon::T {
          vertices: [v0, v1, v_center],
          normals: [n0, n1, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v1, v2, v_center],
          normals: [n1, n2, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v2, v3, v_center],
          normals: [n2, n3, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v3, v0, v_center],
          normals: [n3, n0, n_center],
          material: material.clone(),
        }
      );
    } else {
      panic!("Edge has an unexpected number of neighbors: {}", vertices_and_normals.len());
    }

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


<commit_before>
use {
    BackEnd,
    Clear,
    Borrowed,
    Field,
    Fill,
    Image,
    Value,
};
use triangulation::{
    with_ellipse_tri_list_xy_f32_rgba_f32
};
use internal::{
    CanColor,
    CanRectangle,
    CanTransform,
    CanViewTransform,
    Color,
    HasColor,
    HasRectangle,
    HasTransform,
    HasViewTransform,
    Matrix2d,
    Rectangle,
};

/// An ellipse color context.
pub struct EllipseColorContext<'a> {
    /// View transformation.
    pub view: Field<'a, Matrix2d>,
    /// Current transformation.
    pub transform: Field<'a, Matrix2d>,
    /// Current rectangle.
    pub rect: Field<'a, Rectangle>,
    /// Current color.
    pub color: Field<'a, Color>,
}

impl<'a> Clone for EllipseColorContext<'a> {
    #[inline(always)]
    fn clone(&self) -> EllipseColorContext<'static> {
        EllipseColorContext {
            view: Value(*self.view.get()),
            transform: Value(*self.transform.get()),
            rect: Value(*self.rect.get()),
            color: Value(*self.color.get()),
        }
    }
}

impl<'a> HasTransform<'a, Matrix2d> for EllipseColorContext<'a> {
    #[inline(always)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a> CanTransform<'a, EllipseColorContext<'a>, Matrix2d> for EllipseColorContext<'a> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> EllipseColorContext<'a> {
        EllipseColorContext {
            view: Borrowed(self.view.get()),
            transform: Value(value),
            rect: Borrowed(self.rect.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> HasViewTransform<'a, Matrix2d> for EllipseColorContext<'a> {
    #[inline(always)]
    fn get_view_transform(&'a self) -> &'a Matrix2d {
        self.view.get()
    }
}

impl<'a> CanViewTransform<'a, EllipseColorContext<'a>, Matrix2d>
for EllipseColorContext<'a> {
    #[inline(always)]
    fn view_transform(&'a self, value: Matrix2d) -> EllipseColorContext<'a> {
        EllipseColorContext {
            view: Value(value),
            transform: Borrowed(self.transform.get()),
            rect: Borrowed(self.rect.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> HasColor<'a, Color> for EllipseColorContext<'a> {
    #[inline(always)]
    fn get_color(&'a self) -> &'a Color {
        self.color.get()
    }
}

impl<'a> CanColor<'a, EllipseColorContext<'a>, Color> for EllipseColorContext<'a> {
    #[inline(always)]
    fn color(&'a self, value: Color) -> EllipseColorContext<'a> {
        EllipseColorContext {
            view: Borrowed(self.view.get()),
            transform: Borrowed(self.transform.get()),
            color: Value(value),
            rect: Borrowed(self.rect.get()),
        }
    }
}

impl<'a> HasRectangle<'a, Rectangle> for EllipseColorContext<'a> {
    #[inline(always)]
    fn get_rectangle(&'a self) -> &'a Rectangle {
        self.rect.get()
    }
}

impl<'a> CanRectangle<'a, EllipseColorContext<'a>, Rectangle> for EllipseColorContext<'a> {
    #[inline(always)]
    fn rectangle(&'a self, rect: Rectangle) -> EllipseColorContext<'a> {
        EllipseColorContext {
            view: Borrowed(self.view.get()),
            transform: Borrowed(self.transform.get()),
            rect: Value(rect),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> Fill<'a> for EllipseColorContext<'a> {
    #[inline(always)]
    fn fill<B: BackEnd<I>, I: Image>(&'a self, back_end: &mut B) {
        if back_end.supports_tri_list_xy_f32_rgba_f32() {
            let rect = self.rect.get();
            let color = self.color.get();
            // Complete transparency does not need to be rendered.
            if color[3] == 0.0 { return; }
            // Turn on alpha blending if not completely opaque.
            let needs_alpha = color[3] != 1.0;
            if needs_alpha { back_end.enable_alpha_blend(); }
            with_ellipse_tri_list_xy_f32_rgba_f32(
                128,
                *self.transform.get(),
                *rect,
                *color,
                |vertices, colors| {
                    back_end.tri_list_xy_f32_rgba_f32(vertices, colors)
                }
            );
            if needs_alpha { back_end.disable_alpha_blend(); }
        } else {
            unimplemented!();
        }
    }
}

impl<'a, B: BackEnd<I>, I: Image> Clear<B, I> for EllipseColorContext<'a> {
    #[inline(always)]
    fn clear(&self, back_end: &mut B) {
        if back_end.supports_clear_rgba() {
            let color = self.color.get();
            back_end.clear_rgba(color[0], color[1], color[2], color[3]);
        } else {
            unimplemented!();
        }
    }
}

<commit_msg>Implemented `AddBorder` for `EllipseColorContext`<commit_after>
use {
    AddBorder,
    BackEnd,
    Clear,
    Borrowed,
    EllipseBorderColorContext,
    Field,
    Fill,
    Image,
    Value,
};
use triangulation::{
    with_ellipse_tri_list_xy_f32_rgba_f32
};
use internal::{
    CanColor,
    CanRectangle,
    CanTransform,
    CanViewTransform,
    Color,
    HasColor,
    HasRectangle,
    HasTransform,
    HasViewTransform,
    Matrix2d,
    Rectangle,
};

/// An ellipse color context.
pub struct EllipseColorContext<'a> {
    /// View transformation.
    pub view: Field<'a, Matrix2d>,
    /// Current transformation.
    pub transform: Field<'a, Matrix2d>,
    /// Current rectangle.
    pub rect: Field<'a, Rectangle>,
    /// Current color.
    pub color: Field<'a, Color>,
}

impl<'a> Clone for EllipseColorContext<'a> {
    #[inline(always)]
    fn clone(&self) -> EllipseColorContext<'static> {
        EllipseColorContext {
            view: Value(*self.view.get()),
            transform: Value(*self.transform.get()),
            rect: Value(*self.rect.get()),
            color: Value(*self.color.get()),
        }
    }
}

impl<'a> HasTransform<'a, Matrix2d> for EllipseColorContext<'a> {
    #[inline(always)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a> CanTransform<'a, EllipseColorContext<'a>, Matrix2d> for EllipseColorContext<'a> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> EllipseColorContext<'a> {
        EllipseColorContext {
            view: Borrowed(self.view.get()),
            transform: Value(value),
            rect: Borrowed(self.rect.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> HasViewTransform<'a, Matrix2d> for EllipseColorContext<'a> {
    #[inline(always)]
    fn get_view_transform(&'a self) -> &'a Matrix2d {
        self.view.get()
    }
}

impl<'a> CanViewTransform<'a, EllipseColorContext<'a>, Matrix2d>
for EllipseColorContext<'a> {
    #[inline(always)]
    fn view_transform(&'a self, value: Matrix2d) -> EllipseColorContext<'a> {
        EllipseColorContext {
            view: Value(value),
            transform: Borrowed(self.transform.get()),
            rect: Borrowed(self.rect.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> HasColor<'a, Color> for EllipseColorContext<'a> {
    #[inline(always)]
    fn get_color(&'a self) -> &'a Color {
        self.color.get()
    }
}

impl<'a> CanColor<'a, EllipseColorContext<'a>, Color> for EllipseColorContext<'a> {
    #[inline(always)]
    fn color(&'a self, value: Color) -> EllipseColorContext<'a> {
        EllipseColorContext {
            view: Borrowed(self.view.get()),
            transform: Borrowed(self.transform.get()),
            color: Value(value),
            rect: Borrowed(self.rect.get()),
        }
    }
}

impl<'a> HasRectangle<'a, Rectangle> for EllipseColorContext<'a> {
    #[inline(always)]
    fn get_rectangle(&'a self) -> &'a Rectangle {
        self.rect.get()
    }
}

impl<'a> CanRectangle<'a, EllipseColorContext<'a>, Rectangle> for EllipseColorContext<'a> {
    #[inline(always)]
    fn rectangle(&'a self, rect: Rectangle) -> EllipseColorContext<'a> {
        EllipseColorContext {
            view: Borrowed(self.view.get()),
            transform: Borrowed(self.transform.get()),
            rect: Value(rect),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> Fill<'a> for EllipseColorContext<'a> {
    #[inline(always)]
    fn fill<B: BackEnd<I>, I: Image>(&'a self, back_end: &mut B) {
        if back_end.supports_tri_list_xy_f32_rgba_f32() {
            let rect = self.rect.get();
            let color = self.color.get();
            // Complete transparency does not need to be rendered.
            if color[3] == 0.0 { return; }
            // Turn on alpha blending if not completely opaque.
            let needs_alpha = color[3] != 1.0;
            if needs_alpha { back_end.enable_alpha_blend(); }
            with_ellipse_tri_list_xy_f32_rgba_f32(
                128,
                *self.transform.get(),
                *rect,
                *color,
                |vertices, colors| {
                    back_end.tri_list_xy_f32_rgba_f32(vertices, colors)
                }
            );
            if needs_alpha { back_end.disable_alpha_blend(); }
        } else {
            unimplemented!();
        }
    }
}

impl<'a, B: BackEnd<I>, I: Image> Clear<B, I> for EllipseColorContext<'a> {
    #[inline(always)]
    fn clear(&self, back_end: &mut B) {
        if back_end.supports_clear_rgba() {
            let color = self.color.get();
            back_end.clear_rgba(color[0], color[1], color[2], color[3]);
        } else {
            unimplemented!();
        }
    }
}

impl<'a> AddBorder<'a, EllipseBorderColorContext<'a>> for EllipseColorContext<'a> {
    #[inline(always)]
    fn border_radius(&'a self, radius: f64) -> EllipseBorderColorContext<'a> {
        EllipseBorderColorContext {
            view: Borrowed(self.view.get()),
            transform: Borrowed(self.transform.get()),
            rect: Borrowed(self.rect.get()),
            color: Borrowed(self.color.get()),
            border: Value(radius),
        }
    }
}

<|endoftext|>


<commit_before>extern crate eom;
extern crate ndarray;
#[macro_use]
extern crate ndarray_linalg;

use ndarray::*;
use ndarray_linalg::*;

use eom::*;
use eom::traits::*;
use eom::jacobian::*;

#[test]
fn jacobian_linearity() {
    let eom = ode::Lorenz63::default();
    let mut teo = explicit::RK4::new(eom, 0.01);
    let x0 = arr1(&[1.0, 0.0, 0.0]);
    let mut j = teo.lin_approx(x0, 1e-7);
    let v: Array1<f64> = generate::random(3);
    let w: Array1<f64> = generate::random(3);
    let j_z = j.apply(&v + &w);
    let jv_jw = j.apply(v) + j.apply(w);
    assert_close_l2!(&jv_jw, &j_z, 1e-5);
}

#[test]
fn jacobian_view() {
    let eom = ode::Lorenz63::default();
    let mut teo = explicit::RK4::new(eom, 0.01);
    let x0 = arr1(&[1.0, 0.0, 0.0]);
    let mut j = teo.lin_approx(x0, 1e-7);
    let mut v: Array1<f64> = generate::random(3);
    j.apply_inplace(&mut v.view_mut());
}

#[test]
fn jacobian_2d() {
    let eom = ode::Lorenz63::default();
    let mut teo = explicit::RK4::new(eom, 0.01);
    let x0 = arr1(&[1.0, 0.0, 0.0]);
    let mut j = teo.lin_approx(x0, 1e-7);
    let v: Array2<f64> = generate::random((3, 2));
    j.apply_multi(v);
}
<commit_msg>s/jacobian/lyapunov/g<commit_after>extern crate eom;
extern crate ndarray;
#[macro_use]
extern crate ndarray_linalg;

use ndarray::*;
use ndarray_linalg::*;

use eom::*;
use eom::traits::*;
use eom::lyapunov::*;

#[test]
fn jacobian_linearity() {
    let eom = ode::Lorenz63::default();
    let mut teo = explicit::RK4::new(eom, 0.01);
    let x0 = arr1(&[1.0, 0.0, 0.0]);
    let mut j = teo.lin_approx(x0, 1e-7);
    let v: Array1<f64> = generate::random(3);
    let w: Array1<f64> = generate::random(3);
    let j_z = j.apply(&v + &w);
    let jv_jw = j.apply(v) + j.apply(w);
    assert_close_l2!(&jv_jw, &j_z, 1e-5);
}

#[test]
fn jacobian_view() {
    let eom = ode::Lorenz63::default();
    let mut teo = explicit::RK4::new(eom, 0.01);
    let x0 = arr1(&[1.0, 0.0, 0.0]);
    let mut j = teo.lin_approx(x0, 1e-7);
    let mut v: Array1<f64> = generate::random(3);
    j.apply_inplace(&mut v.view_mut());
}

#[test]
fn jacobian_2d() {
    let eom = ode::Lorenz63::default();
    let mut teo = explicit::RK4::new(eom, 0.01);
    let x0 = arr1(&[1.0, 0.0, 0.0]);
    let mut j = teo.lin_approx(x0, 1e-7);
    let v: Array2<f64> = generate::random((3, 2));
    j.apply_multi(v);
}
<|endoftext|>