<commit_before>mod serialize;

use self::serialize::{Media, MediaDate, MediaListEntry, MediaStatus, ScoreFormat, User};
use super::{AnimeEntry, AnimeInfo, ScoreParser, Status, SyncBackend};
use config::Config;
use error::BackendError;
use input;
use process;
use reqwest::header::{Accept, Authorization, Bearer, ContentType, Headers};
use reqwest::{Client, Response};
use serde_json as json;
use std::borrow::Cow;

const LOGIN_URL: &str =
    "https://anilist.co/api/v2/oauth/authorize?client_id=427&response_type=token";

const API_URL: &str = "https://graphql.anilist.co";

macro_rules! send_query {
    ($backend:expr, $query_str:expr, {$($vars:tt)*}, $($response_root:expr)=>*) => {{
        let vars = json!({
            $($vars)*
        });

        $backend.send_json_request($query_str, &vars).map(|json| json$([$response_root])*.clone())
    }};
}

pub struct AniList {
    client: Client,
    user: User,
    access_token: String,
}

impl AniList {
    fn send_request(
        &self,
        query_str: &str,
        variables: &json::Value,
    ) -> Result<Response, BackendError> {
        let body = json!({
            "query": query_str,
            "variables": variables,
        }).to_string();

        let mut headers = Headers::new();
        headers.set(ContentType::json());
        headers.set(Accept::json());
        headers.set(Authorization(Bearer {
            token: self.access_token.to_owned(),
        }));

        let response = self
            .client
            .post(API_URL)
            .headers(headers)
            .body(body)
            .send()?;

        Ok(response)
    }

    fn send_json_request(
        &self,
        query_str: &str,
        variables: &json::Value,
    ) -> Result<json::Value, BackendError> {
        let text = self.send_request(query_str, variables)?.text()?;
        let json: json::Value = json::from_str(&text)?;

        if json["errors"] != json::Value::Null {
            // TODO: add error chaining
            let err = &json["errors"][0];

            let msg = err["message"].as_str().unwrap_or("unknown error");
            let status_code = err["status"].as_u64().unwrap_or(0) as u32;

            return Err(BackendError::BadResponse(status_code, msg.into()));
        }

        Ok(json)
    }

    fn request_user_info(&self) -> Result<User, BackendError> {
        let resp = send_query!(self,
            r#"
                query {
                    Viewer {
                        id
                        mediaListOptions {
                            scoreFormat
                        }
                    }
                }
            "#,
            {},
            "data" => "Viewer"
        )?;

        let user = json::from_value(resp)?;
        Ok(user)
    }

    fn prompt_for_access_token(open_url: bool) -> Result<String, BackendError> {
        if open_url {
            try_open_url(LOGIN_URL);
        }

        println!(
            "please authorize your account in the opened browser tab and paste the code below:"
        );

        let token = input::read_line()?;
        Ok(token)
    }

    fn login(&mut self, is_first_launch: bool, config: &mut Config) -> Result<(), BackendError> {
        let mut times_token_incorrect = 0;

        loop {
            match self.request_user_info() {
                Ok(user) => {
                    self.user = user;
                    break;
                }
                // As bad as checking for a specific error via its message is, the API does not provide
                // anything else to narrow it down to an invalid token error
                Err(BackendError::BadResponse(400, ref msg))
                    if msg.to_lowercase() == "invalid token" =>
                {
                    times_token_incorrect += 1;
                    println!("\ninvalid access token");

                    let should_open_url = !is_first_launch && times_token_incorrect <= 1;
                    let token = AniList::prompt_for_access_token(should_open_url)?;

                    self.access_token = token;
                }
                Err(err) => return Err(err),
            }
        }

        if times_token_incorrect > 0 {
            config.anilist.token.encode(&self.access_token);
        }

        Ok(())
    }
}

impl SyncBackend for AniList {
    fn name() -> &'static str {
        "AniList"
    }

    fn init(config: &mut Config) -> Result<AniList, BackendError> {
        let is_first_launch = !config.anilist.token.is_set();

        let access_token = if is_first_launch {
            let token = AniList::prompt_for_access_token(true)?;
            config.anilist.token.encode(&token);
            token
        } else {
            config.anilist.token.decode()?
        };

        let mut anilist = AniList {
            client: Client::new(),
            user: User::default(),
            access_token,
        };

        anilist.login(is_first_launch, config)?;

        Ok(anilist)
    }

    fn search_by_name(&self, name: &str) -> Result<Vec<AnimeInfo>, BackendError> {
        let resp = send_query!(self,
            r#"
                query ($name: String) {
                    Page (page: 1, perPage: 30) {
                        media (search: $name, type: ANIME) {
                            id
                            title {
                                romaji
                            }
                            episodes
                        }
                    }
                }
            "#,
            { "name": name },
            "data" => "Page" => "media"
        )?;

        use self::json::Value;
        let mut series = Vec::new();

        match resp {
            Value::Array(ref entries) => {
                for entry in entries {
                    let series_info: Media = json::from_value(entry.clone())?;
                    series.push(series_info.into());
                }
            }
            _ => return Err(BackendError::InvalidJsonResponse),
        }

        Ok(series)
    }

    fn get_series_info_by_id(&self, id: u32) -> Result<AnimeInfo, BackendError> {
        let resp = send_query!(self,
            r#"
                query ($id: Int) {
                    Media (id: $id) {
                        id
                        title {
                            romaji
                        }
                        episodes
                    }
                }
            "#,
            { "id": id },
            "data" => "Media"
        )?;

        let info: Media = json::from_value(resp)?;
        Ok(info.into())
    }

    fn get_list_entry(&self, info: AnimeInfo) -> Result<Option<AnimeEntry>, BackendError> {
        let resp = send_query!(self,
            r#"
                query ($id: Int, $userID: Int) {
                    MediaList(mediaId: $id, userId: $userID) {
                        progress
                        status
                        score
                        startedAt {
                            year
                            month
                            day
                        }
                        completedAt {
                            year
                            month
                            day
                        }
                    }
                }
            "#,
            { "id": info.id, "userID": self.user.id },
            "data" => "MediaList"
        );

        match resp {
            Ok(entry_json) => {
                let media_entry: MediaListEntry = json::from_value(entry_json)?;
                Ok(Some(media_entry.into_generic_entry(info)))
            }
            Err(BackendError::BadResponse(404, _)) => Ok(None),
            Err(err) => Err(err),
        }
    }

    fn update_list_entry(&self, entry: &AnimeEntry) -> Result<(), BackendError> {
        send_query!(self,
            r#"
                mutation ($mediaId: Int, $watched_eps: Int, $score: Float, $status: MediaListStatus, $start_date: FuzzyDateInput, $finish_date: FuzzyDateInput) {
                    SaveMediaListEntry (mediaId: $mediaId, progress: $watched_eps, score: $score, status: $status, startedAt: $start_date, completedAt: $finish_date) {
                        mediaId
                    }
                }
            "#,
            {
                "mediaId": entry.info.id,
                "watched_eps": entry.watched_episodes,
                "score": entry.score.unwrap_or(0.0),
                "status": MediaStatus::from(entry.status),
                "start_date": MediaDate::from_date(entry.start_date),
                "finish_date": MediaDate::from_date(entry.finish_date),
            },
        )?;

        Ok(())
    }
}

impl ScoreParser for AniList {
    fn formatted_score_range(&self) -> (Cow<str>, Cow<str>) {
        match self.user.list_options.score_format {
            ScoreFormat::Point3 => (":(".into(), ":)".into()),
            ScoreFormat::Point10Decimal => ("1.0".into(), "10.0".into()),
            format => ("1".into(), format.max_score().to_string().into()),
        }
    }

    fn parse_score(&self, input: &str) -> Result<f32, BackendError> {
        match self.user.list_options.score_format {
            ScoreFormat::Point3 => match input {
                ":(" => Ok(1.0),
                ":|" => Ok(2.0),
                ":)" => Ok(3.0),
                _ => Err(BackendError::UnknownScoreValue(input.into())),
            },
            format => {
                let value = input.parse::<f32>()?;
                let max_score = f32::from(format.max_score());

                if value < 1.0 || value > max_score {
                    return Err(BackendError::OutOfRangeScore);
                }

                Ok(value)
            }
        }
    }

    fn format_score(&self, raw_score: f32) -> Result<String, BackendError> {
        match self.user.list_options.score_format {
            ScoreFormat::Point3 => match raw_score.round() as u32 {
                1 => Ok(":(".into()),
                2 => Ok(":|".into()),
                3 => Ok(":)".into()),
                _ => Err(BackendError::OutOfRangeScore),
            },
            ScoreFormat::Point5 | ScoreFormat::Point10 | ScoreFormat::Point100 => {
                let value = raw_score.round() as u32;
                Ok(value.to_string())
            }
            ScoreFormat::Point10Decimal => Ok(raw_score.to_string()),
        }
    }
}

fn try_open_url(url: &str) {
    match process::open_with_default(url) {
        Ok(status) if status.success() => (),
        result => {
            eprintln!(
                "failed to open URL in default browser. please open it manually: {}",
                url
            );

            if let Err(err) = result {
                eprintln!("error message: {}", err);
            }
        }
    }
}
<commit_msg>[AniList] Improve formatting of list update query<commit_after>mod serialize;

use self::serialize::{Media, MediaDate, MediaListEntry, MediaStatus, ScoreFormat, User};
use super::{AnimeEntry, AnimeInfo, ScoreParser, Status, SyncBackend};
use config::Config;
use error::BackendError;
use input;
use process;
use reqwest::header::{Accept, Authorization, Bearer, ContentType, Headers};
use reqwest::{Client, Response};
use serde_json as json;
use std::borrow::Cow;

const LOGIN_URL: &str =
    "https://anilist.co/api/v2/oauth/authorize?client_id=427&response_type=token";

const API_URL: &str = "https://graphql.anilist.co";

