<commit_before>#![feature(globs)]

extern crate ncurses;

use ncurses::*;

/*
 * So, how this working?
 * And what functions do we need?
 * First of all, we need 2 fields (user and enemy)
 * So, list of functions
 * initialize, deinit
 * init_field()
 * get_input() -> i32;
 *
 */

enum CellType {
    EMPTY = 0, // Just empty space
    SHOT = 1, // o ?
    SHIP = 2, // x ?
}

enum Color {
    GREEN = 16, // For numbers and current ship while placing
    PINK = 17, // For shoted ships
    RED = 18, // For death ships
    WHITE = 19, // For shot in empty space
}

struct Cell {
    Type: CellType,
    Color: Color
}

impl Cell {
   fn print(&self) {
       let mut symbol = " ";
       match self.Type {
           EMPTY => symbol = ".",
           SHOT => symbol = "o",
           SHIP => symbol = "x",
       }
       printw(format!("{}", symbol).as_slice());
   }
}

struct Coord {
    y: uint,
    x: uint
}

struct Ship {
    coord: Coord,
    cells: Vec<Coord>
}

fn init_colors() {
    start_color();
    init_color((Color::GREEN as i16), 0, 150 * 4, 0);
    init_color((Color::PINK as i16), 0, 150 * 4, 0);
    init_color((Color::RED as i16), 0, 150 * 4, 0);
    init_color((Color::WHITE as i16), 0, 150 * 4, 0);
}

fn initialize() {
    initscr();

    curs_set(CURSOR_INVISIBLE);

    init_colors();

    keypad(stdscr, true);
    noecho();
}

fn deinitialize() {
    endwin();
}

fn get_input() -> i32 {
    getch()
}

fn print_field(field: &mut [[Cell, ..10], ..10], y: i32, x: i32) {
    mv(y, x+2i32);
    for i in range(1u, 11u) {
        printw(format!(" {}", i).as_slice());
    }
    for i in range(1u, 11u) {
        if i != 10u {
            mv(y+(i as i32), x+1i32);
        }
        else {
            mv(y+(i as i32), x);
        }
        printw(format!("{}", i).as_slice());
    }
    for i in range(0u, 10u) {
        mv(y+(i as i32)+1i32, x+2i32);
        for j in range(0u, 10u) {
            printw(" ");
            field[i][j].print();
        }
        printw("\n");
    }
}

fn add_ship(field: &mut [[Cell, ..10], ..10], ship: Ship) {
    let y = ship.coord.y;
    let x = ship.coord.x;
    for i in ship.cells.iter() {
        let cy = i.y;
        let cx = i.x;
        field[y+cy][x+cx] = Cell { Type : SHIP, Color : GREEN };
    }
}

fn remove_ship(field: &mut [[Cell, ..10], ..10], ship: Ship) {
    let y = ship.coord.y;
    let x = ship.coord.x;
    for i in ship.cells.iter() {
        let cy = i.y;
        let cx = i.x;
        field[y+cy][x+cx] = Cell { Type : EMPTY, Color : WHITE };
    }
}

fn rotate_ship(ship: Ship) {
    for i in range(0u, ship.cells.len()) {
        let temp = ship.cells[i].y;
        ship.cells[i].y = ship.cells[i].x;
        ship.cells[i].x = temp;
    }
}

fn place_ship(field: &mut [[Cell, ..10], ..10], ship: Ship) {
    add_ship(field, ship);
}

fn move_ship(field: &mut [[Cell, ..10], ..10], ship: Ship, input: i32) -> bool {
    remove_ship(field, ship);
    let KEY_SPACE = ' ' as i32;
    match input {
        KEY_LEFT =>  {
            ship.coord.x-=1;
        }
        KEY_UP =>  {
            ship.coord.y-=1;
        }
        KEY_DOWN =>  {
            ship.coord.y+=1;
        }
        KEY_RIGHT =>  {
            ship.coord.x+=1;
        }
        KEY_ENTER => {
            place_ship(field, ship);
            return true;
        }
        KEY_SPACE => {
            rotate_ship(ship);
        }
    }
    add_ship(field, ship);
    return false;
}

fn main() {
    initialize();

    let mut userfield = &mut [[Cell { Type : EMPTY, Color : WHITE }, ..10], ..10];
    let mut enemyfield = &mut [[Cell { Type : EMPTY, Color : WHITE }, ..10], ..10];

    let mut oneship = Ship {
        coord : Coord {y : 0u, x : 0u},
        cells : vec!{ Coord {y : 0u, x : 0u}}
    };

    let mut twoship = Ship {
        coord : Coord {y : 0u, x : 0u},
        cells : vec!{ Coord {y : 0u, x : 0u},
                      Coord {y : 1u, x : 0u}}
    };

    let mut threeship = Ship {
        coord : Coord {y : 1u, x : 0u},
        cells : vec!{ Coord {y : -1u, x : 0u},
                      Coord {y : 0u, x : 0u},
                      Coord {y : 1u, x : 0u}}
    };

    let mut fourship = Ship {
        coord : Coord {y : 1u, x : 0u},
        cells : vec!{ Coord {y : -1u, x : 0u},
                      Coord {y : 0u, x : 0u},
                      Coord {y : 1u, x : 0u},
                      Coord {y : 2u, x : 0u}}
    };

    // Which ships must be placed?
    // 1 - 4ship
    // 2 - 3ship
    // 3 - 2ship
    // 4 - 1ship
    // sum - 10

    let mut count = 0u;
    let mut ch = getch();
    let mut curShip : Ship = fourship;
    while ch != ('q' as i32) {
        if (move_ship(userfield, curShip, ch)) {
            count+=1;
            if count <= 2 {
                curShip = threeship;
            }
            else if count <= 5 {
                curShip = twoship;
            }
            else if count <= 9 {
                curShip = oneship;
            }
        }

        print_field(userfield, 0, 0);
        print_field(enemyfield, 0, 35);
        refresh();
        ch = getch();
    }

    // need on end of each frame?

    deinitialize();
}
<commit_msg>Added more useless mess<commit_after>#![feature(globs)]

extern crate ncurses;

use ncurses::*;

/*
 * So, how this working?
 * And what functions do we need?
 * First of all, we need 2 fields (user and enemy)
 * So, list of functions
 * initialize, deinit
 * init_field()
 * get_input() -> i32;
 *
 */

enum CellType {
    EMPTY = 0, // Just empty space
    SHOT = 1, // o ?
    SHIP = 2, // x ?
}

enum Color {
    GREEN = 16, // For numbers and current ship while placing
    PINK = 17, // For shoted ships
    RED = 18, // For death ships
    WHITE = 19, // For shot in empty space
}

struct Cell {
    Type: CellType,
    Color: Color
}

impl Cell {
   fn print(&self) {
       let mut symbol = " ";
       match self.Type {
           EMPTY => symbol = ".",
           SHOT => symbol = "o",
           SHIP => symbol = "x",
       }
       printw(format!("{}", symbol).as_slice());
   }
}

struct Coord {
    y: uint,
    x: uint
}

struct Ship {
    coord: Coord,
    cells: Vec<Coord>
}

fn init_colors() {
    start_color();
    init_color((Color::GREEN as i16), 0, 150 * 4, 0);
    init_color((Color::PINK as i16), 0, 150 * 4, 0);
    init_color((Color::RED as i16), 0, 150 * 4, 0);
    init_color((Color::WHITE as i16), 0, 150 * 4, 0);
}

fn initialize() {
    initscr();

    curs_set(CURSOR_INVISIBLE);

    init_colors();

    keypad(stdscr, true);
    noecho();
}

fn deinitialize() {
    endwin();
}

fn get_input() -> i32 {
    getch()
}

fn print_field(field: [[Cell, ..10], ..10], y: i32, x: i32) {
    mv(y, x+2i32);
    for i in range(1u, 11u) {
        printw(format!(" {}", i).as_slice());
    }
    for i in range(1u, 11u) {
        if i != 10u {
            mv(y+(i as i32), x+1i32);
        }
        else {
            mv(y+(i as i32), x);
        }
        printw(format!("{}", i).as_slice());
    }
    for i in range(0u, 10u) {
        mv(y+(i as i32)+1i32, x+2i32);
        for j in range(0u, 10u) {
            printw(" ");
            field[i][j].print();
        }
        printw("\n");
    }
}

fn add_ship(field: &mut [[Cell, ..10], ..10], ship: &Ship) {
    let y = ship.coord.y;
    let x = ship.coord.x;
    for i in ship.cells.iter() {
        let cy = i.y;
        let cx = i.x;
        field[y+cy][x+cx] = Cell { Type : SHIP, Color : GREEN };
    }
}

fn remove_ship(field: &mut [[Cell, ..10], ..10], ship: &Ship) {
    let y = ship.coord.y;
    let x = ship.coord.x;
    for i in ship.cells.iter() {
        let cy = i.y;
        let cx = i.x;
        field[y+cy][x+cx] = Cell { Type : EMPTY, Color : WHITE };
    }
}

fn rotate_ship(ship: &mut Ship) {
    for i in range(0u, ship.cells.len()) {
        let temp = ship.cells[i].y;
        ship.cells[i].y = ship.cells[i].x;
        ship.cells[i].x = temp;
    }
}

fn move_ship(field: &mut [[Cell, ..10], ..10], ship: &mut Ship, input: i32) -> bool {
    remove_ship(field, &*ship);
    let mut x = ship.coord.x;
    let mut y = ship.coord.y;
    match input {
        KEY_LEFT =>  {
            if field[y][x-1].Type == SHIP {
                return false;
            }
            ship.coord.x-=1;
        }
        KEY_UP =>  {
            if field[y-1][x].Type == SHIP {
                return false;
            }
            ship.coord.y-=1;
        }
        KEY_DOWN =>  {
            if field[y+1][x].Type == SHIP {
                return false;
            }
            ship.coord.y+=1;
        }
        KEY_RIGHT =>  {
            if field[y][x+1].Type == SHIP {
                return false;
            }
            ship.coord.x+=1;
        }
        KEY_ENTER => {
            add_ship(field, &*ship);
            return true;
        }
        _ => {
            rotate_ship(ship);
        }
    }
    add_ship(field, &*ship);
    return false;
}

fn main() {
    initialize();

    let mut userfield = [[Cell { Type : EMPTY, Color : WHITE }, ..10], ..10];
    let mut enemyfield = [[Cell { Type : EMPTY, Color : WHITE }, ..10], ..10];

    let oneship : Ship = Ship {
        coord : Coord {y : 0u, x : 0u},
        cells : vec![ Coord {y : 0u, x : 0u}]
    };

    let twoship : Ship = Ship {
        coord : Coord {y : 0u, x : 0u},
        cells : vec![ Coord {y : 0u, x : 0u},
                      Coord {y : 1u, x : 0u}]
    };

    let fourship : Ship = Ship {
        coord : Coord {y : 1u, x : 0u},
        cells : vec![ Coord {y : -1u, x : 0u},
                      Coord {y : 0u, x : 0u},
                      Coord {y : 1u, x : 0u},
                      Coord {y : 2u, x : 0u}]
    };

    // Which ships must be placed?
    // 1 - 4ship
    // 2 - 3ship
    // 3 - 2ship
    // 4 - 1ship
    // sum - 10

    let mut count = 0u;
    let mut ch = getch();
    let mut curShip : Ship = fourship;
    while ch != ('q' as i32) {
        if (move_ship(&mut userfield, &mut curShip, ch)) {
            count+=1;
            if count <= 2 {
                curShip = Ship {
                    coord : Coord {y : 1u, x : 0u},
                    cells : vec![ Coord {y : -1u, x : 0u},
                                  Coord {y : 0u, x : 0u},
                                  Coord {y : 1u, x : 0u}]
                };
            }
            else if count <= 5 {
                curShip = Ship {
                    coord : Coord {y : 0u, x : 0u},
                    cells : vec![ Coord {y : 0u, x : 0u},
                                  Coord {y : 1u, x : 0u}]
                };
            }
            else if count <= 9 {
                curShip = Ship {
                    coord : Coord {y : 0u, x : 0u},
                    cells : vec![ Coord {y : 0u, x : 0u}]
                };
            }
        }

        print_field(userfield, 0, 0);
        print_field(enemyfield, 0, 35);
        refresh();
        ch = getch();
    }

    // need on end of each frame?

    deinitialize();
}
<|endoftext|>


<commit_before>#[macro_use]
mod lib;

mod command;
mod constant;

use constant::*;
use std::env;

fn main() {
    if let Some(message) = validate_at_first() {
        return println!("{}", message);
    }

    let args = env::args()
        .skip(1)
        .collect::<Vec<String>>();

    command::execute(args);
}

fn validate_at_first() -> Option<String> {
    let current_dir = match env::current_dir() {
        Ok(dir)  => dir,
        Err(why) => return Some(format!("ERROR: {}", why)),
    };

    BANNED_DIRS
        .iter()
        .find(|d| current_dir.ends_with(d))
        .map(|d| format!(r#"ERROR: Cannot run in "{}"."#, d))
}
<commit_msg>Change type of validate_at_first()'s return value<commit_after>#[macro_use]
mod lib;

mod command;
mod constant;

use constant::*;
use std::env;

fn main() {
    if let Err(message) = validate_at_first() {
        return println!("{}", message);
    }

    let args = env::args()
        .skip(1)
        .collect::<Vec<String>>();

    command::execute(args);
}

fn validate_at_first() -> Result<(), String> {
    let current_dir = match env::current_dir() {
        Ok(dir)  => dir,
        Err(why) => return Err(format!("ERROR: {}", why)),
    };

    BANNED_DIRS
        .iter()
        .find(|d| current_dir.ends_with(d))
        .map_or(Ok(()), |dir| Err(format!(r#"ERROR: Cannot run in "{}"."#, dir)))
}
<|endoftext|>


<commit_before>extern crate html5ever;
extern crate html5ever_dom_sink;
extern crate hyper;
extern crate iron;
extern crate rustc_serialize;
extern crate tempfile;
extern crate tendril;
extern crate url;

mod json;

use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
use std::process::Command;
use std::process::Stdio;
use std::str::FromStr;

use html5ever::Attribute;
use html5ever::tree_builder::interface::TreeSink;
use html5ever_dom_sink::common::NodeEnum;
use html5ever_dom_sink::rcdom::Handle;
use html5ever_dom_sink::rcdom::RcDom;
use hyper::Client;
use hyper::header::Connection;
use iron::Iron;
use iron::IronResult;
use iron::Request;
use iron::Response;
use iron::headers::ContentType;
use iron::mime::Mime;
use iron::mime::SubLevel;
use iron::mime::TopLevel;
use rustc_serialize::json::Json;
use tempfile::NamedTempFile;
use url::percent_encoding;

use json::JsonPathElement::{Key, Only};

// TODO: consider doing s/en.wikipedia.org/this app's url/ on the HTML before serving it. This
// currently works fine, but might not over HTTPS.
// TODO: Do nested functions work? If so, use those where appropriate.
// TODO: there are some places where I've handrolled try!() equivalents. Fix those.
// TODO: make sure I'm returning Results everywhere, and propagating errors correctly. Remove all
// uses of unwrap() that might panic.

// TODO: return a Result
fn call_wikimedia_api(parameters: Vec<(&str, &str)>) -> String {
    let post_body =
        parameters.into_iter().map(|p| format!("{}={}", p.0, p.1))
        .collect::<Vec<_>>().join("&") + "&format=json";

    let client = Client::new();
    // TODO: rename to "response".
    let mut res = client.post("https://en.wikipedia.org/w/api.php")
        .body(&post_body)
        .header(Connection::close())
        .send().unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    body
}

// TODO: this name is terrible.
// Returns pairs of (revid, parentid)
fn get_revert_revision_ids(page: &str) -> Result<Vec<(u64, u64)>, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", page),
             ("rvprop", "comment|ids"), ("rvlimit", "60")]);

    let json = Json::from_str(&json_str).unwrap();
    let revisions = try!(
        json::get_json_array(&json, &[Key("query"), Key("pages"), Only, Key("revisions")]));

    // Filter for revisions that mention vandalism, and that have revid and parentid (which they all
    // should, but merits checking).
    Ok(revisions.into_iter().filter(|revision| {
        match (json::get_json_string(revision, &[Key("comment")]),
               json::get_json_number(revision, &[Key("revid")]),
               json::get_json_number(revision, &[Key("parentid")])) {
            (Ok(ref comment), Ok(_), Ok(_)) => comment.contains("vandal"),
            _ => false,
        }})
       .map(|revision| {
           (json::get_json_number(revision, &[Key("revid")]).unwrap(),
            json::get_json_number(revision, &[Key("parentid")]).unwrap())
       }).collect())
}

fn get_latest_revision_id(page: &str) -> Result<u64, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", page), ("rvprop", "ids"),
             ("rvlimit", "1")]);
    let json = Json::from_str(&json_str).unwrap();
    json::get_json_number(
        &json, &[Key("query"), Key("pages"), Only, Key("revisions"), Only, Key("revid")])
}

fn get_revision_content(page: &str, id: u64) -> Result<String, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", page), ("rvprop", "content"),
             ("rvlimit", "1"), ("rvstartid", &id.to_string())]);
    let json = Json::from_str(&json_str).unwrap();
    match json::get_json_string(
        &json, &[Key("query"), Key("pages"), Only, Key("revisions"), Only, Key("*")]) {
        Ok(content) => Ok(content.to_string()),
        Err(msg) => Err(msg),
    }
}

fn write_to_temp_file(contents: &str) -> NamedTempFile {
    let tempfile = NamedTempFile::new().unwrap();
    let mut file = OpenOptions::new().write(true).open(tempfile.path()).unwrap();
    file.write_all(contents.as_bytes()).unwrap();
    file.flush().unwrap();
    tempfile
}

// TODO: I'm not so sure these parameter names aren't terrible.
fn merge(old: &str, new1: &str, new2: &str) -> Option<String> {
    let old_tempfile = write_to_temp_file(old);
    let new1_tempfile = write_to_temp_file(new1);
    let new2_tempfile = write_to_temp_file(new2);

    let mut process = Command::new("diff3");
    process.arg("-m").args(&[new1_tempfile.path(), old_tempfile.path(), new2_tempfile.path()])
        .stdout(Stdio::piped()).stderr(Stdio::null());
    let output = process.output().unwrap();
    if output.status.success() {
        Some(String::from_utf8(output.stdout).unwrap())
    } else {
        None
    }
}

fn render(wikitext: &str) -> Result<String, String> {
    let encoded_wikitext =
        percent_encoding::percent_encode(wikitext.as_bytes(), percent_encoding::QUERY_ENCODE_SET);
    let html = call_wikimedia_api(
        vec![("action", "parse"), ("prop", "text"), ("disablepp", ""), ("contentmodel", "wikitext"),
             ("text", &encoded_wikitext)]);
    // TODO: check return value
    let json = Json::from_str(&html).unwrap();
    match json::get_json_string(&json, &[Key("parse"), Key("text"), Key("*")]) {
        Ok(contents) => Ok(contents.to_string()),
        Err(message) => Err(message),
    }
}

fn has_matching_id(attributes: &Vec<Attribute>, id: &str) -> bool {
    // TODO: could also do this with filter() and is_empty(). Not sure if that would be better.
    for attribute in attributes {
        // TODO: do I seriously have to construct a StrTendril here?
        // There has to be a better way.
        if attribute.name.local.as_slice() == "id" &&
            attribute.value == tendril::StrTendril::from_str(id).unwrap() {
                return true;
            }
    }
    return false;
}

fn find_node_by_id(handle: &Handle, id: &str) -> Result<Handle, String> {
    let node = handle.borrow();
    match node.node {
        NodeEnum::Element(_, ref attributes) if has_matching_id(attributes, id) => Ok(handle.clone()),
        _ => {
            for child in &node.children {
                match find_node_by_id(child, id) {
                    // TODO: this looks weird. Is this an abnormal way to do things?
                    Ok(node) => return Ok(node),
                    _ => continue,
                }
            }
            Err(format!("No node with ID {} found", id))
        },
    }
}

fn replace_node_with_placeholder(original_html: &str, div_id: &str, placeholder: &str) -> Result<String, String> {
    // TODO: check errors
    let html = tendril::StrTendril::from_str(original_html).unwrap();
    let mut dom: RcDom = html5ever::parse(html5ever::one_input(html), Default::default());

    let handle = try!(find_node_by_id(&dom.get_document(), div_id));
    let child_handles =
        (&handle.borrow().children).into_iter().map(|child| child.clone()).collect::<Vec<_>>();
    for child_handle in child_handles {
        dom.remove_from_parent(child_handle);
    }
    dom.append(handle,
               html5ever::tree_builder::interface::NodeOrText::AppendText(
                   tendril::StrTendril::from_str(placeholder).unwrap()));
    let mut serialized: Vec<u8> = vec![];
    html5ever::serialize::serialize(&mut serialized, &dom.document, Default::default());
    // TODO: error handling
    Ok(String::from_utf8(serialized).unwrap())
}

fn get_current_page(page: &str) -> String {
    let client = Client::new();
    let mut res = client.get(
        &format!("https://en.wikipedia.org/wiki/{}", page))
        .header(Connection::close())
        .send().unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    body
}

// TODO: this name is terrible.
fn get_page_with_vandalism_restored(page: &str) -> Result<String, String> {
    let latest_revid = get_latest_revision_id(page).unwrap();
    let mut accumulated_contents = get_revision_content(page, latest_revid).unwrap();
    let mut revisions = vec![];
    for (revert_revid, vandalism_revid) in get_revert_revision_ids(page).ok().unwrap() {
        let reverted_contents = get_revision_content(page, revert_revid).unwrap();
        let vandalized_contents = get_revision_content(page, vandalism_revid).unwrap();
        match merge(&reverted_contents, &vandalized_contents, &accumulated_contents) {
            Some(merged_contents) => {
                // TODO: replace this with a log statement, if there's a good logging framework.
                println!(
                    "For page \"{}\", restored vandalism https://en.wikipedia.org/w/index.php?title=Zachary_Taylor&diff=prev&oldid={}",
                         page, revert_revid);
                accumulated_contents = merged_contents;
                revisions.push(revert_revid);
            }
            None => (),
        }
    }

    // TODO: replace this with a log statement, if there's a good logging framework.
    println!("For page \"{}\", restored vandalisms reverted in: {:?}", page, revisions);

    let rendered_body = render(&accumulated_contents).unwrap();
    let current_page_contents = get_current_page(page);
    // TODO: randomize the placeholder string per-request
    let page_contents_with_placeholder =
        replace_node_with_placeholder(
            &current_page_contents, "mw-content-text", "WMW_PLACEHOLDER_TEXT").unwrap();
    Ok(page_contents_with_placeholder.replace("WMW_PLACEHOLDER_TEXT", &rendered_body))
}