macro_rules! send_query {
    ($backend:expr, $query_str:expr, {$($vars:tt)*}, $($response_root:expr)=>*) => {{
        let vars = json!({
            $($vars)*
        });

        $backend.send_json_request($query_str, &vars).map(|json| json$([$response_root])*.clone())
    }};
}

pub struct AniList {
    client: Client,
    user: User,
    access_token: String,
}

impl AniList {
    fn send_request(
        &self,
        query_str: &str,
        variables: &json::Value,
    ) -> Result<Response, BackendError> {
        let body = json!({
            "query": query_str,
            "variables": variables,
        }).to_string();

        let mut headers = Headers::new();
        headers.set(ContentType::json());
        headers.set(Accept::json());
        headers.set(Authorization(Bearer {
            token: self.access_token.to_owned(),
        }));

        let response = self
            .client
            .post(API_URL)
            .headers(headers)
            .body(body)
            .send()?;

        Ok(response)
    }

    fn send_json_request(
        &self,
        query_str: &str,
        variables: &json::Value,
    ) -> Result<json::Value, BackendError> {
        let text = self.send_request(query_str, variables)?.text()?;
        let json: json::Value = json::from_str(&text)?;

        if json["errors"] != json::Value::Null {
            // TODO: add error chaining
            let err = &json["errors"][0];

            let msg = err["message"].as_str().unwrap_or("unknown error");
            let status_code = err["status"].as_u64().unwrap_or(0) as u32;

            return Err(BackendError::BadResponse(status_code, msg.into()));
        }

        Ok(json)
    }

    fn request_user_info(&self) -> Result<User, BackendError> {
        let resp = send_query!(self,
            r#"
                query {
                    Viewer {
                        id
                        mediaListOptions {
                            scoreFormat
                        }
                    }
                }
            "#,
            {},
            "data" => "Viewer"
        )?;

        let user = json::from_value(resp)?;
        Ok(user)
    }

    fn prompt_for_access_token(open_url: bool) -> Result<String, BackendError> {
        if open_url {
            try_open_url(LOGIN_URL);
        }

        println!(
            "please authorize your account in the opened browser tab and paste the code below:"
        );

        let token = input::read_line()?;
        Ok(token)
    }

    fn login(&mut self, is_first_launch: bool, config: &mut Config) -> Result<(), BackendError> {
        let mut times_token_incorrect = 0;

        loop {
            match self.request_user_info() {
                Ok(user) => {
                    self.user = user;
                    break;
                }
                // As bad as checking for a specific error via its message is, the API does not provide
                // anything else to narrow it down to an invalid token error
                Err(BackendError::BadResponse(400, ref msg))
                    if msg.to_lowercase() == "invalid token" =>
                {
                    times_token_incorrect += 1;
                    println!("\ninvalid access token");

                    let should_open_url = !is_first_launch && times_token_incorrect <= 1;
                    let token = AniList::prompt_for_access_token(should_open_url)?;

                    self.access_token = token;
                }
                Err(err) => return Err(err),
            }
        }

        if times_token_incorrect > 0 {
            config.anilist.token.encode(&self.access_token);
        }

        Ok(())
    }
}

impl SyncBackend for AniList {
    fn name() -> &'static str {
        "AniList"
    }

    fn init(config: &mut Config) -> Result<AniList, BackendError> {
        let is_first_launch = !config.anilist.token.is_set();

        let access_token = if is_first_launch {
            let token = AniList::prompt_for_access_token(true)?;
            config.anilist.token.encode(&token);
            token
        } else {
            config.anilist.token.decode()?
        };

        let mut anilist = AniList {
            client: Client::new(),
            user: User::default(),
            access_token,
        };

        anilist.login(is_first_launch, config)?;

        Ok(anilist)
    }

    fn search_by_name(&self, name: &str) -> Result<Vec<AnimeInfo>, BackendError> {
        let resp = send_query!(self,
            r#"
                query ($name: String) {
                    Page (page: 1, perPage: 30) {
                        media (search: $name, type: ANIME) {
                            id
                            title {
                                romaji
                            }
                            episodes
                        }
                    }
                }
            "#,
            { "name": name },
            "data" => "Page" => "media"
        )?;

        use self::json::Value;
        let mut series = Vec::new();

        match resp {
            Value::Array(ref entries) => {
                for entry in entries {
                    let series_info: Media = json::from_value(entry.clone())?;
                    series.push(series_info.into());
                }
            }
            _ => return Err(BackendError::InvalidJsonResponse),
        }

        Ok(series)
    }

    fn get_series_info_by_id(&self, id: u32) -> Result<AnimeInfo, BackendError> {
        let resp = send_query!(self,
            r#"
                query ($id: Int) {
                    Media (id: $id) {
                        id
                        title {
                            romaji
                        }
                        episodes
                    }
                }
            "#,
            { "id": id },
            "data" => "Media"
        )?;

        let info: Media = json::from_value(resp)?;
        Ok(info.into())
    }

    fn get_list_entry(&self, info: AnimeInfo) -> Result<Option<AnimeEntry>, BackendError> {
        let resp = send_query!(self,
            r#"
                query ($id: Int, $userID: Int) {
                    MediaList(mediaId: $id, userId: $userID) {
                        progress
                        status
                        score
                        startedAt {
                            year
                            month
                            day
                        }
                        completedAt {
                            year
                            month
                            day
                        }
                    }
                }
            "#,
            { "id": info.id, "userID": self.user.id },
            "data" => "MediaList"
        );

        match resp {
            Ok(entry_json) => {
                let media_entry: MediaListEntry = json::from_value(entry_json)?;
                Ok(Some(media_entry.into_generic_entry(info)))
            }
            Err(BackendError::BadResponse(404, _)) => Ok(None),
            Err(err) => Err(err),
        }
    }

    fn update_list_entry(&self, entry: &AnimeEntry) -> Result<(), BackendError> {
        send_query!(self,
            r#"
                mutation (
                    $mediaId: Int,
                    $watched_eps: Int,
                    $score: Float,
                    $status: MediaListStatus,
                    $start_date: FuzzyDateInput,
                    $finish_date: FuzzyDateInput) {

                    SaveMediaListEntry (
                        mediaId: $mediaId,
                        progress: $watched_eps,
                        score: $score,
                        status: $status,
                        startedAt: $start_date,
                        completedAt: $finish_date) {

                        mediaId
                    }
                }
            "#,
            {
                "mediaId": entry.info.id,
                "watched_eps": entry.watched_episodes,
                "score": entry.score.unwrap_or(0.0),
                "status": MediaStatus::from(entry.status),
                "start_date": MediaDate::from_date(entry.start_date),
                "finish_date": MediaDate::from_date(entry.finish_date),
            },
        )?;

        Ok(())
    }
}

impl ScoreParser for AniList {
    fn formatted_score_range(&self) -> (Cow<str>, Cow<str>) {
        match self.user.list_options.score_format {
            ScoreFormat::Point3 => (":(".into(), ":)".into()),
            ScoreFormat::Point10Decimal => ("1.0".into(), "10.0".into()),
            format => ("1".into(), format.max_score().to_string().into()),
        }
    }

    fn parse_score(&self, input: &str) -> Result<f32, BackendError> {
        match self.user.list_options.score_format {
            ScoreFormat::Point3 => match input {
                ":(" => Ok(1.0),
                ":|" => Ok(2.0),
                ":)" => Ok(3.0),
                _ => Err(BackendError::UnknownScoreValue(input.into())),
            },
            format => {
                let value = input.parse::<f32>()?;
                let max_score = f32::from(format.max_score());

                if value < 1.0 || value > max_score {
                    return Err(BackendError::OutOfRangeScore);
                }

                Ok(value)
            }
        }
    }

    fn format_score(&self, raw_score: f32) -> Result<String, BackendError> {
        match self.user.list_options.score_format {
            ScoreFormat::Point3 => match raw_score.round() as u32 {
                1 => Ok(":(".into()),
                2 => Ok(":|".into()),
                3 => Ok(":)".into()),
                _ => Err(BackendError::OutOfRangeScore),
            },
            ScoreFormat::Point5 | ScoreFormat::Point10 | ScoreFormat::Point100 => {
                let value = raw_score.round() as u32;
                Ok(value.to_string())
            }
            ScoreFormat::Point10Decimal => Ok(raw_score.to_string()),
        }
    }
}