fn serve_request(request: &mut Request) -> IronResult<Response> {
    if request.url.path.len() == 2 && request.url.path[0] == "wiki" {
        let mut response = Response::with(
            (iron::status::Ok, get_page_with_vandalism_restored(&request.url.path[1]).unwrap()));
        response.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
        Ok(response)
    } else {
        let client = Client::new();
        // TODO: error handling
        let mut wikipedia_response =
            client.get(&format!("https://en.wikipedia.org/{}", request.url.path.join("/")))
            .header(Connection::close())
            .send().unwrap();
        let mut wikipedia_body: Vec<u8> = Vec::new();
        wikipedia_response.read_to_end(&mut wikipedia_body);

        let mut response = Response::with(wikipedia_body);
        response.status = Some(wikipedia_response.status);
        response.headers = wikipedia_response.headers.clone();
        //response.body = wikipedia_body;
        println!("Forwarded request for {} to en.wikipedia.org", request.url.path.join("/"));
        Ok(response)
    }
}

fn main() {
    Iron::new(serve_request).http("localhost:3000").unwrap();
}

#[cfg(test)]
mod tests {
    use super::{merge, replace_node_with_placeholder};

    #[test]
    fn test_merge_clean() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed.\n";
        let new2 = "First line changed.\n\nSecond line.\n";
        assert_eq!("First line changed.\n\nSecond line changed.\n", merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_merge_conflicting() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed one way.\n";
        let new2 = "First line changed.\n\nSecond line changed a different way.\n";
        assert_eq!(None, merge(old, new1, new2));
    }

    #[test]
    fn test_merge_special_characters() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line 𐅃.\n";
        let new2 = "First line さようなら.\n\nSecond line.\n";
        assert_eq!("First line さようなら.\n\nSecond line 𐅃.\n", merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_replace_html_content() {
        let original_html = "

original text

Other text

";
        let expected_html = "

replaced text

Other text

";
        let processed_html = replace_node_with_placeholder(original_html, "mw-content-text", "replaced text").unwrap();
        assert_eq!(expected_html, processed_html);
    }
}
<commit_msg>Pass the page title to the Wikipedia API when parsing Wikitext, so disambiguation links work properly.<commit_after>extern crate html5ever;
extern crate html5ever_dom_sink;
extern crate hyper;
extern crate iron;
extern crate rustc_serialize;
extern crate tempfile;
extern crate tendril;
extern crate url;

mod json;

use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
use std::process::Command;
use std::process::Stdio;
use std::str::FromStr;

use html5ever::Attribute;
use html5ever::tree_builder::interface::TreeSink;
use html5ever_dom_sink::common::NodeEnum;
use html5ever_dom_sink::rcdom::Handle;
use html5ever_dom_sink::rcdom::RcDom;
use hyper::Client;
use hyper::header::Connection;
use iron::Iron;
use iron::IronResult;
use iron::Request;
use iron::Response;
use iron::headers::ContentType;
use iron::mime::Mime;
use iron::mime::SubLevel;
use iron::mime::TopLevel;
use rustc_serialize::json::Json;
use tempfile::NamedTempFile;
use url::percent_encoding;

use json::JsonPathElement::{Key, Only};

// TODO: consider doing s/en.wikipedia.org/this app's url/ on the HTML before serving it. This
// currently works fine, but might not over HTTPS.
// TODO: Do nested functions work? If so, use those where appropriate.
// TODO: there are some places where I've handrolled try!() equivalents. Fix those.
// TODO: make sure I'm returning Results everywhere, and propagating errors correctly. Remove all
// uses of unwrap() that might panic.

// TODO: return a Result
fn call_wikimedia_api(parameters: Vec<(&str, &str)>) -> String {
    let post_body =
        parameters.into_iter().map(|p| format!("{}={}", p.0, p.1))
        .collect::<Vec<_>>().join("&") + "&format=json";

    let client = Client::new();
    // TODO: rename to "response".
    let mut res = client.post("https://en.wikipedia.org/w/api.php")
        .body(&post_body)
        .header(Connection::close())
        .send().unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    body
}

// TODO: this name is terrible.
// Returns pairs of (revid, parentid)
fn get_revert_revision_ids(page: &str) -> Result<Vec<(u64, u64)>, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", page),
             ("rvprop", "comment|ids"), ("rvlimit", "60")]);

    let json = Json::from_str(&json_str).unwrap();
    let revisions = try!(
        json::get_json_array(&json, &[Key("query"), Key("pages"), Only, Key("revisions")]));

    // Filter for revisions that mention vandalism, and that have revid and parentid (which they all
    // should, but merits checking).
    Ok(revisions.into_iter().filter(|revision| {
        match (json::get_json_string(revision, &[Key("comment")]),
               json::get_json_number(revision, &[Key("revid")]),
               json::get_json_number(revision, &[Key("parentid")])) {
            (Ok(ref comment), Ok(_), Ok(_)) => comment.contains("vandal"),
            _ => false,
        }})
       .map(|revision| {
           (json::get_json_number(revision, &[Key("revid")]).unwrap(),
            json::get_json_number(revision, &[Key("parentid")]).unwrap())
       }).collect())
}

fn get_latest_revision_id(page: &str) -> Result<u64, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", page), ("rvprop", "ids"),
             ("rvlimit", "1")]);
    let json = Json::from_str(&json_str).unwrap();
    json::get_json_number(
        &json, &[Key("query"), Key("pages"), Only, Key("revisions"), Only, Key("revid")])
}

fn get_revision_content(page: &str, id: u64) -> Result<String, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", page), ("rvprop", "content"),
             ("rvlimit", "1"), ("rvstartid", &id.to_string())]);
    let json = Json::from_str(&json_str).unwrap();
    match json::get_json_string(
        &json, &[Key("query"), Key("pages"), Only, Key("revisions"), Only, Key("*")]) {
        Ok(content) => Ok(content.to_string()),
        Err(msg) => Err(msg),
    }
}

fn write_to_temp_file(contents: &str) -> NamedTempFile {
    let tempfile = NamedTempFile::new().unwrap();
    let mut file = OpenOptions::new().write(true).open(tempfile.path()).unwrap();
    file.write_all(contents.as_bytes()).unwrap();
    file.flush().unwrap();
    tempfile
}

// TODO: I'm not so sure these parameter names aren't terrible.
fn merge(old: &str, new1: &str, new2: &str) -> Option<String> {
    let old_tempfile = write_to_temp_file(old);
    let new1_tempfile = write_to_temp_file(new1);
    let new2_tempfile = write_to_temp_file(new2);

    let mut process = Command::new("diff3");
    process.arg("-m").args(&[new1_tempfile.path(), old_tempfile.path(), new2_tempfile.path()])
        .stdout(Stdio::piped()).stderr(Stdio::null());
    let output = process.output().unwrap();
    if output.status.success() {
        Some(String::from_utf8(output.stdout).unwrap())
    } else {
        None
    }
}

fn render(page: &str, wikitext: &str) -> Result<String, String> {
    let encoded_wikitext =
        percent_encoding::percent_encode(wikitext.as_bytes(), percent_encoding::QUERY_ENCODE_SET);
    let html = call_wikimedia_api(
        vec![("action", "parse"), ("prop", "text"), ("disablepp", ""), ("contentmodel", "wikitext"),
             ("title", page), ("text", &encoded_wikitext)]);
    // TODO: check return value
    let json = Json::from_str(&html).unwrap();
    match json::get_json_string(&json, &[Key("parse"), Key("text"), Key("*")]) {
        Ok(contents) => Ok(contents.to_string()),
        Err(message) => Err(message),
    }
}

fn has_matching_id(attributes: &Vec<Attribute>, id: &str) -> bool {
    // TODO: could also do this with filter() and is_empty(). Not sure if that would be better.
    for attribute in attributes {
        // TODO: do I seriously have to construct a StrTendril here?
        // There has to be a better way.
        if attribute.name.local.as_slice() == "id" &&
            attribute.value == tendril::StrTendril::from_str(id).unwrap() {
                return true;
            }
    }
    return false;
}

fn find_node_by_id(handle: &Handle, id: &str) -> Result<Handle, String> {
    let node = handle.borrow();
    match node.node {
        NodeEnum::Element(_, ref attributes) if has_matching_id(attributes, id) => Ok(handle.clone()),
        _ => {
            for child in &node.children {
                match find_node_by_id(child, id) {
                    // TODO: this looks weird. Is this an abnormal way to do things?
                    Ok(node) => return Ok(node),
                    _ => continue,
                }
            }
            Err(format!("No node with ID {} found", id))
        },
    }
}

fn replace_node_with_placeholder(original_html: &str, div_id: &str, placeholder: &str) -> Result<String, String> {
    // TODO: check errors
    let html = tendril::StrTendril::from_str(original_html).unwrap();
    let mut dom: RcDom = html5ever::parse(html5ever::one_input(html), Default::default());

    let handle = try!(find_node_by_id(&dom.get_document(), div_id));
    let child_handles =
        (&handle.borrow().children).into_iter().map(|child| child.clone()).collect::<Vec<_>>();
    for child_handle in child_handles {
        dom.remove_from_parent(child_handle);
    }
    dom.append(handle,
               html5ever::tree_builder::interface::NodeOrText::AppendText(
                   tendril::StrTendril::from_str(placeholder).unwrap()));
    let mut serialized: Vec<u8> = vec![];
    html5ever::serialize::serialize(&mut serialized, &dom.document, Default::default());
    // TODO: error handling
    Ok(String::from_utf8(serialized).unwrap())
}

fn get_current_page(page: &str) -> String {
    let client = Client::new();
    let mut res = client.get(
        &format!("https://en.wikipedia.org/wiki/{}", page))
        .header(Connection::close())
        .send().unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    body
}

// TODO: this name is terrible.
fn get_page_with_vandalism_restored(page: &str) -> Result<String, String> {
    let latest_revid = get_latest_revision_id(page).unwrap();
    let mut accumulated_contents = get_revision_content(page, latest_revid).unwrap();
    let mut revisions = vec![];
    for (revert_revid, vandalism_revid) in get_revert_revision_ids(page).ok().unwrap() {
        let reverted_contents = get_revision_content(page, revert_revid).unwrap();
        let vandalized_contents = get_revision_content(page, vandalism_revid).unwrap();
        match merge(&reverted_contents, &vandalized_contents, &accumulated_contents) {
            Some(merged_contents) => {
                // TODO: replace this with a log statement, if there's a good logging framework.
                println!(
                    "For page \"{}\", restored vandalism https://en.wikipedia.org/w/index.php?title=Zachary_Taylor&diff=prev&oldid={}",
                         page, revert_revid);
                accumulated_contents = merged_contents;
                revisions.push(revert_revid);
            }
            None => (),
        }
    }

    // TODO: replace this with a log statement, if there's a good logging framework.
    println!("For page \"{}\", restored vandalisms reverted in: {:?}", page, revisions);

    let rendered_body = render(page, &accumulated_contents).unwrap();
    let current_page_contents = get_current_page(page);
    // TODO: randomize the placeholder string per-request
    let page_contents_with_placeholder =
        replace_node_with_placeholder(
            &current_page_contents, "mw-content-text", "WMW_PLACEHOLDER_TEXT").unwrap();
    Ok(page_contents_with_placeholder.replace("WMW_PLACEHOLDER_TEXT", &rendered_body))
}

fn serve_request(request: &mut Request) -> IronResult<Response> {
    if request.url.path.len() == 2 && request.url.path[0] == "wiki" {
        let mut response = Response::with(
            (iron::status::Ok, get_page_with_vandalism_restored(&request.url.path[1]).unwrap()));
        response.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
        Ok(response)
    } else {
        let client = Client::new();
        // TODO: error handling
        let mut wikipedia_response =
            client.get(&format!("https://en.wikipedia.org/{}", request.url.path.join("/")))
            .header(Connection::close())
            .send().unwrap();
        let mut wikipedia_body: Vec<u8> = Vec::new();
        wikipedia_response.read_to_end(&mut wikipedia_body);

        let mut response = Response::with(wikipedia_body);
        response.status = Some(wikipedia_response.status);
        response.headers = wikipedia_response.headers.clone();
        //response.body = wikipedia_body;
        println!("Forwarded request for {} to en.wikipedia.org", request.url.path.join("/"));
        Ok(response)
    }
}