fn try_open_url(url: &str) {
    match process::open_with_default(url) {
        Ok(status) if status.success() => (),
        result => {
            eprintln!(
                "failed to open URL in default browser. please open it manually: {}",
                url
            );

            if let Err(err) = result {
                eprintln!("error message: {}", err);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::{PackageIdSpec, Workspace};
use ops;
use util::CargoResult;

pub struct DocOptions<'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a>,
}

pub fn doc(ws: &Workspace,
           options: &DocOptions) -> CargoResult<()> {
    let package = try!(ws.current());

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if options.compile_opts.spec.is_empty() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.crate_name()));
            } else {
                assert!(bin_names.insert(target.crate_name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                bail!("cannot document a package where a library and a binary \
                       have the same name. Consider renaming one or marking \
                       the target as `doc = false`")
            }
        }
    }

    try!(ops::compile(ws, &options.compile_opts));

    if options.open_result {
        let name = if options.compile_opts.spec.len() > 1 {
            bail!("Passing multiple packages and `open` is not supported")
        } else if options.compile_opts.spec.len() == 1 {
            try!(PackageIdSpec::parse(&options.compile_opts.spec[0]))
                                             .name().replace("-", "_")
        } else {
            match lib_names.iter().chain(bin_names.iter()).nth(0) {
                Some(s) => s.to_string(),
                None => return Ok(())
            }
        };

        // Don't bother locking here as if this is getting deleted there's
        // nothing we can do about it and otherwise if it's getting overwritten
        // then that's also ok!
        let target_dir = ws.target_dir();
        let path = target_dir.join("doc").join(&name).join("index.html");
        let path = path.into_path_unlocked();
        if fs::metadata(&path).is_ok() {
            let mut shell = options.compile_opts.config.shell();
            match open_docs(&path) {
                Ok(m) => try!(shell.status("Launching", m)),
                Err(e) => {
                    try!(shell.warn(
                            "warning: could not determine a browser to open docs with, tried:"));
                    for method in e {
                        try!(shell.warn(format!("\t{}", method)));
                    }
                }
            }
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    use std::env;
    let mut methods = Vec::new();
    // trying $BROWSER
    match env::var("BROWSER"){
        Ok(name) => match Command::new(name).arg(path).status() {
            Ok(_) => return Ok("$BROWSER"),
            Err(_) => methods.push("$BROWSER")
        },
        Err(_) => () // Do nothing here if $BROWSER is not found
    }

    for m in ["xdg-open", "gnome-open", "kde-open"].iter() {
        match Command::new(m).arg(path).status() {
            Ok(_) => return Ok(m),
            Err(_) => methods.push(m)
        }
    }

    Err(methods)
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("cmd").arg("/C").arg(path).status() {
        Ok(_) => return Ok("cmd /C"),
        Err(_) => return Err(vec!["cmd /C"])
    };
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("open").arg(path).status() {
        Ok(_) => return Ok("open"),
        Err(_) => return Err(vec!["open"])
    };
}
<commit_msg>Auto merge of #2988 - lifthrasiir:doc-open-with-target, r=alexcrichton<commit_after>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::{PackageIdSpec, Workspace};
use ops;
use util::CargoResult;

pub struct DocOptions<'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a>,
}

pub fn doc(ws: &Workspace,
           options: &DocOptions) -> CargoResult<()> {
    let package = try!(ws.current());

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if options.compile_opts.spec.is_empty() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.crate_name()));
            } else {
                assert!(bin_names.insert(target.crate_name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                bail!("cannot document a package where a library and a binary \
                       have the same name. Consider renaming one or marking \
                       the target as `doc = false`")
            }
        }
    }

    try!(ops::compile(ws, &options.compile_opts));

    if options.open_result {
        let name = if options.compile_opts.spec.len() > 1 {
            bail!("Passing multiple packages and `open` is not supported")
        } else if options.compile_opts.spec.len() == 1 {
            try!(PackageIdSpec::parse(&options.compile_opts.spec[0]))
                                             .name().replace("-", "_")
        } else {
            match lib_names.iter().chain(bin_names.iter()).nth(0) {
                Some(s) => s.to_string(),
                None => return Ok(())
            }
        };

        // Don't bother locking here as if this is getting deleted there's
        // nothing we can do about it and otherwise if it's getting overwritten
        // then that's also ok!
        let mut target_dir = ws.target_dir();
        if let Some(triple) = options.compile_opts.target {
            target_dir.push(Path::new(triple).file_stem().unwrap());
        }
        let path = target_dir.join("doc").join(&name).join("index.html");
        let path = path.into_path_unlocked();
        if fs::metadata(&path).is_ok() {
            let mut shell = options.compile_opts.config.shell();
            match open_docs(&path) {
                Ok(m) => try!(shell.status("Launching", m)),
                Err(e) => {
                    try!(shell.warn(
                            "warning: could not determine a browser to open docs with, tried:"));
                    for method in e {
                        try!(shell.warn(format!("\t{}", method)));
                    }
                }
            }
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    use std::env;
    let mut methods = Vec::new();
    // trying $BROWSER
    match env::var("BROWSER"){
        Ok(name) => match Command::new(name).arg(path).status() {
            Ok(_) => return Ok("$BROWSER"),
            Err(_) => methods.push("$BROWSER")
        },
        Err(_) => () // Do nothing here if $BROWSER is not found
    }

    for m in ["xdg-open", "gnome-open", "kde-open"].iter() {
        match Command::new(m).arg(path).status() {
            Ok(_) => return Ok(m),
            Err(_) => methods.push(m)
        }
    }

    Err(methods)
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("cmd").arg("/C").arg(path).status() {
        Ok(_) => return Ok("cmd /C"),
        Err(_) => return Err(vec!["cmd /C"])
    };
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("open").arg(path).status() {
        Ok(_) => return Ok("open"),
        Err(_) => return Err(vec!["open"])
    };
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use common = super::super::shade;
use super::gl;

pub fn create_object(stage: common::Stage, data: &[u8]) -> (Option<super::Shader>, String) {
    let target = match stage {
        common::Vertex => gl::VERTEX_SHADER,
        common::Geometry => gl::GEOMETRY_SHADER,
        common::Fragment => gl::FRAGMENT_SHADER,
    };
    let name = gl::CreateShader(target);
    let mut length = data.len() as gl::types::GLint;
    unsafe {
        gl::ShaderSource(name, 1, &(data.as_ptr() as *gl::types::GLchar), &length);
    }
    gl::CompileShader(name);
    info!("\tCompiled shader {}", name);
    // get info message
    let mut status = 0 as gl::types::GLint;
    length = 0;
    unsafe {
        gl::GetShaderiv(name, gl::COMPILE_STATUS,  &mut status);
        gl::GetShaderiv(name, gl::INFO_LOG_LENGTH, &mut length);
    }
    let mut info = String::with_capacity(length as uint);
    info.grow(length as uint, 0u8 as char);
    unsafe {
        gl::GetShaderInfoLog(name, length, &mut length,
            info.as_slice().as_ptr() as *mut gl::types::GLchar);
    }
    info.truncate(length as uint);
    (if status != 0 {Some(name)} else {None}, info)
}

fn query_program_int(prog: super::Program, query: gl::types::GLenum) -> gl::types::GLint {
    let mut ret = 0 as gl::types::GLint;
    unsafe {
        gl::GetProgramiv(prog, query, &mut ret);
    }
    ret
}

fn derive_attribute(storage: gl::types::GLenum) -> common::VarType {
    match storage {
        // float vecs
        gl::FLOAT =>
            common::Vector(common::BaseFloat, 1),
        gl::FLOAT_VEC2 | gl::FLOAT_VEC3 | gl::FLOAT_VEC4 =>
            common::Vector(common::BaseFloat, (storage+2-gl::FLOAT_VEC2) as u8),
        // int vecs
        gl::INT =>
            common::Vector(common::BaseInt, 1),
        gl::INT_VEC2 | gl::INT_VEC3 | gl::INT_VEC4 =>
            common::Vector(common::BaseInt, (storage+2-gl::INT_VEC2) as u8),
        // unsigned vecs
        gl::UNSIGNED_INT =>
            common::Vector(common::BaseUnsigned, 1),
        gl::UNSIGNED_INT_VEC2 | gl::UNSIGNED_INT_VEC3 | gl::UNSIGNED_INT_VEC4 =>
            common::Vector(common::BaseUnsigned, (storage+2-gl::UNSIGNED_INT_VEC2) as u8),
        // bool vecs
        gl::BOOL =>
            common::Vector(common::BaseBool, 1),
        gl::BOOL_VEC2 | gl::BOOL_VEC3 | gl::BOOL_VEC4 =>
            common::Vector(common::BaseBool, (storage+2-gl::BOOL_VEC2) as u8),
        // float matrices
        gl::FLOAT_MAT2 | gl::FLOAT_MAT3 | gl::FLOAT_MAT4 => {
            let dim = (storage+2-gl::FLOAT_MAT2) as u8;
            common::Matrix(common::ColumnMajor, false, dim, dim)
        },
        gl::FLOAT_MAT2x3 =>
            common::Matrix(common::ColumnMajor, false, 2, 3),
        gl::FLOAT_MAT2x4 =>
            common::Matrix(common::ColumnMajor, false, 2, 4),
        gl::FLOAT_MAT3x2 =>
            common::Matrix(common::ColumnMajor, false, 3, 2),
        gl::FLOAT_MAT3x4 =>
            common::Matrix(common::ColumnMajor, false, 3, 4),
        gl::FLOAT_MAT4x2 =>
            common::Matrix(common::ColumnMajor, false, 4, 2),
        gl::FLOAT_MAT4x3 =>
            common::Matrix(common::ColumnMajor, false, 4, 3),
        // double matrices //TODO
        // unknown
        _ => {
            error!("Unrecognized attribute storage: {}", storage);
            common::Vector(common::BaseFloat, 0)
        }
    }
}

fn query_attributes(prog: super::Program) -> Vec<common::Attribute> {
    let num     = query_program_int(prog, gl::ACTIVE_ATTRIBUTES);
    let max_len = query_program_int(prog, gl::ACTIVE_ATTRIBUTE_MAX_LENGTH);
    let mut name = String::with_capacity(max_len as uint);
    name.grow(max_len as uint, 0u8 as char);
    range(0, num).map(|i| {
        let mut length = 0 as gl::types::GLint;
        let mut size = 0 as gl::types::GLint;
        let mut storage = 0 as gl::types::GLenum;
        let loc = unsafe {
            let raw = name.as_slice().as_ptr() as *mut gl::types::GLchar;
            gl::GetActiveAttrib(prog, i as gl::types::GLuint,
                max_len, &mut length, &mut size, &mut storage, raw);
            gl::GetAttribLocation(prog, raw as *gl::types::GLchar)
        };
        info!("\t\tAttrib[{}] = '{}',\tformat = 0x{:x}", loc, name, storage);
        common::Attribute {
            name: name.clone(),
            location: loc as uint,
            count: size as uint,
            var_type: derive_attribute(storage),
        }
    }).collect()
}

pub fn create_program(shaders: &[super::Shader]) -> (Option<common::ProgramMeta>, String) {
    let name = gl::CreateProgram();
    for &sh in shaders.iter() {
        gl::AttachShader(name, sh);
    }
    gl::LinkProgram(name);
    info!("\tLinked program {}", name);
    // get info message
    let status      = query_program_int(name, gl::LINK_STATUS);
    let mut length  = query_program_int(name, gl::INFO_LOG_LENGTH);
    let mut info = String::with_capacity(length as uint);
    info.grow(length as uint, 0u8 as char);
    unsafe {
        gl::GetProgramInfoLog(name, length, &mut length,
            info.as_slice().as_ptr() as *mut gl::types::GLchar);
    }
    info.truncate(length as uint);
    (if status != 0 {
        let meta = common::ProgramMeta {
            name: name,
            attributes: query_attributes(name),
            uniforms: Vec::new(),   //TODO
            blocks: Vec::new(),     //TODO
            textures: Vec::new(),   //TODO
        };
        Some(meta)
    }else {
        None
    }, info)
}
<commit_msg>Implemented uniform block queries<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use common = super::super::shade;
use super::gl;
use std::cell::Cell;

pub fn create_object(stage: common::Stage, data: &[u8]) -> (Option<super::Shader>, String) {
    let target = match stage {
        common::Vertex => gl::VERTEX_SHADER,
        common::Geometry => gl::GEOMETRY_SHADER,
        common::Fragment => gl::FRAGMENT_SHADER,
    };
    let name = gl::CreateShader(target);
    let mut length = data.len() as gl::types::GLint;
    unsafe {
        gl::ShaderSource(name, 1, &(data.as_ptr() as *gl::types::GLchar), &length);
    }
    gl::CompileShader(name);
    info!("\tCompiled shader {}", name);
    // get info message
    let mut status = 0 as gl::types::GLint;
    length = 0;
    unsafe {
        gl::GetShaderiv(name, gl::COMPILE_STATUS,  &mut status);
        gl::GetShaderiv(name, gl::INFO_LOG_LENGTH, &mut length);
    }
    let mut info = String::with_capacity(length as uint);
    info.grow(length as uint, 0u8 as char);
    unsafe {
        gl::GetShaderInfoLog(name, length, &mut length,
            info.as_slice().as_ptr() as *mut gl::types::GLchar);
    }
    info.truncate(length as uint);
    (if status != 0 {Some(name)} else {None}, info)
}

fn query_program_int(prog: super::Program, query: gl::types::GLenum) -> gl::types::GLint {
    let mut ret = 0 as gl::types::GLint;
    unsafe {
        gl::GetProgramiv(prog, query, &mut ret);
    }
    ret
}

fn derive_attribute(storage: gl::types::GLenum) -> common::VarType {
    match storage {
        // float vecs
        gl::FLOAT =>
            common::Vector(common::BaseFloat, 1),
        gl::FLOAT_VEC2 | gl::FLOAT_VEC3 | gl::FLOAT_VEC4 =>
            common::Vector(common::BaseFloat, (storage+2-gl::FLOAT_VEC2) as u8),
        // int vecs
        gl::INT =>
            common::Vector(common::BaseInt, 1),
        gl::INT_VEC2 | gl::INT_VEC3 | gl::INT_VEC4 =>
            common::Vector(common::BaseInt, (storage+2-gl::INT_VEC2) as u8),
        // unsigned vecs
        gl::UNSIGNED_INT =>
            common::Vector(common::BaseUnsigned, 1),
        gl::UNSIGNED_INT_VEC2 | gl::UNSIGNED_INT_VEC3 | gl::UNSIGNED_INT_VEC4 =>
            common::Vector(common::BaseUnsigned, (storage+2-gl::UNSIGNED_INT_VEC2) as u8),
        // bool vecs
        gl::BOOL =>
            common::Vector(common::BaseBool, 1),
        gl::BOOL_VEC2 | gl::BOOL_VEC3 | gl::BOOL_VEC4 =>
            common::Vector(common::BaseBool, (storage+2-gl::BOOL_VEC2) as u8),
        // float matrices
        gl::FLOAT_MAT2 | gl::FLOAT_MAT3 | gl::FLOAT_MAT4 => {
            let dim = (storage+2-gl::FLOAT_MAT2) as u8;
            common::Matrix(common::ColumnMajor, false, dim, dim)
        },
        gl::FLOAT_MAT2x3 =>
            common::Matrix(common::ColumnMajor, false, 2, 3),
        gl::FLOAT_MAT2x4 =>
            common::Matrix(common::ColumnMajor, false, 2, 4),
        gl::FLOAT_MAT3x2 =>
            common::Matrix(common::ColumnMajor, false, 3, 2),
        gl::FLOAT_MAT3x4 =>
            common::Matrix(common::ColumnMajor, false, 3, 4),
        gl::FLOAT_MAT4x2 =>
            common::Matrix(common::ColumnMajor, false, 4, 2),
        gl::FLOAT_MAT4x3 =>
            common::Matrix(common::ColumnMajor, false, 4, 3),
        // double matrices //TODO
        // unknown
        _ => {
            error!("Unrecognized attribute storage: {}", storage);
            common::Vector(common::BaseFloat, 0)
        }
    }
}

fn query_attributes(prog: super::Program) -> Vec<common::Attribute> {
    let num     = query_program_int(prog, gl::ACTIVE_ATTRIBUTES);
    let max_len = query_program_int(prog, gl::ACTIVE_ATTRIBUTE_MAX_LENGTH);
    let mut name = String::with_capacity(max_len as uint);
    name.grow(max_len as uint, 0u8 as char);
    range(0, num).map(|i| {
        let mut length = 0 as gl::types::GLint;
        let mut size = 0 as gl::types::GLint;
        let mut storage = 0 as gl::types::GLenum;
        let loc = unsafe {
            let raw = name.as_slice().as_ptr() as *mut gl::types::GLchar;
            gl::GetActiveAttrib(prog, i as gl::types::GLuint,
                max_len, &mut length, &mut size, &mut storage, raw);
            gl::GetAttribLocation(prog, raw as *gl::types::GLchar)
        };
        info!("\t\tAttrib[{}] = '{}',\tformat = 0x{:x}", loc, name, storage);
        common::Attribute {
            name: name.as_slice().slice_to(length as uint).to_string(),
            location: loc as uint,
            count: size as uint,
            var_type: derive_attribute(storage),
        }
    }).collect()
}

fn query_blocks(prog: super::Program) -> Vec<common::BlockVar> {
    let num     = query_program_int(prog, gl::ACTIVE_UNIFORM_BLOCKS);
    range(0, num as gl::types::GLuint).map(|i| {
        let mut length  = 0 as gl::types::GLint;
        let mut size    = 0 as gl::types::GLint;
        let mut tmp     = 0 as gl::types::GLint;
        let mut usage = 0u8;
        unsafe {
            gl::GetActiveUniformBlockiv(prog, i, gl::UNIFORM_BLOCK_NAME_LENGTH, &mut size);
            for (j, &eval) in [gl::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER,
                    gl::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER].iter().enumerate() {
                gl::GetActiveUniformBlockiv(prog, i, eval, &mut tmp);
                if tmp != 0 {usage |= 1<<i;}
            }
        }
        let mut name = String::with_capacity(size as uint); //includes terminating null
        name.grow(size as uint, 0u8 as char);
        let mut actual_name_size = 0 as gl::types::GLint;
        unsafe {
            gl::GetActiveUniformBlockName(prog, i, size, &mut actual_name_size,
                name.as_slice().as_ptr() as *mut gl::types::GLchar);
            assert_eq!(actual_name_size+1, size);
            gl::GetActiveUniformBlockiv(prog, i, gl::UNIFORM_BLOCK_DATA_SIZE, &mut size);
        }
        info!("\t\tBlock '{}' of size {}", name, size);
        common::BlockVar {
            name: name,
            size: size as uint,
            usage: usage,
            active_slot: Cell::new(0),
        }
    }).collect()
}

fn query_parameters(prog: super::Program) -> (Vec<common::UniformVar>, Vec<common::SamplerVar>) {
    let mut uniforms = Vec::new();
    let mut textures = Vec::new();

    (uniforms, textures)
}

pub fn create_program(shaders: &[super::Shader]) -> (Option<common::ProgramMeta>, String) {
    let name = gl::CreateProgram();
    for &sh in shaders.iter() {
        gl::AttachShader(name, sh);
    }
    gl::LinkProgram(name);
    info!("\tLinked program {}", name);
    // get info message
    let status      = query_program_int(name, gl::LINK_STATUS);
    let mut length  = query_program_int(name, gl::INFO_LOG_LENGTH);
    let mut info = String::with_capacity(length as uint);
    info.grow(length as uint, 0u8 as char);
    unsafe {
        gl::GetProgramInfoLog(name, length, &mut length,
            info.as_slice().as_ptr() as *mut gl::types::GLchar);
    }
    info.truncate(length as uint);
    (if status != 0 {
        let (uniforms, textures) = query_parameters(name);
        let meta = common::ProgramMeta {
            name: name,
            attributes: query_attributes(name),
            uniforms: uniforms,   //TODO
            blocks: query_blocks(name),     //TODO
            textures: textures,   //TODO
        };
        Some(meta)
    }else {
        None
    }, info)
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::marker::PhantomData;
use std::ops::{Add, Deref, DerefMut, Mul, Sub};

use graph::geometry::{AsPosition, Cross, Geometry, Normalize};
use graph::mesh::{Face, Mesh};
use graph::storage::{EdgeKey, FaceKey};
use graph::topology::EdgeView;

// TODO: Generalize this pairing of a ref to a mesh and a key for topology
//       within the mesh.

// NOTE: This code assumes that any keys for topological structures in the mesh
//       are valid (hence the `unwrap` calls), which is very important for
//       `Deref`. Topological mutations using views like `FaceView` are
//       dangerous if they do not consume `self`. If these views can be used to
//       mutate that data, then they can also invalidate these constraints and
//       cause panics.

pub type FaceRef<'a, G> = FaceView<&'a Mesh<G>, G>;
pub type FaceMut<'a, G> = FaceView<&'a mut Mesh<G>, G>;

#[derive(Clone, Copy)]
pub struct FaceView<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    mesh: M,
    pub key: FaceKey,
    phantom: PhantomData<G>,
}

impl<M, G> FaceView<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    pub(crate) fn new(mesh: M, face: FaceKey) -> Self {
        FaceView {
            mesh: mesh,
            key: face,
            phantom: PhantomData,
        }
    }

    pub fn edges(&self) -> EdgeCirculator<&Mesh<G>, G> {
        EdgeCirculator::new(self.with_mesh_ref())
    }

    pub fn faces(&self) -> FaceCirculator<&Mesh<G>, G> {
        FaceCirculator::new(self.with_mesh_ref())
    }

    // Resolve the `M` parameter to a concrete reference.
    fn with_mesh_ref(&self) -> FaceView<&Mesh<G>, G> {
        FaceView::new(self.mesh.as_ref(), self.key)
    }
}

impl<M, G> FaceView<M, G>
where
    M: AsRef<Mesh<G>> + AsMut<Mesh<G>>,
    G: Geometry,
{
    pub fn faces_mut(&mut self) -> FaceCirculator<&mut Mesh<G>, G> {
        FaceCirculator::new(self.with_mesh_mut())
    }

    // Resolve the `M` parameter to a concrete reference.
    fn with_mesh_mut(&mut self) -> FaceView<&mut Mesh<G>, G> {
        FaceView::new(self.mesh.as_mut(), self.key)
    }
}

// The elaborate type constraints state that positions stored in the vertex
// geometry can be used to compute a normal of a face using vector subtraction,
// cross product, normalization, vector addition, and vector scaling.
#[cfg_attr(rustfmt, rustfmt_skip)]
impl<M, G> FaceView<M, G>
where
    M: AsRef<Mesh<G>> + AsMut<Mesh<G>>,
    G: Geometry,
    G::Vertex: AsPosition + Clone,
    // Constraints on the vertex geometry when converted to a position:
    //
    // 1. Supports cloning.
    // 2. Supports subtraction with itself.
    // 3. Supports addition with the output of multiplication with `f64` of the
    //    cross product of subtraction with itself.
    // 4. The output of the above addition is itself.
    <G::Vertex as AsPosition>::Target: Add<<<<<G::Vertex as AsPosition>::Target as Sub>::Output as Cross>::Output as Mul<f64>>::Output, Output = <G::Vertex as AsPosition>::Target> + Clone + Sub,
    // Constraints on the output of subtraction of vertex geometry when
    // converted to a position:
    //
    // 1. Supports the cross product.
    <<G::Vertex as AsPosition>::Target as Sub>::Output: Cross,
    // Constraints on the output of the cross product of subtraction of vertex
    // geometry when converted to a position:
    //
    // 1. Supports multiplication with `f64` and normalization.
    <<<G::Vertex as AsPosition>::Target as Sub>::Output as Cross>::Output: Mul<f64> + Normalize,
    // Constraints on the output of multiplication with `f64` of the cross
    // product of subtraction of vertex geometry when converted to a position:
    //
    // 1. Supports cloning.
    <<<<G::Vertex as AsPosition>::Target as Sub>::Output as Cross>::Output as Mul<f64>>::Output: Clone,
{
    pub fn extrude(self, distance: f64) -> Result<Self, ()> {
        // Collect all the vertex keys of the face along with their translated
        // geometries.
        let vertices = {
            let mesh = self.mesh.as_ref();
            let edges = self.edges().collect::<Vec<_>>();
            if edges.len() < 3 {
                return Err(());
            }
            let (a, b, c) = (
                mesh.vertices.get(&edges[0].vertex).unwrap(),
                mesh.vertices.get(&edges[1].vertex).unwrap(),
                mesh.vertices.get(&edges[2].vertex).unwrap(),
            );
            let ab = a.geometry.as_position().clone() - b.geometry.as_position().clone();
            let bc = b.geometry.as_position().clone() - c.geometry.as_position().clone();
            let translation = ab.cross(bc).normalize() * distance;
            edges.into_iter().map(|edge| {
                let mut geometry = mesh.vertices.get(&edge.vertex).unwrap().geometry.clone();
                *geometry.as_position_mut() = geometry.as_position().clone() + translation.clone();
                (edge.vertex, geometry)
            }).collect::<Vec<_>>()
        };
        // Begin topological mutations, starting by removing the originating
        // face. These mutations invalidate the key used by the `FaceView`, so
        // destructure `self` to prevent its use.
        let FaceView { mut mesh, key, .. } = self;
        mesh.as_mut().remove_face(key).unwrap();
        // Use the keys for the existing vertices and the translated geometries
        // to construct the extruded face and its connective faces.
        let face = {
            let mesh = mesh.as_mut();
            let vertices = vertices.into_iter()
                .map(|vertex| (vertex.0, mesh.insert_vertex(vertex.1))).collect::<Vec<_>>();
            let edges = vertices.iter().enumerate().map(|(index, &(_, a))| {
                let b = vertices[(index + 1) % vertices.len()].1;
                // TODO: Copy the geometry of the edges in the originating face.
                mesh.insert_edge((a, b), G::Edge::default()).unwrap()
            }).collect::<Vec<_>>();
            // TODO: Copy the geometry of the originating face.
            let face = mesh.insert_face(&edges, G::Face::default()).unwrap();
            for index in 0..vertices.len() {
                let (a, b) = vertices[index];
                let (d, c) = vertices[(index + 1) % vertices.len()];
                let ab = mesh.insert_edge((a, b), G::Edge::default()).unwrap();
                let bc = mesh.insert_edge((b, c), G::Edge::default()).unwrap();
                let cd = mesh.insert_edge((c, d), G::Edge::default()).unwrap();
                let da = mesh.insert_edge((d, a), G::Edge::default()).unwrap();
                let ca = mesh.insert_edge((c, a), G::Edge::default()).unwrap(); // Diagonal.
                let ac = mesh.insert_edge((a, c), G::Edge::default()).unwrap(); // Diagonal.
                mesh.insert_face(&[ab, bc, ca], G::Face::default()).unwrap();
                mesh.insert_face(&[ac, cd, da], G::Face::default()).unwrap();
            }
            face
        };
        Ok(FaceView::new(mesh, face))
    }
}

impl<M, G> AsRef<FaceView<M, G>> for FaceView<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    fn as_ref(&self) -> &FaceView<M, G> {
        self
    }
}