fn main() {
    Iron::new(serve_request).http("localhost:3000").unwrap();
}

#[cfg(test)]
mod tests {
    use super::{merge, replace_node_with_placeholder};

    #[test]
    fn test_merge_clean() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed.\n";
        let new2 = "First line changed.\n\nSecond line.\n";
        assert_eq!("First line changed.\n\nSecond line changed.\n", merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_merge_conflicting() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed one way.\n";
        let new2 = "First line changed.\n\nSecond line changed a different way.\n";
        assert_eq!(None, merge(old, new1, new2));
    }

    #[test]
    fn test_merge_special_characters() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line 𐅃.\n";
        let new2 = "First line さようなら.\n\nSecond line.\n";
        assert_eq!("First line さようなら.\n\nSecond line 𐅃.\n", merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_replace_html_content() {
        let original_html = "

original text

Other text

";
        let expected_html = "

replaced text

Other text

";
        let processed_html = replace_node_with_placeholder(original_html, "mw-content-text", "replaced text").unwrap();
        assert_eq!(expected_html, processed_html);
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Write};
use std::f32::consts::PI;
use std::f32::INFINITY;

#[derive(Copy, Clone, Debug)]
pub struct Position(f32, f32);

impl Position {
    pub fn origin() -> Position {
        Position(0.0, 0.0)
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Degree(pub f32);

#[derive(Copy, Clone, Debug)]
pub struct Radiant(pub f32);

impl Into<Degree> for Radiant {
    fn into(self) -> Degree {
        Degree(self.0 * 180.0 / PI)
    }
}

impl Into<Radiant> for Degree {
    fn into(self) -> Radiant {
        Radiant(self.0 * PI / 180.0)
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Distance(f32);

impl Into<Distance> for f32 {
    fn into(self) -> Distance {
        Distance(self)
    }
}

pub trait Turtle {
    /// Move turtle forward by specified `distance`.
    fn forward<T: Into<Distance>>(&mut self, distance: T);

    /// Move turtle backward by specified `distance`.
    fn backward<T: Into<Distance>>(&mut self, distance: T);

    /// Turn turtle right by `angle` degree.
    fn right<T: Into<Degree>>(&mut self, angle: T);

    /// Turn turtle left by `angle` degree.
    fn left<T: Into<Degree>>(&mut self, angle: T);

    /// Put the pen down.
    fn pendown(&mut self);

    /// Put the pen up.
    fn penup(&mut self);

    fn goto(&mut self, pos: Position);

    fn home(&mut self) {
        self.goto(Position::origin());
    }

    /// Push current turtle state on stack.
    fn push(&mut self);

    /// Restore previously saved turtle state.
    fn pop(&mut self);
}

#[derive(Clone)]
pub struct TurtleState {
    pos: Position,
    angle: Degree,
    pendown: bool,
}

impl TurtleState {
    fn new() -> TurtleState {
        TurtleState {
            pos: Position::origin(),
            angle: Degree(0.0),
            pendown: false,
        }
    }
}

pub struct TurtleRecorder {
    states: Vec<TurtleState>,
    lines: Vec<(Position, Position)>,
}

impl TurtleRecorder {
    pub fn new() -> TurtleRecorder {
        TurtleRecorder {
            states: vec![TurtleState::new()],
            lines: vec![],
        }
    }

    #[inline]
    fn current_state_mut(&mut self) -> &mut TurtleState {
        self.states.last_mut().unwrap()
    }

    #[inline]
    fn current_state(&self) -> &TurtleState {
        self.states.last().unwrap()
    }

    #[inline]
    fn direction(&self, distance: Distance) -> (f32, f32) {
        let state = self.current_state();
        let rad: Radiant = state.angle.into();
        let dx = rad.0.cos() * distance.0;
        let dy = rad.0.sin() * distance.0;
        (dx, dy)
    }

    fn draw_line(&mut self, src: Position, dst: Position) {
        self.lines.push((src, dst));
    }

    /// Saves the turtle graphic as Scalable Vector Graphic (SVG).
    pub fn save_as_svg<W: Write>(&self, wr: &mut W) -> io::Result<()> {
        // Determine extend of canvas
        let mut min = Position(INFINITY, INFINITY);
        let mut max = Position(-INFINITY, -INFINITY);
        for &(ref src, ref dst) in self.lines.iter() {
            min.0 = min.0.min(src.0).min(dst.0);
            max.0 = max.0.max(src.0).max(dst.0);

            min.1 = min.1.min(src.1).min(dst.1);
            max.1 = max.1.max(src.1).max(dst.1);

        }
        let width = (max.0 - min.0).abs();
        let height = (max.1 - min.1).abs();

        let top_left = Position(min.0 - width / 10.0, min.1 - height / 10.0);
        let bottom_right = Position(max.0 + width / 10.0, max.1 + height / 10.0);

        try!(writeln!(wr,
                      r#"<?xml version="1.0" encoding="UTF-8"?>
                <svg xmlns="http://www.w3.org/2000/svg"
                version="1.1" baseProfile="full"
                width="100%" height="100%"
                viewBox="{} {} {} {}">"#,
                      top_left.0,
                      top_left.1,
                      bottom_right.0,
                      bottom_right.1));

        for &(ref src, ref dst) in self.lines.iter() {
            try!(writeln!(wr,
                          r#"<path d="M{} {} L{} {}" stroke="black" stroke-width="1"/>"#,
                          src.0,
                          src.1,
                          dst.0,
                          dst.1));
        }

        writeln!(wr, "</svg>")
    }
}

impl Turtle for TurtleRecorder {
    /// Move turtle forward by specified `distance`.
    fn forward<T: Into<Distance>>(&mut self, distance: T) {
        let (dx, dy) = self.direction(distance.into());
        let pendown = self.current_state().pendown;
        let src: Position = self.current_state().pos;
        let dst = Position(src.0 + dx, src.1 + dy);
        if pendown {
            self.draw_line(src, dst);
        }
        self.current_state_mut().pos = dst;
    }

    /// Move turtle backward by specified `distance`.
    fn backward<T: Into<Distance>>(&mut self, distance: T) {
        let distance: Distance = distance.into();
        self.forward(Distance(-distance.0))
    }

    /// Turn turtle right by `angle` degrees.
    fn right<T: Into<Degree>>(&mut self, angle: T) {
        let angle: Degree = angle.into();
        self.current_state_mut().angle.0 += angle.0;
    }

    /// Turn turtle left by `angle` degrees.
    fn left<T: Into<Degree>>(&mut self, angle: T) {
        let angle: Degree = angle.into();
        self.current_state_mut().angle.0 -= angle.0;
    }

    /// Put the pen down.
    fn pendown(&mut self) {
        self.current_state_mut().pendown = true;
    }

    /// Put the pen up.
    fn penup(&mut self) {
        self.current_state_mut().pendown = false;
    }

    fn goto(&mut self, pos: Position) {
        self.current_state_mut().pos = pos;
    }

    /// Push current turtle state on stack.
    fn push(&mut self) {
        let state = self.current_state_mut().clone();
        self.states.push(state);
    }

    /// Restore previously saved turtle state.
    fn pop(&mut self) {
        self.states.pop();
    }
}

fn main() {
    use std::fs::File;
    let mut t = TurtleRecorder::new();
    t.pendown();
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.save_as_svg(&mut File::create("test.svg").unwrap());
}
<commit_msg>Use f32::sin_cos()<commit_after>use std::io::{self, Write};
use std::f32::consts::PI;
use std::f32::INFINITY;

#[derive(Copy, Clone, Debug)]
pub struct Position(f32, f32);

impl Position {
    pub fn origin() -> Position {
        Position(0.0, 0.0)
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Degree(pub f32);

#[derive(Copy, Clone, Debug)]
pub struct Radiant(pub f32);

impl Into<Degree> for Radiant {
    fn into(self) -> Degree {
        Degree(self.0 * 180.0 / PI)
    }
}

impl Into<Radiant> for Degree {
    fn into(self) -> Radiant {
        Radiant(self.0 * PI / 180.0)
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Distance(f32);

impl Into<Distance> for f32 {
    fn into(self) -> Distance {
        Distance(self)
    }
}

pub trait Turtle {
    /// Move turtle forward by specified `distance`.
    fn forward<T: Into<Distance>>(&mut self, distance: T);

    /// Move turtle backward by specified `distance`.
    fn backward<T: Into<Distance>>(&mut self, distance: T);

    /// Turn turtle right by `angle` degree.
    fn right<T: Into<Degree>>(&mut self, angle: T);

    /// Turn turtle left by `angle` degree.
    fn left<T: Into<Degree>>(&mut self, angle: T);

    /// Put the pen down.
    fn pendown(&mut self);

    /// Put the pen up.
    fn penup(&mut self);

    fn goto(&mut self, pos: Position);

    fn home(&mut self) {
        self.goto(Position::origin());
    }

    /// Push current turtle state on stack.
    fn push(&mut self);

    /// Restore previously saved turtle state.
    fn pop(&mut self);
}

#[derive(Clone)]
pub struct TurtleState {
    pos: Position,
    angle: Degree,
    pendown: bool,
}

impl TurtleState {
    fn new() -> TurtleState {
        TurtleState {
            pos: Position::origin(),
            angle: Degree(0.0),
            pendown: false,
        }
    }
}

pub struct TurtleRecorder {
    states: Vec<TurtleState>,
    lines: Vec<(Position, Position)>,
}

impl TurtleRecorder {
    pub fn new() -> TurtleRecorder {
        TurtleRecorder {
            states: vec![TurtleState::new()],
            lines: vec![],
        }
    }

    #[inline]
    fn current_state_mut(&mut self) -> &mut TurtleState {
        self.states.last_mut().unwrap()
    }

    #[inline]
    fn current_state(&self) -> &TurtleState {
        self.states.last().unwrap()
    }

    #[inline]
    fn direction(&self, distance: Distance) -> (f32, f32) {
        let state = self.current_state();
        let rad: Radiant = state.angle.into();
        let (sin, cos) = rad.0.sin_cos();
        let dx = cos * distance.0;
        let dy = sin * distance.0;
        (dx, dy)
    }

    fn draw_line(&mut self, src: Position, dst: Position) {
        self.lines.push((src, dst));
    }

    /// Saves the turtle graphic as Scalable Vector Graphic (SVG).
    pub fn save_as_svg<W: Write>(&self, wr: &mut W) -> io::Result<()> {
        // Determine extend of canvas
        let mut min = Position(INFINITY, INFINITY);
        let mut max = Position(-INFINITY, -INFINITY);
        for &(ref src, ref dst) in self.lines.iter() {
            min.0 = min.0.min(src.0).min(dst.0);
            max.0 = max.0.max(src.0).max(dst.0);

            min.1 = min.1.min(src.1).min(dst.1);
            max.1 = max.1.max(src.1).max(dst.1);

        }
        let width = (max.0 - min.0).abs();
        let height = (max.1 - min.1).abs();

        let top_left = Position(min.0 - width / 10.0, min.1 - height / 10.0);
        let bottom_right = Position(max.0 + width / 10.0, max.1 + height / 10.0);

        try!(writeln!(wr,
                      r#"<?xml version="1.0" encoding="UTF-8"?>
                <svg xmlns="http://www.w3.org/2000/svg"
                version="1.1" baseProfile="full"
                width="100%" height="100%"
                viewBox="{} {} {} {}">"#,
                      top_left.0,
                      top_left.1,
                      bottom_right.0,
                      bottom_right.1));

        for &(ref src, ref dst) in self.lines.iter() {
            try!(writeln!(wr,
                          r#"<path d="M{} {} L{} {}" stroke="black" stroke-width="1"/>"#,
                          src.0,
                          src.1,
                          dst.0,
                          dst.1));
        }

        writeln!(wr, "</svg>")
    }
}

impl Turtle for TurtleRecorder {
    /// Move turtle forward by specified `distance`.
    fn forward<T: Into<Distance>>(&mut self, distance: T) {
        let (dx, dy) = self.direction(distance.into());
        let pendown = self.current_state().pendown;
        let src: Position = self.current_state().pos;
        let dst = Position(src.0 + dx, src.1 + dy);
        if pendown {
            self.draw_line(src, dst);
        }
        self.current_state_mut().pos = dst;
    }

    /// Move turtle backward by specified `distance`.
    fn backward<T: Into<Distance>>(&mut self, distance: T) {
        let distance: Distance = distance.into();
        self.forward(Distance(-distance.0))
    }

    /// Turn turtle right by `angle` degrees.
    fn right<T: Into<Degree>>(&mut self, angle: T) {
        let angle: Degree = angle.into();
        self.current_state_mut().angle.0 += angle.0;
    }

    /// Turn turtle left by `angle` degrees.
    fn left<T: Into<Degree>>(&mut self, angle: T) {
        let angle: Degree = angle.into();
        self.current_state_mut().angle.0 -= angle.0;
    }

    /// Put the pen down.
    fn pendown(&mut self) {
        self.current_state_mut().pendown = true;
    }

    /// Put the pen up.
    fn penup(&mut self) {
        self.current_state_mut().pendown = false;
    }

    fn goto(&mut self, pos: Position) {
        self.current_state_mut().pos = pos;
    }

    /// Push current turtle state on stack.
    fn push(&mut self) {
        let state = self.current_state_mut().clone();
        self.states.push(state);
    }

    /// Restore previously saved turtle state.
    fn pop(&mut self) {
        self.states.pop();
    }
}

fn main() {
    use std::fs::File;
    let mut t = TurtleRecorder::new();
    t.pendown();
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.save_as_svg(&mut File::create("test.svg").unwrap());
}
<|endoftext|>


<commit_before>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net;
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: net::SocketAddr) -> net::UdpSocket {
    let attempt = net::UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

pub fn send_message(send_addr: net::SocketAddr, target: net::SocketAddr, data: &[u8]) {
    let socket = socket(send_addr);
    let _ = socket.send_to(data, target);
}

fn main() {
    let command_vals = ["white", "red", "orange", "yellow", "green", "cyan", "blue", "purple",
                        "black", "question", "exclamation", "quit"];
    let matches = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    // bind to the correct UDP port
    let numeric_port = value_t!(matches.value_of("PORT"), u16).unwrap_or(1738);
    // it's safe to unwrap here, cos we already checked success
    let to_send = matches.value_of("COMMAND").unwrap();
    let ip = net::Ipv4Addr::new(127, 0, 0, 1);
    let listen_addr = net::SocketAddrV4::new(ip, numeric_port);
    let send_addr = net::SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(net::SocketAddr::V4(send_addr),
                 net::SocketAddr::V4(listen_addr),
                 to_send.as_bytes());
}
<commit_msg>Another rustfmt pass<commit_after>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net;
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: net::SocketAddr) -> net::UdpSocket {
    let attempt = net::UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

pub fn send_message(send_addr: net::SocketAddr, target: net::SocketAddr, data: &[u8]) {
    let socket = socket(send_addr);
    let _ = socket.send_to(data, target);
}

fn main() {
    let command_vals = ["white",
                        "red",
                        "orange",
                        "yellow",
                        "green",
                        "cyan",
                        "blue",
                        "purple",
                        "black",
                        "question",
                        "exclamation",
                        "quit"];
    let matches = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    // bind to the correct UDP port
    let numeric_port = value_t!(matches.value_of("PORT"), u16).unwrap_or(1738);
    // it's safe to unwrap here, cos we already checked success
    let to_send = matches.value_of("COMMAND").unwrap();
    let ip = net::Ipv4Addr::new(127, 0, 0, 1);
    let listen_addr = net::SocketAddrV4::new(ip, numeric_port);
    let send_addr = net::SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(net::SocketAddr::V4(send_addr),
                 net::SocketAddr::V4(listen_addr),
                 to_send.as_bytes());
}
<|endoftext|>


<commit_before>#![feature(duration)]
#![feature(duration_span)]

extern crate image;
extern crate rand;
extern crate docopt;
extern crate rustc_serialize as rustc_serialize;
extern crate threadpool;
extern crate num_cpus;
extern crate tray_rust;

use std::vec::Vec;
use std::sync::{Arc};
use std::sync::mpsc::{self, Sender, Receiver};
use std::path::Path;
use std::time::Duration;

use threadpool::ThreadPool;
use rand::StdRng;
use docopt::Docopt;

use tray_rust::film;
use tray_rust::geometry::Geometry;
use tray_rust::sampler::{self, Sampler};
use tray_rust::scene;
use tray_rust::integrator::Integrator;

static WIDTH: usize = 800;
static HEIGHT: usize = 600;
static USAGE: &'static str = "
Usage: tray_rust [options]

Options:
  -o <file>     Specify the output file to save the image. Supported formats are
                PNG, JPG and PPM. Default is 'out.png'.
  -n <number>   Specify the number of threads to use for rendering. Defaults to the number of cores
                on the system.
  -h, --help    Show this message.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_o: Option<String>,
    flag_n: Option<usize>,
}

// TODO: AREA LIGHTS NOTES:
// - They should be intersectable so they're like wierd instances.
// - What if point lights were also instances that just always returned None
//   for intersection tests?
// - Then each thread would build a list of &Instance that refer to the emissive
//   instances in the scene and we'd pass this around for the various lighting calculations.
//   It's a bit ugly but maybe cleaner than what was done previously in tray
//   It breaks the awkward handling of mixing point and area lights with each other which
//   is a plus.

/// Threads are each sent a sender end of the channel that is
/// read from by the render target thread which then saves the
/// values recieved to the render target
fn thread_work(tx: Sender<(f32, f32, film::Colorf)>, queue: Arc<sampler::BlockQueue>,
               scene: Arc<scene::Scene>) {
    let mut sampler = sampler::LowDiscrepancy::new(queue.block_dim(), 4);
    let mut samples = Vec::with_capacity(sampler.max_spp());
    let mut sample_pos = Vec::with_capacity(sampler.max_spp());
    let mut rng = match StdRng::new() {
        Ok(r) => r,
        Err(e) => { println!("Failed to get StdRng, {}", e); return }
    };
    // Grab a block from the queue and start working on it, submitting samples
    // to the render target thread after each pixel
    for b in queue.iter() {
        sampler.select_block(b);
        while sampler.has_samples() {
            // Get samples for a pixel and render them
            sampler.get_samples(&mut sample_pos, &mut rng);
            for s in sample_pos.iter() {
                let mut ray = scene.camera.generate_ray(s);
                if let Some(hit) = scene.intersect(&mut ray) {
                    let c = scene.integrator.illumination(&*scene, &ray, &hit, &mut sampler, &mut rng).clamp();
                    samples.push((s.0, s.1, c));
                }
            }
            for s in samples.iter() {
                if let Err(e) = tx.send(*s) {
                    println!("Worker thread exiting with send error {:?}", e);
                    return;
                }
            }
            samples.clear();
        }
    }
}

/// Spawn `n` worker threads to render the scene in parallel. Returns the receive end
/// of the channel where the threads will write their samples so that the receiver
/// can write these samples to the render target
fn spawn_workers(pool: &ThreadPool, n: usize, scene: Arc<scene::Scene>) -> Receiver<(f32, f32, film::Colorf)> {
    let (tx, rx) = mpsc::channel();
    let block_queue = Arc::new(sampler::BlockQueue::new((WIDTH as u32, HEIGHT as u32), (8, 8)));
    for _ in 0..n {
        let q = block_queue.clone();
        let t = tx.clone();
        let s = scene.clone();
        pool.execute(move || {
            thread_work(t, q, s);
        });
    }
    rx
}

/// Render the scene in parallel to the render target
fn render_parallel(rt: &mut film::RenderTarget, n: usize){
    let scene = Arc::new(scene::Scene::new(WIDTH, HEIGHT));
    let pool = ThreadPool::new(n);
    let rx = spawn_workers(&pool, n, scene);
    for m in rx.iter() {
        rt.write(m.0, m.1, &m.2);
    }
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    let mut rt = film::RenderTarget::new(WIDTH, HEIGHT);
    let n = match args.flag_n {
        Some(n) => n,
        None => num_cpus::get(),
    };
    println!("Rendering using {} threads", n);
    let d = Duration::span(|| render_parallel(&mut rt, n));
    println!("Rendering took {}", d);
    let img = rt.get_render();
    let out_file = match args.flag_o {
        Some(f) => f,
        None => "out.png".to_string(),
    };
    match image::save_buffer(&Path::new(&out_file), &img[..], WIDTH as u32, HEIGHT as u32, image::RGB(8)) {
        Ok(_) => {},
        Err(e) => println!("Error saving image, {}", e),
    };
}

<commit_msg>Some thoughts on how to do AABB for point lights so they can be in BVH<commit_after>#![feature(duration)]
#![feature(duration_span)]

extern crate image;
extern crate rand;
extern crate docopt;
extern crate rustc_serialize as rustc_serialize;
extern crate threadpool;
extern crate num_cpus;
extern crate tray_rust;

use std::vec::Vec;
use std::sync::{Arc};
use std::sync::mpsc::{self, Sender, Receiver};
use std::path::Path;
use std::time::Duration;

use threadpool::ThreadPool;
use rand::StdRng;
use docopt::Docopt;

use tray_rust::film;
use tray_rust::geometry::Geometry;
use tray_rust::sampler::{self, Sampler};
use tray_rust::scene;
use tray_rust::integrator::Integrator;

static WIDTH: usize = 800;
static HEIGHT: usize = 600;
static USAGE: &'static str = "
Usage: tray_rust [options]

Options:
  -o <file>     Specify the output file to save the image. Supported formats are
                PNG, JPG and PPM. Default is 'out.png'.
  -n <number>   Specify the number of threads to use for rendering. Defaults to the number of cores
                on the system.
  -h, --help    Show this message.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_o: Option<String>,
    flag_n: Option<usize>,
}

// TODO: AREA LIGHTS NOTES:
// - They should be intersectable so they're like wierd instances.
// - What if point lights were also instances that just always returned None
//   for intersection tests?
// - Then each thread would build a list of &Instance that refer to the emissive
//   instances in the scene and we'd pass this around for the various lighting calculations.
//   It's a bit ugly but maybe cleaner than what was done previously in tray
//   It breaks the awkward handling of mixing point and area lights with each other which
//   is a plus. I guess we can also put point lights in the BVH and their bounds would
//   just be the point they're located at.

/// Threads are each sent a sender end of the channel that is
/// read from by the render target thread which then saves the
/// values recieved to the render target
fn thread_work(tx: Sender<(f32, f32, film::Colorf)>, queue: Arc<sampler::BlockQueue>,
               scene: Arc<scene::Scene>) {
    let mut sampler = sampler::LowDiscrepancy::new(queue.block_dim(), 4);
    let mut samples = Vec::with_capacity(sampler.max_spp());
    let mut sample_pos = Vec::with_capacity(sampler.max_spp());
    let mut rng = match StdRng::new() {
        Ok(r) => r,
        Err(e) => { println!("Failed to get StdRng, {}", e); return }
    };
    // Grab a block from the queue and start working on it, submitting samples
    // to the render target thread after each pixel
    for b in queue.iter() {
        sampler.select_block(b);
        while sampler.has_samples() {
            // Get samples for a pixel and render them
            sampler.get_samples(&mut sample_pos, &mut rng);
            for s in sample_pos.iter() {
                let mut ray = scene.camera.generate_ray(s);
                if let Some(hit) = scene.intersect(&mut ray) {
                    let c = scene.integrator.illumination(&*scene, &ray, &hit, &mut sampler, &mut rng).clamp();
                    samples.push((s.0, s.1, c));
                }
            }
            for s in samples.iter() {
                if let Err(e) = tx.send(*s) {
                    println!("Worker thread exiting with send error {:?}", e);
                    return;
                }
            }
            samples.clear();
        }
    }
}

/// Spawn `n` worker threads to render the scene in parallel. Returns the receive end
/// of the channel where the threads will write their samples so that the receiver
/// can write these samples to the render target
fn spawn_workers(pool: &ThreadPool, n: usize, scene: Arc<scene::Scene>) -> Receiver<(f32, f32, film::Colorf)> {
    let (tx, rx) = mpsc::channel();
    let block_queue = Arc::new(sampler::BlockQueue::new((WIDTH as u32, HEIGHT as u32), (8, 8)));
    for _ in 0..n {
        let q = block_queue.clone();
        let t = tx.clone();
        let s = scene.clone();
        pool.execute(move || {
            thread_work(t, q, s);
        });
    }
    rx
}

/// Render the scene in parallel to the render target
fn render_parallel(rt: &mut film::RenderTarget, n: usize){
    let scene = Arc::new(scene::Scene::new(WIDTH, HEIGHT));
    let pool = ThreadPool::new(n);
    let rx = spawn_workers(&pool, n, scene);
    for m in rx.iter() {
        rt.write(m.0, m.1, &m.2);
    }
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    let mut rt = film::RenderTarget::new(WIDTH, HEIGHT);
    let n = match args.flag_n {
        Some(n) => n,
        None => num_cpus::get(),
    };
    println!("Rendering using {} threads", n);
    let d = Duration::span(|| render_parallel(&mut rt, n));
    println!("Rendering took {}", d);
    let img = rt.get_render();
    let out_file = match args.flag_o {
        Some(f) => f,
        None => "out.png".to_string(),
    };
    match image::save_buffer(&Path::new(&out_file), &img[..], WIDTH as u32, HEIGHT as u32, image::RGB(8)) {
        Ok(_) => {},
        Err(e) => println!("Error saving image, {}", e),
    };
}

<|endoftext|>


<commit_before>//! Uniform point distribution in an annulus using Bridson’s algorithm for
//! Poisson-disc sampling.
//!
//! Bridson, Robert. "Fast Poisson disk sampling in arbitrary dimensions." ACM
//! SIGGRAPH. Vol. 2007. 2007.
//!
//! See also [Visualizing Algorithms](http://bost.ocks.org/mike/algorithms/).
//!
//! Uses [qhull](http://qhull.org/) to generate a Delaunay triangulation.
extern crate docopt;
extern crate gnuplot;
extern crate mersenne_twister;
extern crate rand;
extern crate rustc_serialize;

mod annulus_distribution;
pub mod blue_noise;

use docopt::Docopt;
#[allow(unused_imports)]
use gnuplot::{
    AxesCommon, Caption, Color, Coordinate, DashType, Figure, Fix, PlotOption, TextColor,
};
use mersenne_twister::MT19937_64;
use rand::SeedableRng;
use std::f64::consts::PI;
use std::io::{BufRead, Cursor, Write};
use std::ops::Add;
use std::process::{Command, Stdio};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

static USAGE: &'static str = r"
Simple annulus triangulation generator.

Usage: annulus_mesh [options]
       annulus_mesh (-h | --help | --version)

Options:
    -r INNER        Inner radius [default: 0.2]
    -d DIST         Point distance [default: 0.2]
    -h, --help      Print help
    --plot FILE     Output mesh plot
    --no-show       Do not plot anything
    --version       Print version
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_d: f64,
    flag_r: f64,
    flag_no_show: bool,
    flag_plot: Option<String>,
}

/// A point in 2D.
#[derive(Copy, Clone, Debug)]
pub struct Point(f64, f64);

impl Add for Point {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        Point(self.0 + rhs.0, self.1 + rhs.1)
    }
}

impl Point {
    /// Distance between two points.
    fn dist(self, y: Point) -> f64 {
        self.dist_sq(y).sqrt()
    }

    /// The square of the distance between two points.
    fn dist_sq(self, y: Point) -> f64 {
        (self.0 - y.0).powi(2) + (self.1 - y.1).powi(2)
    }
}

/// Compute the Delaunay triangulation of a set of points by running them through `qhull`.
///
/// # Panics
///
/// Panics if `qhull` command is not available or fails.
fn qhull_triangulation(ps: &[Point]) -> Vec<Vec<usize>> {
    let mut process = Command::new("qhull")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .arg("d")
        .arg("i")
        .spawn()
        .unwrap_or_else(|e| panic!("couldn't spawn qhull: {:?}", e));

    if let Some(ref mut out) = process.stdin {
        writeln!(out, "2\n{}", ps.len()).unwrap();

        for &Point(x, y) in ps {
            writeln!(out, "{} {}", x, y).unwrap();
        }
    }

    // process.stdout.unwrap().read_to_string(&mut s);
    let output = process.wait_with_output().unwrap();

    let mut lines = Cursor::new(output.stdout).lines();

    let n = lines.next().unwrap().unwrap().parse::<usize>().unwrap();

    let mut res = vec![];

    for line in lines {
        let s = line.unwrap();
        let poly: Vec<usize> = s
            .split_whitespace()
            .map(|e| e.parse::<usize>().unwrap())
            .collect();
        res.push(poly);
    }

    assert_eq!(res.len(), n);
    res
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.version(Some(VERSION.to_string())).decode())
        .unwrap_or_else(|e| e.exit());

    let seed: u64 = 0;
    let mut rng: MT19937_64 = SeedableRng::from_seed(seed);

    let mut ps = vec![];

    let r1 = args.flag_r;
    let r2 = 1.;

    let h = args.flag_d;
    let mean_dist_ratio = 0.7;

    // generate boundary points
    for &r in &[r1, r2] {
        let k = (mean_dist_ratio * 2. * PI * r / h).round() as i32;
        for i in 0..k {
            let (y, x) = (2. * PI * i as f64 / k as f64).sin_cos();
            ps.push(Point(r * x, r * y));
        }
    }

    let n_fixed = ps.len();

    blue_noise::generate_blue_noise_cull(
        &mut rng,
        &mut ps,
        h,
        (Point(-r2, -r2), Point(r2, r2)),
        |p| {
            let d = p.dist(Point(0., 0.));
            r1 <= d && d <= r2
        },
    );

    println!("Generated {} nodes", ps.len());

    let indices = qhull_triangulation(&ps);

    println!("Delaunay triangulation has {} triangles", indices.len());

    let mut neighbors: Vec<Vec<usize>> = ps.iter().map(|_| vec![]).collect();
    for poly in &indices {
        for (&i, &j) in poly.iter().zip(poly.iter().cycle().skip(1)) {
            neighbors[i].push(j);
            neighbors[j].push(i);
        }
    }

    for list in &mut neighbors {
        list.sort();
        list.dedup();
    }

    // Laplace smoothing
    let n_laplace_step = 1;

    let laplace_eps = 1.0e-3;

    for _ in 0..n_laplace_step {
        let mut change = 0.;
        // Gauss-Seidel
        for i in n_fixed..ps.len() {
            let mut x = 0.;
            let mut y = 0.;
            for &j in &neighbors[i] {
                x += ps[j].0;
                y += ps[j].1;
            }
            let n = neighbors[i].len() as f64;
            let p = ps[i];
            let q = Point(x / n, y / n);
            ps[i] = q;
            change += p.dist_sq(q);
        }

        if change.sqrt() < laplace_eps {
            break;
        }
    }

    if !args.flag_no_show {
        let mut fg = Figure::new();
        if let Some(file) = args.flag_plot {
            fg.set_terminal("pngcairo size 720, 720", &file);
        }

        fg.clear_axes();
        {
            let axes = fg.axes2d();
            axes.set_aspect_ratio(Fix(1.));
            for poly in indices {
                axes.lines(
                    poly.iter()
                        .cycle()
                        .take(poly.len() + 1)
                        .map(|&i| ps[i as usize].0),
                    poly.iter()
                        .cycle()
                        .take(poly.len() + 1)
                        .map(|&i| ps[i as usize].1),
                    &[],
                );
            }
        }

        fg.show();
    }
}
<commit_msg>Add laplace smoothing steps param<commit_after>//! Uniform point distribution in an annulus using Bridson’s algorithm for
//! Poisson-disc sampling.
//!
//! Bridson, Robert. "Fast Poisson disk sampling in arbitrary dimensions." ACM
//! SIGGRAPH. Vol. 2007. 2007.
//!
//! See also [Visualizing Algorithms](http://bost.ocks.org/mike/algorithms/).
//!
//! Uses [qhull](http://qhull.org/) to generate a Delaunay triangulation.
extern crate docopt;
extern crate gnuplot;
extern crate mersenne_twister;
extern crate rand;
extern crate rustc_serialize;

mod annulus_distribution;
pub mod blue_noise;

use docopt::Docopt;
#[allow(unused_imports)]
use gnuplot::{
    AxesCommon, Caption, Color, Coordinate, DashType, Figure, Fix, PlotOption, TextColor,
};
use mersenne_twister::MT19937_64;
use rand::SeedableRng;
use std::f64::consts::PI;
use std::io::{BufRead, Cursor, Write};
use std::ops::Add;
use std::process::{Command, Stdio};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

static USAGE: &'static str = r"
Simple annulus triangulation generator.

Usage: annulus_mesh [options]
       annulus_mesh (-h | --help | --version)

Options:
    -r INNER        Inner radius [default: 0.2]
    -d DIST         Point distance [default: 0.2]
    -l NUM          Laplace smoothing steps [default: 1]
    -h, --help      Print help
    --plot FILE     Output mesh plot
    --no-show       Do not plot anything
    --version       Print version
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_d: f64,
    flag_r: f64,
    flag_l: u32,
    flag_no_show: bool,
    flag_plot: Option<String>,
}

/// A point in 2D.
#[derive(Copy, Clone, Debug)]
pub struct Point(f64, f64);

impl Add for Point {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        Point(self.0 + rhs.0, self.1 + rhs.1)
    }
}

impl Point {
    /// Distance between two points.
    fn dist(self, y: Point) -> f64 {
        self.dist_sq(y).sqrt()
    }

    /// The square of the distance between two points.
    fn dist_sq(self, y: Point) -> f64 {
        (self.0 - y.0).powi(2) + (self.1 - y.1).powi(2)
    }
}

/// Compute the Delaunay triangulation of a set of points by running them through `qhull`.
///
/// # Panics
///
/// Panics if `qhull` command is not available or fails.
fn qhull_triangulation(ps: &[Point]) -> Vec<Vec<usize>> {
    let mut process = Command::new("qhull")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .arg("d")
        .arg("i")
        .spawn()
        .unwrap_or_else(|e| panic!("couldn't spawn qhull: {:?}", e));

    if let Some(ref mut out) = process.stdin {
        writeln!(out, "2\n{}", ps.len()).unwrap();

        for &Point(x, y) in ps {
            writeln!(out, "{} {}", x, y).unwrap();
        }
    }

    // process.stdout.unwrap().read_to_string(&mut s);
    let output = process.wait_with_output().unwrap();

    let mut lines = Cursor::new(output.stdout).lines();

    let n = lines.next().unwrap().unwrap().parse::<usize>().unwrap();

    let mut res = vec![];

    for line in lines {
        let s = line.unwrap();
        let poly: Vec<usize> = s
            .split_whitespace()
            .map(|e| e.parse::<usize>().unwrap())
            .collect();
        res.push(poly);
    }

    assert_eq!(res.len(), n);
    res
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.version(Some(VERSION.to_string())).decode())
        .unwrap_or_else(|e| e.exit());

    let seed: u64 = 0;
    let mut rng: MT19937_64 = SeedableRng::from_seed(seed);

    let mut ps = vec![];

    let r1 = args.flag_r;
    let r2 = 1.;

    let h = args.flag_d;
    let mean_dist_ratio = 0.7;

    // generate boundary points
    for &r in &[r1, r2] {
        let k = (mean_dist_ratio * 2. * PI * r / h).round() as i32;
        for i in 0..k {
            let (y, x) = (2. * PI * i as f64 / k as f64).sin_cos();
            ps.push(Point(r * x, r * y));
        }
    }

    let n_fixed = ps.len();

    blue_noise::generate_blue_noise_cull(
        &mut rng,
        &mut ps,
        h,
        (Point(-r2, -r2), Point(r2, r2)),
        |p| {
            let d = p.dist(Point(0., 0.));
            r1 <= d && d <= r2
        },
    );

    println!("Generated {} nodes", ps.len());

    let indices = qhull_triangulation(&ps);

    println!("Delaunay triangulation has {} triangles", indices.len());

    let mut neighbors: Vec<Vec<usize>> = ps.iter().map(|_| vec![]).collect();
    for poly in &indices {
        for (&i, &j) in poly.iter().zip(poly.iter().cycle().skip(1)) {
            neighbors[i].push(j);
            neighbors[j].push(i);
        }
    }

    for list in &mut neighbors {
        list.sort();
        list.dedup();
    }

    // Laplace smoothing
    let n_laplace_step = args.flag_l;

    let laplace_eps = 1.0e-3;

    for _ in 0..n_laplace_step {
        let mut change = 0.;
        // Gauss-Seidel
        for i in n_fixed..ps.len() {
            let mut x = 0.;
            let mut y = 0.;
            for &j in &neighbors[i] {
                x += ps[j].0;
                y += ps[j].1;
            }
            let n = neighbors[i].len() as f64;
            let p = ps[i];
            let q = Point(x / n, y / n);
            ps[i] = q;
            change += p.dist_sq(q);
        }

        if change.sqrt() < laplace_eps {
            break;
        }
    }

    if !args.flag_no_show {
        let mut fg = Figure::new();
        if let Some(file) = args.flag_plot {
            fg.set_terminal("pngcairo size 720, 720", &file);
        }

        fg.clear_axes();
        {
            let axes = fg.axes2d();
            axes.set_aspect_ratio(Fix(1.));
            for poly in indices {
                axes.lines(
                    poly.iter()
                        .cycle()
                        .take(poly.len() + 1)
                        .map(|&i| ps[i as usize].0),
                    poly.iter()
                        .cycle()
                        .take(poly.len() + 1)
                        .map(|&i| ps[i as usize].1),
                    &[],
                );
            }
        }

        fg.show();
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

use std::str;
use nom::{IResult, space, hex_digit};

named!(statement, take_until_either_and_consume!("."));

fn is_pn_chars_base(c: char) -> bool {
    let u = c as u32;
    match u {
        0x41    ... 0x5A   | 0x61   ... 0x7A   |
        0xC0    ... 0xD6   | 0xD8   ... 0xF6   |
        0xF8    ... 0x2FF  | 0x370  ... 0x37D  |
        0x37F   ... 0x1FFF | 0x200C ... 0x200D |
        0x2C00  ... 0x2FEF | 0x3001 ... 0xD7FF |
        0xF900  ... 0xFDCF | 0xFDF0 ... 0xFFFD |
        0x10000 ... 0xEFFFF
          => true,
        _ => false,
    }
}
fn is_pn_chars_u(c: char) -> bool {
    is_pn_chars_base(c) || c == '_'
}
fn is_pn_chars(c: char) -> bool {
    let u = c as u32;
    match u {
        0x2d | 0xB7 | 0x30...0x39 | 0x300...0x36F | 0x203F...0x2040
          => true,
        _ => is_pn_chars_base(c),
    }
}
fn is_pn_local_esc(c: char) -> bool {
    match "_~.-!$&'()*+,;=/?#@%".find(c) {
        Some(_) => true,
        _       => false,
    }
}

/* [163s] PN_CHARS_BASE */
named!(pn_chars_base<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars_base(c)
        }
        false
    }
));

/* [164s] PN_CHARS_U */
named!(pn_chars_u<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars_u(c)
        }
        false
    }
));