impl<M, G> AsMut<FaceView<M, G>> for FaceView<M, G>
where
    M: AsRef<Mesh<G>> + AsMut<Mesh<G>>,
    G: Geometry,
{
    fn as_mut(&mut self) -> &mut FaceView<M, G> {
        self
    }
}

impl<M, G> Deref for FaceView<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    type Target = Face<G::Face>;

    fn deref(&self) -> &Self::Target {
        self.mesh.as_ref().faces.get(&self.key).unwrap()
    }
}

impl<M, G> DerefMut for FaceView<M, G>
where
    M: AsRef<Mesh<G>> + AsMut<Mesh<G>>,
    G: Geometry,
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.mesh.as_mut().faces.get_mut(&self.key).unwrap()
    }
}

pub struct EdgeCirculator<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    face: FaceView<M, G>,
    edge: Option<EdgeKey>,
    breadcrumbs: HashSet<EdgeKey>,
}

impl<M, G> EdgeCirculator<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    fn new(face: FaceView<M, G>) -> Self {
        let edge = face.edge;
        EdgeCirculator {
            face: face,
            edge: Some(edge),
            breadcrumbs: HashSet::with_capacity(3),
        }
    }

    fn next(&mut self) -> Option<EdgeKey> {
        let mesh = self.face.mesh.as_ref();
        if let Some((key, edge)) = self.edge.map(|edge| (edge, mesh.edges.get(&edge).unwrap())) {
            if self.breadcrumbs.contains(&key) {
                return None;
            }
            self.breadcrumbs.insert(key);
            self.edge = edge.next;
            Some(key)
        }
        else {
            None
        }
    }
}

impl<'a, G> Iterator for EdgeCirculator<&'a Mesh<G>, G>
where
    G: Geometry,
{
    type Item = EdgeView<&'a Mesh<G>, G>;

    fn next(&mut self) -> Option<Self::Item> {
        <EdgeCirculator<_, _>>::next(self).map(|edge| EdgeView::new(self.face.mesh, edge))
    }
}

pub struct FaceCirculator<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    face: FaceView<M, G>,
    edge: Option<EdgeKey>,
    breadcrumbs: HashSet<EdgeKey>,
}

impl<M, G> FaceCirculator<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    fn new(face: FaceView<M, G>) -> Self {
        let edge = face.edge;
        FaceCirculator {
            face: face,
            edge: Some(edge),
            breadcrumbs: HashSet::with_capacity(3),
        }
    }

    fn next(&mut self) -> Option<FaceKey> {
        let mesh = self.face.mesh.as_ref();
        while let Some((key, edge)) = self.edge.map(|edge| (edge, mesh.edges.get(&edge).unwrap())) {
            if self.breadcrumbs.contains(&key) {
                return None;
            }
            self.breadcrumbs.insert(key);
            self.edge = edge.next;
            if let Some(face) = edge.opposite
                .map(|opposite| mesh.edges.get(&opposite).unwrap())
                .and_then(|opposite| opposite.face)
            {
                return Some(face);
            }
            else {
                continue;
            }
        }
        None
    }
}

impl<'a, G> Iterator for FaceCirculator<&'a Mesh<G>, G>
where
    G: Geometry,
{
    type Item = FaceView<&'a Mesh<G>, G>;

    fn next(&mut self) -> Option<Self::Item> {
        <FaceCirculator<_, _>>::next(self).map(|face| FaceView::new(self.face.mesh, face))
    }
}