/* [166s] PN_CHARS */
named!(pn_chars<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars(c)
        }
        false
    }
));

/* [169s] PLX */
named!(plx<&str, &str>, alt!(percent | pn_local_esc));

/* [170s] PERCENT */
named!(percent<&str, &str>, verify!(
    take_s!(3),
    |val:&str| {
        let bytes = val.as_bytes();
        bytes[0] == 0x25 && is_hex_digit(bytes[1]) && is_hex_digit(bytes[2])
    }
));

/* [172s]  PN_LOCAL_ESC */
named!(pn_local_esc<&str, &str>, verify!(
    take_s!(2),
    |val:&str| {
        let c1 = val.chars().nth(0).unwrap();
        let c2 = val.chars().nth(1).unwrap();
        c1 == '\\' && is_pn_local_esc(c2)
    }
));

fn main() {
    assert_eq!(statement(&b"a b c. d e f."[..]), IResult::Done(&b" d e f."[..], &b"a b c"[..]));
    assert_eq!(pn_chars_base("a%2Ab")          , IResult::Done("%2Ab"         , "a"          ));
    assert_eq!(pn_chars_u("_a")                , IResult::Done("a"            , "_"          ));
    assert_eq!(pn_chars("-_a")                 , IResult::Done("_a"           , "-"          ));
    assert_eq!(plx(&b"%2Abc"[..])              , IResult::Done(&b"bc"[..]     , &b"2A"[..]   ));
    assert_eq!(plx(&b"\\.%2A"[..])             , IResult::Done(&b"%2A"[..]    , &b"\\."[..]  ));
    assert_eq!(percent(&b"%2Abc"[..])          , IResult::Done(&b"bc"[..]     , &b"2A"[..]   ));
    assert_eq!(pn_local_esc(&b"\\.a b c"[..])  , IResult::Done(&b"a b c"[..]  , &b"\\."[..]  ));
}
<commit_msg>Implement PN_PREFIX<commit_after>#[macro_use]
extern crate nom;

use std::str;
use nom::{
    IResult,
    ErrorKind,
    is_hex_digit
};

fn is_pn_chars_base(c: char) -> bool {
    let u = c as u32;
    match u {
        0x41    ... 0x5A   | 0x61   ... 0x7A   |
        0xC0    ... 0xD6   | 0xD8   ... 0xF6   |
        0xF8    ... 0x2FF  | 0x370  ... 0x37D  |
        0x37F   ... 0x1FFF | 0x200C ... 0x200D |
        0x2C00  ... 0x2FEF | 0x3001 ... 0xD7FF |
        0xF900  ... 0xFDCF | 0xFDF0 ... 0xFFFD |
        0x10000 ... 0xEFFFF
          => true,
        _ => false,
    }
}
fn is_pn_chars_u(c: char) -> bool {
    is_pn_chars_base(c) || c == '_'
}
fn is_pn_chars(c: char) -> bool {
    let u = c as u32;
    match u {
        0x2d | 0xB7 | 0x30...0x39 | 0x300...0x36F | 0x203F...0x2040
          => true,
        _ => is_pn_chars_base(c),
    }
}
fn is_pn_local_esc(c: char) -> bool {
    match "_~.-!$&'()*+,;=/?#@%".find(c) {
        Some(_) => true,
        _       => false,
    }
}