impl<'a, G> Iterator for FaceCirculator<&'a mut Mesh<G>, G>
where
    G: 'a + Geometry,
{
    // TODO: One way to "unify" the output types of this iterator is to
    //       introduce an `OrphanFaceView` that holds a reference to the
    //       geometry but not the mesh. Code working with these structures
    //       would look nearly the same.
    //
    // This cannot be a `FaceView`, because that would alias the mutable
    // reference to the mesh. Instead, yield the key and a mutable reference to
    // the geometry data.
    type Item = (FaceKey, &'a mut G::Face);

    fn next(&mut self) -> Option<Self::Item> {
        <FaceCirculator<_, _>>::next(self).map(|face| {
            let geometry = {
                unsafe {
                    use std::mem;

                    // There is no way to bind the anonymous lifetime of this
                    // function to `Self::Item`. This is problematic for the
                    // call to `get_mut`, which requires autoref. However, this
                    // should be safe, because the use of this iterator
                    // requires a mutable borrow of the source mesh with
                    // lifetime `'a`. Therefore, the (disjoint) geometry data
                    // within the mesh should also be valid over the lifetime
                    // '`a'.
                    let face = mem::transmute::<_, &'a mut Face<G::Face>>(
                        self.face.mesh.faces.get_mut(&face).unwrap(),
                    );
                    &mut face.geometry
                }
            };
            (face, geometry)
        })
    }
}

#[cfg(test)]
mod tests {
    use r32;
    use generate::*;
    use graph::*;

    #[test]
    fn circulate_over_edges() {
        let mesh = sphere::UVSphere::<f32>::with_unit_radius(3, 2)
            .spatial_polygons() // 6 triangles, 18 vertices.
            .map_vertices(|(x, y, z)| (r32::from(x), r32::from(y), r32::from(z)))
            .triangulate()
            .collect::<Mesh<(r32, r32, r32)>>();
        // TODO: Provide a way to get a key for the faces in the mesh. Using
        //       `default` only works if the initial face has not been removed.
        let face = mesh.face(FaceKey::default()).unwrap();

        // All faces should be triangles and should have three edges.
        assert_eq!(3, face.edges().count());
    }

    #[test]
    fn circulate_over_faces() {
        let mesh = sphere::UVSphere::<f32>::with_unit_radius(3, 2)
            .spatial_polygons() // 6 triangles, 18 vertices.
            .map_vertices(|(x, y, z)| (r32::from(x), r32::from(y), r32::from(z)))
            .triangulate()
            .collect::<Mesh<(r32, r32, r32)>>();
        // TODO: Provide a way to get a key for the faces in the mesh. Using
        //       `default` only works if the initial face has not been removed.
        let face = mesh.face(FaceKey::default()).unwrap();

        // No matter which face is selected, it should have three neighbors.
        assert_eq!(3, face.faces().count());
    }
}
<commit_msg>Split `extrude` into two functions.<commit_after>use std::collections::HashSet;
use std::marker::PhantomData;
use std::ops::{Add, Deref, DerefMut, Mul, Sub};

use graph::geometry::{AsPosition, Cross, Geometry, Normalize};
use graph::mesh::{Face, Mesh};
use graph::storage::{EdgeKey, FaceKey, VertexKey};
use graph::topology::EdgeView;

// TODO: Generalize this pairing of a ref to a mesh and a key for topology
//       within the mesh.

// NOTE: This code assumes that any keys for topological structures in the mesh
//       are valid (hence the `unwrap` calls), which is very important for
//       `Deref`. Topological mutations using views like `FaceView` are
//       dangerous if they do not consume `self`. If these views can be used to
//       mutate that data, then they can also invalidate these constraints and
//       cause panics.

pub type FaceRef<'a, G> = FaceView<&'a Mesh<G>, G>;
pub type FaceMut<'a, G> = FaceView<&'a mut Mesh<G>, G>;

#[derive(Clone, Copy)]
pub struct FaceView<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    mesh: M,
    pub key: FaceKey,
    phantom: PhantomData<G>,
}

impl<M, G> FaceView<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    pub(crate) fn new(mesh: M, face: FaceKey) -> Self {
        FaceView {
            mesh: mesh,
            key: face,
            phantom: PhantomData,
        }
    }

    pub fn edges(&self) -> EdgeCirculator<&Mesh<G>, G> {
        EdgeCirculator::new(self.with_mesh_ref())
    }

    pub fn faces(&self) -> FaceCirculator<&Mesh<G>, G> {
        FaceCirculator::new(self.with_mesh_ref())
    }

    // Resolve the `M` parameter to a concrete reference.
    fn with_mesh_ref(&self) -> FaceView<&Mesh<G>, G> {
        FaceView::new(self.mesh.as_ref(), self.key)
    }
}

impl<M, G> FaceView<M, G>
where
    M: AsRef<Mesh<G>> + AsMut<Mesh<G>>,
    G: Geometry,
{
    pub fn faces_mut(&mut self) -> FaceCirculator<&mut Mesh<G>, G> {
        FaceCirculator::new(self.with_mesh_mut())
    }

    // Resolve the `M` parameter to a concrete reference.
    fn with_mesh_mut(&mut self) -> FaceView<&mut Mesh<G>, G> {
        FaceView::new(self.mesh.as_mut(), self.key)
    }
}

// The elaborate type constraints state that positions stored in the vertex
// geometry can be used to compute a normal of a face using vector subtraction,
// cross product, normalization, vector addition, and vector scaling.
#[cfg_attr(rustfmt, rustfmt_skip)]
impl<M, G> FaceView<M, G>
where
    M: AsRef<Mesh<G>> + AsMut<Mesh<G>>,
    G: Geometry,
    G::Vertex: AsPosition + Clone,
    // Constraints on the vertex geometry when converted to a position:
    //
    // 1. Supports cloning.
    // 2. Supports subtraction with itself.
    // 3. Supports addition with the output of multiplication with `f64` of the
    //    cross product of subtraction with itself.
    // 4. The output of the above addition is itself.
    <G::Vertex as AsPosition>::Target: Add<<<<<G::Vertex as AsPosition>::Target as Sub>::Output as Cross>::Output as Mul<f64>>::Output, Output = <G::Vertex as AsPosition>::Target> + Clone + Sub,
    // Constraints on the output of subtraction of vertex geometry when
    // converted to a position:
    //
    // 1. Supports the cross product.
    <<G::Vertex as AsPosition>::Target as Sub>::Output: Cross,
    // Constraints on the output of the cross product of subtraction of vertex
    // geometry when converted to a position:
    //
    // 1. Supports multiplication with `f64` and normalization.
    <<<G::Vertex as AsPosition>::Target as Sub>::Output as Cross>::Output: Mul<f64> + Normalize,
    // Constraints on the output of multiplication with `f64` of the cross
    // product of subtraction of vertex geometry when converted to a position:
    //
    // 1. Supports cloning.
    <<<<G::Vertex as AsPosition>::Target as Sub>::Output as Cross>::Output as Mul<f64>>::Output: Clone,
{
    pub fn extrude(self, distance: f64) -> Result<Self, ()> {
        // Collect all the vertex keys of the face along with their translated
        // geometries.
        let geometry = self.compute_extrusion_geometry(distance)?;
        // Begin topological mutations, starting by removing the originating
        // face. These mutations invalidate the key used by the `FaceView`, so
        // destructure `self` to avoid its reuse.
        let FaceView { mut mesh, key, .. } = self;
        mesh.as_mut().remove_face(key).unwrap();
        // Use the keys for the existing vertices and the translated geometries
        // to construct the extruded face and its connective faces.
        let extrusion = {
            let mesh = mesh.as_mut();
            let vertices = geometry.into_iter()
                .map(|vertex| (vertex.0, mesh.insert_vertex(vertex.1))).collect::<Vec<_>>();
            let edges = vertices.iter().enumerate().map(|(index, &(_, a))| {
                let b = vertices[(index + 1) % vertices.len()].1;
                // TODO: Copy the geometry of the edges in the originating face.
                mesh.insert_edge((a, b), G::Edge::default()).unwrap()
            }).collect::<Vec<_>>();
            // TODO: Copy the geometry of the originating face.
            let extrusion = mesh.insert_face(&edges, G::Face::default()).unwrap();
            for index in 0..vertices.len() {
                let (a, b) = vertices[index];
                let (d, c) = vertices[(index + 1) % vertices.len()];
                let ab = mesh.insert_edge((a, b), G::Edge::default()).unwrap();
                let bc = mesh.insert_edge((b, c), G::Edge::default()).unwrap();
                let cd = mesh.insert_edge((c, d), G::Edge::default()).unwrap();
                let da = mesh.insert_edge((d, a), G::Edge::default()).unwrap();
                let ca = mesh.insert_edge((c, a), G::Edge::default()).unwrap(); // Diagonal.
                let ac = mesh.insert_edge((a, c), G::Edge::default()).unwrap(); // Diagonal.
                mesh.insert_face(&[ab, bc, ca], G::Face::default()).unwrap();
                mesh.insert_face(&[ac, cd, da], G::Face::default()).unwrap();
            }
            extrusion
        };
        Ok(FaceView::new(mesh, extrusion))
    }

    fn compute_extrusion_geometry(&self, distance: f64) -> Result<Vec<(VertexKey, G::Vertex)>, ()> {
        let mesh = self.mesh.as_ref();
        let edges = self.edges().collect::<Vec<_>>();
        if edges.len() < 3 {
            return Err(());
        }
        let (a, b, c) = (
            mesh.vertices.get(&edges[0].vertex).unwrap(),
            mesh.vertices.get(&edges[1].vertex).unwrap(),
            mesh.vertices.get(&edges[2].vertex).unwrap(),
        );
        let ab = a.geometry.as_position().clone() - b.geometry.as_position().clone();
        let bc = b.geometry.as_position().clone() - c.geometry.as_position().clone();
        let translation = ab.cross(bc).normalize() * distance;
        Ok(edges.into_iter().map(|edge| {
            let mut geometry = mesh.vertices.get(&edge.vertex).unwrap().geometry.clone();
            *geometry.as_position_mut() = geometry.as_position().clone() + translation.clone();
            (edge.vertex, geometry)
        }).collect::<Vec<_>>())
    }
}

impl<M, G> AsRef<FaceView<M, G>> for FaceView<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    fn as_ref(&self) -> &FaceView<M, G> {
        self
    }
}