/* [163s] PN_CHARS_BASE */
named!(pn_chars_base<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars_base(c)
        }
        false
    }
));

/* [164s] PN_CHARS_U */
named!(pn_chars_u<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars_u(c)
        }
        false
    }
));

/* [166s] PN_CHARS */
named!(pn_chars<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars(c)
        }
        false
    }
));

/* [167s] PN_PREFIX */
named!(pn_prefix<&str, &str>, do_parse!(
    base:  pn_chars_base    >>
    chars: many0!(
        alt!(pn_chars)
    ) >>
    colon: verify!(take_s!(1), |val:&str| val == ":") >>
    (base) // TODO: Fix me
));

/* [169s] PLX */
named!(plx<&str, &str>, alt!(percent | pn_local_esc));

/* [170s] PERCENT */
named!(percent<&str, &str>, verify!(
    take_s!(3),
    |val:&str| {
        let bytes = val.as_bytes();
        bytes[0] == 0x25 && is_hex_digit(bytes[1]) && is_hex_digit(bytes[2])
    }
));

/* [172s]  PN_LOCAL_ESC */
named!(pn_local_esc<&str, &str>, verify!(
    take_s!(2),
    |val:&str| {
        let c1 = val.chars().nth(0).unwrap();
        let c2 = val.chars().nth(1).unwrap();
        c1 == '\\' && is_pn_local_esc(c2)
    }
));

fn main() {
    assert_eq!(pn_chars_base("a%2Ab")  , IResult::Done("%2Ab" , "a")      );
    assert_eq!(pn_chars_u("_a")        , IResult::Done("a"    , "_")      );
    assert_eq!(pn_chars("-_a")         , IResult::Done("_a"   , "-")      );
    assert_eq!(pn_chars(":a2Ab")       , IResult::Error(ErrorKind::Verify));
    assert_eq!(plx("%2Abc")            , IResult::Done("bc"   , "%2A")    );
    assert_eq!(plx("\\.%2A")           , IResult::Done("%2A"  , "\\.")    );
    assert_eq!(percent("%2Abc")        , IResult::Done("bc"   , "%2A")    );
    assert_eq!(pn_local_esc("\\.a b c"), IResult::Done("a b c", "\\.")    );
}
<|endoftext|>


<commit_before>#![warn(bad_style)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
// #![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unused)]
#![warn(unused_extern_crates)]
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
#![warn(unused_results)]

extern crate board_game_geom as geom;
#[macro_use]
extern crate conrod;
extern crate find_folder;
extern crate piston_window;
extern crate vecmath;

use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;

use geom::{Point, Size};

use conrod::{Canvas, Circle, Frameable, Sizeable, Text, Theme, Widget, WidgetMatrix};
use conrod::color::{self, Color, Colorable};
use conrod::Positionable;

use piston_window::{Glyphs, PistonWindow, UpdateEvent, WindowSettings};

use board::Board;
use othello_disk::OthelloDisk;

type Ui = conrod::Ui<Glyphs>;

mod board;
mod othello_disk;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Side {
    Black,
    White,
}

impl Side {
    fn flip(self) -> Side {
        match self {
            Side::Black => Side::White,
            Side::White => Side::Black,
        }
    }
}

#[derive(Clone, Debug)]
struct App {
    board: Board,

    frame_width: f64,
    cell_size: f64,
    disk_radius: f64,
    dot_radius: f64,
    board_margin: f64,
    indicator_text_width: f64,

    frame_color: Color,
    board_color: Color,
    white_color: Color,
    black_color: Color,
}

impl Default for App {
    fn default() -> App {
        App {
            board: Board::new(Size(8, 8)),

            frame_width: 1.0,
            cell_size: 80.0,
            disk_radius: 32.0,
            dot_radius: 6.0,
            board_margin: 40.0,
            indicator_text_width: 90.0,

            frame_color: color::black(),
            board_color: color::rgba(0.0, 0.5, 0.0, 1.0),
            white_color: color::white(),
            black_color: color::black(),
        }
    }
}