impl<M, G> AsMut<FaceView<M, G>> for FaceView<M, G>
where
    M: AsRef<Mesh<G>> + AsMut<Mesh<G>>,
    G: Geometry,
{
    fn as_mut(&mut self) -> &mut FaceView<M, G> {
        self
    }
}

impl<M, G> Deref for FaceView<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    type Target = Face<G::Face>;

    fn deref(&self) -> &Self::Target {
        self.mesh.as_ref().faces.get(&self.key).unwrap()
    }
}

impl<M, G> DerefMut for FaceView<M, G>
where
    M: AsRef<Mesh<G>> + AsMut<Mesh<G>>,
    G: Geometry,
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.mesh.as_mut().faces.get_mut(&self.key).unwrap()
    }
}

pub struct EdgeCirculator<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    face: FaceView<M, G>,
    edge: Option<EdgeKey>,
    breadcrumbs: HashSet<EdgeKey>,
}

impl<M, G> EdgeCirculator<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    fn new(face: FaceView<M, G>) -> Self {
        let edge = face.edge;
        EdgeCirculator {
            face: face,
            edge: Some(edge),
            breadcrumbs: HashSet::with_capacity(3),
        }
    }

    fn next(&mut self) -> Option<EdgeKey> {
        let mesh = self.face.mesh.as_ref();
        if let Some((key, edge)) = self.edge.map(|edge| (edge, mesh.edges.get(&edge).unwrap())) {
            if self.breadcrumbs.contains(&key) {
                return None;
            }
            self.breadcrumbs.insert(key);
            self.edge = edge.next;
            Some(key)
        }
        else {
            None
        }
    }
}

impl<'a, G> Iterator for EdgeCirculator<&'a Mesh<G>, G>
where
    G: Geometry,
{
    type Item = EdgeView<&'a Mesh<G>, G>;

    fn next(&mut self) -> Option<Self::Item> {
        <EdgeCirculator<_, _>>::next(self).map(|edge| EdgeView::new(self.face.mesh, edge))
    }
}

pub struct FaceCirculator<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    face: FaceView<M, G>,
    edge: Option<EdgeKey>,
    breadcrumbs: HashSet<EdgeKey>,
}

impl<M, G> FaceCirculator<M, G>
where
    M: AsRef<Mesh<G>>,
    G: Geometry,
{
    fn new(face: FaceView<M, G>) -> Self {
        let edge = face.edge;
        FaceCirculator {
            face: face,
            edge: Some(edge),
            breadcrumbs: HashSet::with_capacity(3),
        }
    }

    fn next(&mut self) -> Option<FaceKey> {
        let mesh = self.face.mesh.as_ref();
        while let Some((key, edge)) = self.edge.map(|edge| (edge, mesh.edges.get(&edge).unwrap())) {
            if self.breadcrumbs.contains(&key) {
                return None;
            }
            self.breadcrumbs.insert(key);
            self.edge = edge.next;
            if let Some(face) = edge.opposite
                .map(|opposite| mesh.edges.get(&opposite).unwrap())
                .and_then(|opposite| opposite.face)
            {
                return Some(face);
            }
            else {
                continue;
            }
        }
        None
    }
}

impl<'a, G> Iterator for FaceCirculator<&'a Mesh<G>, G>
where
    G: Geometry,
{
    type Item = FaceView<&'a Mesh<G>, G>;

    fn next(&mut self) -> Option<Self::Item> {
        <FaceCirculator<_, _>>::next(self).map(|face| FaceView::new(self.face.mesh, face))
    }
}

impl<'a, G> Iterator for FaceCirculator<&'a mut Mesh<G>, G>
where
    G: 'a + Geometry,
{
    // TODO: One way to "unify" the output types of this iterator is to
    //       introduce an `OrphanFaceView` that holds a reference to the
    //       geometry but not the mesh. Code working with these structures
    //       would look nearly the same.
    //
    // This cannot be a `FaceView`, because that would alias the mutable
    // reference to the mesh. Instead, yield the key and a mutable reference to
    // the geometry data.
    type Item = (FaceKey, &'a mut G::Face);

    fn next(&mut self) -> Option<Self::Item> {
        <FaceCirculator<_, _>>::next(self).map(|face| {
            let geometry = {
                unsafe {
                    use std::mem;

                    // There is no way to bind the anonymous lifetime of this
                    // function to `Self::Item`. This is problematic for the
                    // call to `get_mut`, which requires autoref. However, this
                    // should be safe, because the use of this iterator
                    // requires a mutable borrow of the source mesh with
                    // lifetime `'a`. Therefore, the (disjoint) geometry data
                    // within the mesh should also be valid over the lifetime
                    // '`a'.
                    let face = mem::transmute::<_, &'a mut Face<G::Face>>(
                        self.face.mesh.faces.get_mut(&face).unwrap(),
                    );
                    &mut face.geometry
                }
            };
            (face, geometry)
        })
    }
}

#[cfg(test)]
mod tests {
    use r32;
    use generate::*;
    use graph::*;

    #[test]
    fn circulate_over_edges() {
        let mesh = sphere::UVSphere::<f32>::with_unit_radius(3, 2)
            .spatial_polygons() // 6 triangles, 18 vertices.
            .map_vertices(|(x, y, z)| (r32::from(x), r32::from(y), r32::from(z)))
            .triangulate()
            .collect::<Mesh<(r32, r32, r32)>>();
        // TODO: Provide a way to get a key for the faces in the mesh. Using
        //       `default` only works if the initial face has not been removed.
        let face = mesh.face(FaceKey::default()).unwrap();

        // All faces should be triangles and should have three edges.
        assert_eq!(3, face.edges().count());
    }

    #[test]
    fn circulate_over_faces() {
        let mesh = sphere::UVSphere::<f32>::with_unit_radius(3, 2)
            .spatial_polygons() // 6 triangles, 18 vertices.
            .map_vertices(|(x, y, z)| (r32::from(x), r32::from(y), r32::from(z)))
            .triangulate()
            .collect::<Mesh<(r32, r32, r32)>>();
        // TODO: Provide a way to get a key for the faces in the mesh. Using
        //       `default` only works if the initial face has not been removed.
        let face = mesh.face(FaceKey::default()).unwrap();

        // No matter which face is selected, it should have three neighbors.
        assert_eq!(3, face.faces().count());
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use llvm;
use builder::Builder;
use llvm::{BasicBlockRef, ValueRef};
use common::*;
use rustc::ty::Ty;

pub fn slice_for_each<'a, 'tcx, F>(
    bcx: &Builder<'a, 'tcx>,
    data_ptr: ValueRef,
    unit_ty: Ty<'tcx>,
    len: ValueRef,
    f: F
) -> Builder<'a, 'tcx> where F: FnOnce(&Builder<'a, 'tcx>, ValueRef, BasicBlockRef) {
    // Special-case vectors with elements of size 0  so they don't go out of bounds (#9890)
    let zst = type_is_zero_size(bcx.ccx, unit_ty);
    let add = |bcx: &Builder, a, b| if zst {
        bcx.add(a, b)
    } else {
        bcx.inbounds_gep(a, &[b])
    };

    let body_bcx = bcx.build_sibling_block("slice_loop_body");
    let next_bcx = bcx.build_sibling_block("slice_loop_next");
    let header_bcx = bcx.build_sibling_block("slice_loop_header");

    let start = if zst {
        C_uint(bcx.ccx, 0usize)
    } else {
        data_ptr
    };
    let end = add(&bcx, start, len);

    bcx.br(header_bcx.llbb());
    let current = header_bcx.phi(val_ty(start), &[start], &[bcx.llbb()]);

    let keep_going = header_bcx.icmp(llvm::IntNE, current, end);
    header_bcx.cond_br(keep_going, body_bcx.llbb(), next_bcx.llbb());

    let next = add(&body_bcx, current, C_uint(bcx.ccx, 1usize));
    f(&body_bcx, if zst { data_ptr } else { current }, header_bcx.llbb());
    header_bcx.add_incoming_to_phi(current, next, body_bcx.llbb());
    next_bcx
}
<commit_msg>trans: Reorder basic blocks in slice_for_each<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use llvm;
use builder::Builder;
use llvm::{BasicBlockRef, ValueRef};
use common::*;
use rustc::ty::Ty;

pub fn slice_for_each<'a, 'tcx, F>(
    bcx: &Builder<'a, 'tcx>,
    data_ptr: ValueRef,
    unit_ty: Ty<'tcx>,
    len: ValueRef,
    f: F
) -> Builder<'a, 'tcx> where F: FnOnce(&Builder<'a, 'tcx>, ValueRef, BasicBlockRef) {
    // Special-case vectors with elements of size 0  so they don't go out of bounds (#9890)
    let zst = type_is_zero_size(bcx.ccx, unit_ty);
    let add = |bcx: &Builder, a, b| if zst {
        bcx.add(a, b)
    } else {
        bcx.inbounds_gep(a, &[b])
    };

    let body_bcx = bcx.build_sibling_block("slice_loop_body");
    let header_bcx = bcx.build_sibling_block("slice_loop_header");
    let next_bcx = bcx.build_sibling_block("slice_loop_next");

    let start = if zst {
        C_uint(bcx.ccx, 0usize)
    } else {
        data_ptr
    };
    let end = add(&bcx, start, len);

    bcx.br(header_bcx.llbb());
    let current = header_bcx.phi(val_ty(start), &[start], &[bcx.llbb()]);

    let keep_going = header_bcx.icmp(llvm::IntNE, current, end);
    header_bcx.cond_br(keep_going, body_bcx.llbb(), next_bcx.llbb());

    let next = add(&body_bcx, current, C_uint(bcx.ccx, 1usize));
    f(&body_bcx, if zst { data_ptr } else { current }, header_bcx.llbb());
    header_bcx.add_incoming_to_phi(current, next, body_bcx.llbb());
    next_bcx
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#[macro_use]
extern crate criterion;
extern crate rand;
extern crate rav1e;

mod predict;
mod transform;
mod me;

use criterion::*;
use rav1e::cdef::cdef_filter_frame;
use rav1e::context::*;
use rav1e::ec;
use rav1e::partition::*;
use rav1e::predict::*;
use rav1e::rdo::rdo_cfl_alpha;
use rav1e::*;

use transform::transform;

#[cfg(feature = "comparative_bench")]
mod comparative;

fn write_b(c: &mut Criterion) {
  for &tx_size in &[TxSize::TX_4X4, TxSize::TX_8X8] {
    for &qi in &[20, 55] {
      let n = format!("write_b_bench({:?}, {})", tx_size, qi);
      c.bench_function(&n, move |b| write_b_bench(b, tx_size, qi));
    }
  }
}

fn write_b_bench(b: &mut Bencher, tx_size: TxSize, qindex: usize) {
  #[cfg(feature = "aom")]
  unsafe {
    av1_rtcd();
    aom_dsp_rtcd();
  }
  let config =
    EncoderConfig { quantizer: qindex, speed: 10, ..Default::default() };
  let mut fi = FrameInvariants::new(1024, 1024, config);
  let mut w = ec::WriterEncoder::new();
  let fc = CDFContext::new(fi.base_q_idx);
  let bc = BlockContext::new(fi.sb_width * 16, fi.sb_height * 16);
  let mut fs = FrameState::new(&fi);
  // For now, restoration unit size is locked to superblock size.
  let rc = RestorationContext::new(fi.sb_width, fi.sb_height);
  let mut cw = ContextWriter::new(fc, bc, rc);

  let tx_type = TxType::DCT_DCT;

  let sbx = 0;
  let sby = 0;
  let ac = &[0i16; 32 * 32];

  b.iter(|| {
    for &mode in RAV1E_INTRA_MODES {
      let sbo = SuperBlockOffset { x: sbx, y: sby };
      fs.qc.update(fi.base_q_idx, tx_size, mode.is_intra(), 8);
      for p in 1..3 {
        for by in 0..8 {
          for bx in 0..8 {
            // For ex, 8x8 tx should be applied to even numbered (bx,by)
            if (tx_size.width_mi() >> 1) & bx != 0
              || (tx_size.height_mi() >> 1) & by != 0
            {
              continue;
            };
            let bo = sbo.block_offset(bx, by);
            let tx_bo = BlockOffset { x: bo.x + bx, y: bo.y + by };
            let po = tx_bo.plane_offset(&fs.input.planes[p].cfg);
            encode_tx_block(
              &mut fi,
              &mut fs,
              &mut cw,
              &mut w,
              p,
              &bo,
              mode,
              tx_size,
              tx_type,
              tx_size.block_size(),
              &po,
              false,
              8,
              ac,
              0,
              false
            );
          }
        }
      }
    }
  });
}

fn cdef_frame(c: &mut Criterion) {
  let w = 128;
  let h = 128;
  let n = format!("cdef_frame({}, {})", w, h);
  c.bench_function(&n, move |b| cdef_frame_bench(b, w, h));
}

fn cdef_frame_bench(b: &mut Bencher, w: usize, h: usize) {
  let config =
    EncoderConfig { quantizer: 100, speed: 10, ..Default::default() };
  let fi = FrameInvariants::new(w, h, config);
  let mut bc = BlockContext::new(fi.sb_width * 16, fi.sb_height * 16);
  let mut fs = FrameState::new(&fi);

  b.iter(|| cdef_filter_frame(&fi, &mut fs.rec, &mut bc, 8));
}

fn cfl_rdo(c: &mut Criterion) {
  for &bsize in &[
    BlockSize::BLOCK_4X4,
    BlockSize::BLOCK_8X8,
    BlockSize::BLOCK_16X16,
    BlockSize::BLOCK_32X32
  ] {
    let n = format!("cfl_rdo({:?})", bsize);
    c.bench_function(&n, move |b| cfl_rdo_bench(b, bsize));
  }
}

fn cfl_rdo_bench(b: &mut Bencher, bsize: BlockSize) {
  let config =
    EncoderConfig { quantizer: 100, speed: 10, ..Default::default() };
  let fi = FrameInvariants::new(1024, 1024, config);
  let mut fs = FrameState::new(&fi);
  let offset = BlockOffset { x: 1, y: 1 };
  b.iter(|| rdo_cfl_alpha(&mut fs, &offset, bsize, 8))
}

criterion_group!(intra_prediction, predict::pred_bench,);

criterion_group!(cfl, cfl_rdo);
criterion_group!(cdef, cdef_frame);
criterion_group!(write_block, write_b);
criterion_group!(me, me::get_sad);

#[cfg(feature = "comparative_bench")]
criterion_main!(comparative::intra_prediction);

#[cfg(not(feature = "comparative_bench"))]
criterion_main!(write_block, intra_prediction, cdef, cfl, me, transform);
<commit_msg>Use reduced warm up time for get_sad.<commit_after>// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#[macro_use]
extern crate criterion;
extern crate rand;
extern crate rav1e;

mod predict;
mod transform;
mod me;

use std::time::Duration;
use criterion::*;
use rav1e::cdef::cdef_filter_frame;
use rav1e::context::*;
use rav1e::ec;
use rav1e::partition::*;
use rav1e::predict::*;
use rav1e::rdo::rdo_cfl_alpha;
use rav1e::*;

use transform::transform;

#[cfg(feature = "comparative_bench")]
mod comparative;

fn write_b(c: &mut Criterion) {
  for &tx_size in &[TxSize::TX_4X4, TxSize::TX_8X8] {
    for &qi in &[20, 55] {
      let n = format!("write_b_bench({:?}, {})", tx_size, qi);
      c.bench_function(&n, move |b| write_b_bench(b, tx_size, qi));
    }
  }
}

fn write_b_bench(b: &mut Bencher, tx_size: TxSize, qindex: usize) {
  #[cfg(feature = "aom")]
  unsafe {
    av1_rtcd();
    aom_dsp_rtcd();
  }
  let config =
    EncoderConfig { quantizer: qindex, speed: 10, ..Default::default() };
  let mut fi = FrameInvariants::new(1024, 1024, config);
  let mut w = ec::WriterEncoder::new();
  let fc = CDFContext::new(fi.base_q_idx);
  let bc = BlockContext::new(fi.sb_width * 16, fi.sb_height * 16);
  let mut fs = FrameState::new(&fi);
  // For now, restoration unit size is locked to superblock size.
  let rc = RestorationContext::new(fi.sb_width, fi.sb_height);
  let mut cw = ContextWriter::new(fc, bc, rc);

  let tx_type = TxType::DCT_DCT;

  let sbx = 0;
  let sby = 0;
  let ac = &[0i16; 32 * 32];

  b.iter(|| {
    for &mode in RAV1E_INTRA_MODES {
      let sbo = SuperBlockOffset { x: sbx, y: sby };
      fs.qc.update(fi.base_q_idx, tx_size, mode.is_intra(), 8);
      for p in 1..3 {
        for by in 0..8 {
          for bx in 0..8 {
            // For ex, 8x8 tx should be applied to even numbered (bx,by)
            if (tx_size.width_mi() >> 1) & bx != 0
              || (tx_size.height_mi() >> 1) & by != 0
            {
              continue;
            };
            let bo = sbo.block_offset(bx, by);
            let tx_bo = BlockOffset { x: bo.x + bx, y: bo.y + by };
            let po = tx_bo.plane_offset(&fs.input.planes[p].cfg);
            encode_tx_block(
              &mut fi,
              &mut fs,
              &mut cw,
              &mut w,
              p,
              &bo,
              mode,
              tx_size,
              tx_type,
              tx_size.block_size(),
              &po,
              false,
              8,
              ac,
              0,
              false
            );
          }
        }
      }
    }
  });
}

fn cdef_frame(c: &mut Criterion) {
  let w = 128;
  let h = 128;
  let n = format!("cdef_frame({}, {})", w, h);
  c.bench_function(&n, move |b| cdef_frame_bench(b, w, h));
}

fn cdef_frame_bench(b: &mut Bencher, w: usize, h: usize) {
  let config =
    EncoderConfig { quantizer: 100, speed: 10, ..Default::default() };
  let fi = FrameInvariants::new(w, h, config);
  let mut bc = BlockContext::new(fi.sb_width * 16, fi.sb_height * 16);
  let mut fs = FrameState::new(&fi);

  b.iter(|| cdef_filter_frame(&fi, &mut fs.rec, &mut bc, 8));
}

fn cfl_rdo(c: &mut Criterion) {
  for &bsize in &[
    BlockSize::BLOCK_4X4,
    BlockSize::BLOCK_8X8,
    BlockSize::BLOCK_16X16,
    BlockSize::BLOCK_32X32
  ] {
    let n = format!("cfl_rdo({:?})", bsize);
    c.bench_function(&n, move |b| cfl_rdo_bench(b, bsize));
  }
}

fn cfl_rdo_bench(b: &mut Bencher, bsize: BlockSize) {
  let config =
    EncoderConfig { quantizer: 100, speed: 10, ..Default::default() };
  let fi = FrameInvariants::new(1024, 1024, config);
  let mut fs = FrameState::new(&fi);
  let offset = BlockOffset { x: 1, y: 1 };
  b.iter(|| rdo_cfl_alpha(&mut fs, &offset, bsize, 8))
}

criterion_group!(intra_prediction, predict::pred_bench,);

criterion_group!(cfl, cfl_rdo);
criterion_group!(cdef, cdef_frame);
criterion_group!(write_block, write_b);
criterion_group!{ name = me;
                  config = Criterion::default().warm_up_time(Duration::new(1,0));
                  targets = me::get_sad
}

#[cfg(feature = "comparative_bench")]
criterion_main!(comparative::intra_prediction);

#[cfg(not(feature = "comparative_bench"))]
criterion_main!(write_block, intra_prediction, cdef, cfl, me, transform);
<|endoftext|>