fn main() {
    let app_ref = Rc::new(RefCell::new(App::default()));

    let window: PistonWindow = {
        let app = app_ref.deref().borrow();

        let board_width = app.cell_size * (app.board.size().1 as f64);
        let indicator_width = app.cell_size + app.indicator_text_width;
        let width = board_width + app.board_margin * 2.0 + indicator_width + app.board_margin;

        let board_height = app.cell_size * (app.board.size().0 as f64);
        let indicator_height = app.cell_size * 2.0;
        let height = app.board_margin * 2.0 + f64::max(board_height, indicator_height);

        WindowSettings::new("Othello", (width as u32, height as u32))
            .exit_on_esc(true)
            .vsync(true)
            .build()
            .unwrap_or_else(|e| panic!("Failed to build PistonWindow: {}", e))
    };

    let mut ui = {
        let assets = find_folder::Search::KidsThenParents(3, 5)
                         .for_folder("assets")
                         .unwrap();
        let ref font_path = assets.join("FiraSans-Regular.ttf");
        let theme = Theme::default();
        let factory = window.factory.borrow().clone();
        let glyph_cache = Glyphs::new(font_path, factory).unwrap();
        Ui::new(glyph_cache, theme)
    };

    for event in window {
        ui.handle_event(&event);

        let _ = event.update(|_| ui.set_widgets(|ui| set_widgets(ui, app_ref.clone())));
        event.draw_2d(|c, g| ui.draw_if_changed(c, g));
    }
}

widget_ids! {
    CANVAS,
    BOARD,
    DOT with 4,
    INDICATOR_LABEL_ICON with 2,
    INDICATOR_LABEL_TEXT with 2,
}

fn set_widgets(ui: &mut Ui, app_ref: Rc<RefCell<App>>) {
    let Size(rows, cols) = {
        let app = app_ref.deref().borrow();
        Canvas::new().color(app.board_color).set(CANVAS, ui);
        app.board.size()
    };

    let matrix = {
        let app = app_ref.deref().borrow();
        WidgetMatrix::new(cols as usize, rows as usize)
            .top_left_with_margins_on(CANVAS, app.board_margin, app.board_margin)
            .w_h(app.cell_size * (cols as f64), app.cell_size * (rows as f64))
    };

    matrix.each_widget(|_n, col, row| {
              let pt = Point(row as i32, col as i32);
              let disk = {
                  let app_ref = app_ref.clone();
                  let app = app_ref.deref().borrow();
                  let mut disk = OthelloDisk::new()
                                     .background_color(app.board_color)
                                     .frame(app.frame_width)
                                     .frame_color(app.frame_color)
                                     .white_color(app.white_color)
                                     .black_color(app.black_color)
                                     .radius(app.disk_radius)
                                     .disk(app.board[pt]);
                  if let Some(turn) = app.board.turn() {
                      if app.board.can_locate(pt) {
                          disk = disk.flow_disk(Some(turn));
                      }
                  }
                  disk
              };

              let app_ref = app_ref.clone();
              disk.react(move || {
                  app_ref.borrow_mut().board.locate(pt);
              })
          })
          .set(BOARD, ui);

    {
        let app = app_ref.deref().borrow();
        let x = app.cell_size * ((cols / 4) as f64);
        let y = app.cell_size * ((rows / 4) as f64);
        let signs = &[(-1.0, 1.0), (1.0, 1.0), (-1.0, -1.0), (1.0, -1.0)];
        for (i, &(sx, sy)) in signs.iter().enumerate() {
            Circle::fill(app.dot_radius)
                .x_y_relative_to(BOARD, sx * x, sy * y)
                .color(app.frame_color)
                .set(DOT + i, ui);
        }
    }

    for (i, &side) in [Side::Black, Side::White].iter().enumerate() {
        let app = app_ref.deref().borrow();
        if side == Side::Black {
            OthelloDisk::new().right_from(BOARD, app.board_margin)
        } else {
            OthelloDisk::new().down_from(INDICATOR_LABEL_ICON + (i - 1), 0.0)
        }
        .w_h(app.cell_size, app.cell_size)
        .background_color(app.board_color)
        .frame(0.0)
        .white_color(app.white_color)
        .black_color(app.black_color)
        .radius(app.disk_radius)
        .disk(Some(side))
        .react(|| {})
        .set(INDICATOR_LABEL_ICON + i, ui);

        let text = match side {
            Side::Black => app.board.num_black().to_string(),
            Side::White => app.board.num_white().to_string(),
        };
        Text::new(&text)
            .w(app.indicator_text_width)
            .right_from(INDICATOR_LABEL_ICON + i, 0.0)
            .font_size(60)
            .align_text_right()
            .set(INDICATOR_LABEL_TEXT + i, ui);
    }
}
<commit_msg>support variable window size<commit_after>#![warn(bad_style)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
// #![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unused)]
#![warn(unused_extern_crates)]
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
#![warn(unused_results)]

extern crate board_game_geom as geom;
#[macro_use]
extern crate conrod;
extern crate find_folder;
extern crate piston_window;
extern crate vecmath;

use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;

use geom::{Point, Size};

use conrod::{Canvas, Circle, Frameable, LineStyle, Rectangle, Sizeable, Text, Theme, Widget,
             WidgetMatrix};
use conrod::color::{self, Color, Colorable};
use conrod::Positionable;

use piston_window::{Glyphs, PistonWindow, UpdateEvent, WindowSettings};

use board::Board;
use othello_disk::OthelloDisk;

type Ui = conrod::Ui<Glyphs>;

mod board;
mod othello_disk;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Side {
    Black,
    White,
}

impl Side {
    fn flip(self) -> Side {
        match self {
            Side::Black => Side::White,
            Side::White => Side::Black,
        }
    }
}

#[derive(Clone, Debug)]
struct App {
    board: Board,

    frame_width: f64,
    cell_size: f64,
    disk_radius: f64,
    dot_radius: f64,
    board_margin: f64,
    indicator_text_width: f64,

    frame_color: Color,
    board_color: Color,
    white_color: Color,
    black_color: Color,
}

impl Default for App {
    fn default() -> App {
        App {
            board: Board::new(Size(8, 8)),

            frame_width: 1.0,
            cell_size: 80.0,
            disk_radius: 32.0,
            dot_radius: 6.0,
            board_margin: 40.0,
            indicator_text_width: 90.0,

            frame_color: color::black(),
            board_color: color::rgba(0.0, 0.5, 0.0, 1.0),
            white_color: color::white(),
            black_color: color::black(),
        }
    }
}

fn main() {
    let app_ref = Rc::new(RefCell::new(App::default()));

    let window: PistonWindow = {
        WindowSettings::new("Othello", (1024, 768))
            .exit_on_esc(true)
            .vsync(true)
            .build()
            .unwrap_or_else(|e| panic!("Failed to build PistonWindow: {}", e))
    };

    let mut ui = {
        let assets = find_folder::Search::KidsThenParents(3, 5)
                         .for_folder("assets")
                         .unwrap();
        let ref font_path = assets.join("FiraSans-Regular.ttf");
        let theme = Theme::default();
        let factory = window.factory.borrow().clone();
        let glyph_cache = Glyphs::new(font_path, factory).unwrap();
        Ui::new(glyph_cache, theme)
    };

    for event in window {
        ui.handle_event(&event);

        let _ = event.update(|_| ui.set_widgets(|ui| set_widgets(ui, app_ref.clone())));
        event.draw_2d(|c, g| ui.draw_if_changed(c, g));
    }
}

widget_ids! {
    CANVAS,
    OTHELLO_CANVAS,
    BOARD,
    DOT with 4,
    INDICATOR_LABEL_ICON with 2,
    INDICATOR_LABEL_TEXT with 2,
}

fn set_widgets(ui: &mut Ui, app_ref: Rc<RefCell<App>>) {
    let Size(rows, cols) = {
        let app = app_ref.deref().borrow();
        Canvas::new().color(app.board_color).scrolling(true).set(CANVAS, ui);
        app.board.size()
    };

    {
        let app = app_ref.deref().borrow();
        let board_width = app.cell_size * (app.board.size().1 as f64);
        let indicator_width = app.cell_size + app.indicator_text_width;
        let width = board_width + app.board_margin * 2.0 + indicator_width + app.board_margin;

        let board_height = app.cell_size * (app.board.size().0 as f64);
        let indicator_height = app.cell_size * 2.0;
        let height = app.board_margin * 2.0 + f64::max(board_height, indicator_height);

        let style = LineStyle::new().thickness(0.0);
        let rect = Rectangle::outline_styled([width, height], style);

        match (ui.win_w < board_width, ui.win_h < board_height) {
            (true, true) => rect.top_left_of(CANVAS),
            (false, true) => rect.mid_top_of(CANVAS),
            (true, false) => rect.mid_left_of(CANVAS),
            (false, false) => rect.middle_of(CANVAS),
        }
        .set(OTHELLO_CANVAS, ui);
    }

    let matrix = {
        let app = app_ref.deref().borrow();
        WidgetMatrix::new(cols as usize, rows as usize)
            .top_left_with_margins_on(OTHELLO_CANVAS, app.board_margin, app.board_margin)
            .w_h(app.cell_size * (cols as f64), app.cell_size * (rows as f64))
    };

    matrix.each_widget(|_n, col, row| {
              let pt = Point(row as i32, col as i32);
              let disk = {
                  let app_ref = app_ref.clone();
                  let app = app_ref.deref().borrow();
                  let mut disk = OthelloDisk::new()
                                     .background_color(app.board_color)
                                     .frame(app.frame_width)
                                     .frame_color(app.frame_color)
                                     .white_color(app.white_color)
                                     .black_color(app.black_color)
                                     .radius(app.disk_radius)
                                     .disk(app.board[pt]);
                  if let Some(turn) = app.board.turn() {
                      if app.board.can_locate(pt) {
                          disk = disk.flow_disk(Some(turn));
                      }
                  }
                  disk
              };

              let app_ref = app_ref.clone();
              disk.react(move || {
                  app_ref.borrow_mut().board.locate(pt);
              })
          })
          .set(BOARD, ui);

    {
        let app = app_ref.deref().borrow();
        let x = app.cell_size * ((cols / 4) as f64);
        let y = app.cell_size * ((rows / 4) as f64);
        let signs = &[(-1.0, 1.0), (1.0, 1.0), (-1.0, -1.0), (1.0, -1.0)];
        for (i, &(sx, sy)) in signs.iter().enumerate() {
            Circle::fill(app.dot_radius)
                .x_y_relative_to(BOARD, sx * x, sy * y)
                .color(app.frame_color)
                .set(DOT + i, ui);
        }
    }

    for (i, &side) in [Side::Black, Side::White].iter().enumerate() {
        let app = app_ref.deref().borrow();
        if side == Side::Black {
            OthelloDisk::new().right_from(BOARD, app.board_margin)
        } else {
            OthelloDisk::new().down_from(INDICATOR_LABEL_ICON + (i - 1), 0.0)
        }
        .w_h(app.cell_size, app.cell_size)
        .background_color(app.board_color)
        .frame(0.0)
        .white_color(app.white_color)
        .black_color(app.black_color)
        .radius(app.disk_radius)
        .disk(Some(side))
        .react(|| {})
        .set(INDICATOR_LABEL_ICON + i, ui);

        let text = match side {
            Side::Black => app.board.num_black().to_string(),
            Side::White => app.board.num_white().to_string(),
        };
        Text::new(&text)
            .w(app.indicator_text_width)
            .right_from(INDICATOR_LABEL_ICON + i, 0.0)
            .font_size(60)
            .align_text_right()
            .set(INDICATOR_LABEL_TEXT + i, ui);
    }
}
<|endoftext|>