<commit_before>mod figure;

use stamp;

use summary::Summary;

macro_rules! plot {
    ($p: expr) => {
        match $p {
            Ok(t) => Ok(t),
            Err(_) => Err("Unable to plot sample data"),
        }
    }
}

struct Boxplot {
    box_lo: f64,
    box_mid: f64,
    box_hi: f64,
    marker: f64,
    wh_lo: f64,
    wh_hi: f64,
}

impl Boxplot {
    fn from_summary(summary: &Summary) -> Self {
        let range = summary.range();
        let n = |x| (x - summary.min()) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min()),
            wh_hi: n(summary.max()),
        }
    }

    fn from_summary_no_outliers(summary: &Summary) -> Self {
        let min = summary.min_non_outlier().min(summary.mean());
        let max = summary.max_non_outlier().max(summary.mean());
        let range = max - min;
        let n = |x| (x - min) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min_non_outlier()),
            wh_hi: n(summary.max_non_outlier()),
        }
    }
}

struct BoxplotCols {
    box_lo: usize,
    box_mid: usize,
    box_hi: usize,
    marker: usize,
    wh_lo: usize,
    wh_hi: usize,
}

impl BoxplotCols {
    fn new(data: &Boxplot, width: usize) -> Self {
        let max_col = (width - 1) as f64;
        let to_col = |x: f64| (x * max_col).floor() as usize;

        BoxplotCols {
            box_lo: to_col(data.box_lo),
            box_mid: to_col(data.box_mid),
            box_hi: to_col(data.box_hi),
            marker: to_col(data.marker),
            wh_lo: to_col(data.wh_lo),
            wh_hi: to_col(data.wh_hi),
        }
    }
}

struct RowChars {
    wh_lo: &'static str,
    wh_lo_box_lo_fill: &'static str,
    box_lo: &'static str,
    box_lo_box_mid_fill: &'static str,
    box_mid: &'static str,
    box_mid_box_hi_fill: &'static str,
    box_hi: &'static str,
    box_hi_wh_hi_fill: &'static str,
    wh_hi: &'static str,
}

impl RowChars {
    pub fn render(&self, row: &mut Vec<String>, cols: &BoxplotCols) {
        // Lower whisker extent.
        for i in (cols.wh_lo + 1)..cols.box_lo {
            row[i] = self.wh_lo_box_lo_fill.to_string();
        }

        // Upper whisker extent.
        for i in (cols.box_hi + 1)..cols.wh_hi {
            row[i] = self.box_hi_wh_hi_fill.to_string();
        }

        // Lower box extent.
        for i in (cols.box_lo + 1)..cols.box_mid {
            row[i] = self.box_lo_box_mid_fill.to_string();
        }

        // Upper box extent.
        for i in (cols.box_mid + 1)..cols.box_hi {
            row[i] = self.box_mid_box_hi_fill.to_string();
        }

        // Lower box end.
        row[cols.box_lo] = self.box_lo.to_string();

        // Upper box end.
        row[cols.box_hi] = self.box_hi.to_string();

        // Lower whisker end.
        row[cols.wh_lo] = self.wh_lo.to_string();

        // Upper whisker end.
        row[cols.wh_hi] = self.wh_hi.to_string();

        // Middle line.
        row[cols.box_mid] = self.box_mid.to_string();
    }
}

struct BoxplotChars {
    marker: &'static str,
    rows: [RowChars; 3],
}

impl BoxplotChars {
    pub fn render(&self, summary: &Summary, width: usize, outliers: bool)
                  -> Result<String, &'static str> {
        let data = if outliers {
            Boxplot::from_summary(summary)
        } else {
            Boxplot::from_summary_no_outliers(summary)
        };
        let cols = BoxplotCols::new(&data, width);
        let mut plot = Plot::new(width);

        self.rows[0].render(&mut plot.0, &cols);
        self.rows[1].render(&mut plot.1, &cols);
        self.rows[2].render(&mut plot.2, &cols);

        let no_marker = plot.render();

        let base = plot!(stamp::Stamp::new(&no_marker))?;
        let marker = plot!(stamp::Stamp::new(self.marker))?;
        let layered = plot!(base.layer(&marker, cols.marker, 1))?;

        Ok(layered.render())
    }
}

static ASCII_CHARS: BoxplotChars = BoxplotChars {
    marker: "x",
    rows: [
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
        RowChars {
            wh_lo: "|",
            wh_lo_box_lo_fill: "-",
            box_lo: "|",
            box_lo_box_mid_fill: " ",
            box_mid: "|",
            box_mid_box_hi_fill: " ",
            box_hi: "|",
            box_hi_wh_hi_fill: "-",
            wh_hi: "|",
        },
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
    ],
};

static UNICODE_CHARS: BoxplotChars = BoxplotChars {
    marker: "✕",
    rows: [
        RowChars {
            wh_lo: "┬",
            wh_lo_box_lo_fill: " ",
            box_lo: "┌",
            box_lo_box_mid_fill: "─",
            box_mid: "┬",
            box_mid_box_hi_fill: "─",
            box_hi: "┐",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┬",
        },
        RowChars {
            wh_lo: "├",
            wh_lo_box_lo_fill: "─",
            box_lo: "┤",
            box_lo_box_mid_fill: " ",
            box_mid: "│",
            box_mid_box_hi_fill: " ",
            box_hi: "├",
            box_hi_wh_hi_fill: "─",
            wh_hi: "┤",
        },
        RowChars {
            wh_lo: "┴",
            wh_lo_box_lo_fill: " ",
            box_lo: "└",
            box_lo_box_mid_fill: "─",
            box_mid: "┴",
            box_mid_box_hi_fill: "─",
            box_hi: "┘",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┴",
        },
    ],
};

fn make_row(width: usize) -> Vec<String> {
    use std::iter::repeat;

    let mut row = vec![];
    row.extend(repeat(String::from("")).take(width));

    row
}

struct Plot(Vec<String>, Vec<String>, Vec<String>);

impl Plot {
    fn new(width: usize) -> Self {
        Plot(make_row(width), make_row(width), make_row(width))
    }

    fn render(&self) -> String {
        let rows = vec![
            self.0.join(""),
            self.1.join(""),
            self.2.join(""),
        ];

        rows.join("\n")
    }
}

pub fn summary_plot(summary: &Summary, width: usize, ascii: bool, outliers: bool)
                    -> Result<String, &'static str> {
    let plot_style = if ascii { &ASCII_CHARS } else { &UNICODE_CHARS };

    plot_style.render(summary, width, outliers)
}

pub fn comparison_plot(
    summaries: &[&Summary],
    width: usize,
    ascii: bool,
    border: bool,
    outliers: bool,
) -> Result<String, &'static str> {
    if summaries.is_empty() {
        return Err("Cannot plot empty list of summaries");
    }

    let padding = if border { 2 } else { 0 };
    let content_width = (width - 2 * padding) as f64;
    let border_style = if ascii {
        figure::ASCII_BORDER
    } else {
        figure::UNICODE_BORDER
    };

    use std::f64;

    let plot_min = |s: &Summary| if outliers {
        s.min()
    } else {
        s.min_non_outlier().min(s.mean())
    };
    let min = summaries
        .iter()
        .map(|s| plot_min(s))
        .fold(f64::MAX, |x, y| x.min(y));

    let plot_max = |s: &Summary| if outliers {
        s.max()
    } else {
        s.max_non_outlier().max(s.mean())
    };
    let max = summaries
        .iter()
        .map(|s| plot_max(s))
        .fold(f64::MIN, |x, y| x.max(y));

    // Used to compute relative widths of boxplots from their own ranges.
    let range = max - min;

    let mut plots = vec![];

    for s in summaries {
        let s_min = if outliers {
            s.min()
        } else {
            s.min_non_outlier().min(s.mean())
        };
        let s_max = if outliers {
            s.max()
        } else {
            s.max_non_outlier().max(s.mean())
        };

        // Proportion of total content width spanned by this plot.
        let p = (s_max - s_min) / range;

        // Boxplot content width in cols.
        let w = (content_width * p).floor().max(1.0);
        assert!(1.0 <= w);
        assert!(w <= content_width);

        let plot = plot!(stamp::Stamp::new(&summary_plot(s, w as usize, ascii, outliers)?))?;

        assert!(min <= s_min);
        let offset_p = (s_min - min) / range;

        let offset = (offset_p * content_width).min(content_width - w);
        assert!(offset + w <= content_width);

        plots.push((plot, padding + (offset as usize)));
    }

    let height = &plots
        .iter()
        .map(|&(ref p, _)| p.height())
        .sum() + (padding * 2);

    let base = if border {
        figure::Border::new(border_style, width, height).render()
    } else {
        figure::Filled::blank(width, height).render()
    };

    let mut all_plots = plot!(stamp::Stamp::new(&base))?;

    for (i, &(ref plot, left_offset)) in plots.iter().enumerate() {
        all_plots = plot!(all_plots.layer(&plot, left_offset, padding + i * plot.height()))?;
    }

    Ok(all_plots.render())
}
<commit_msg>Avoid potential loss of blank space<commit_after>mod figure;

use stamp;

use summary::Summary;

macro_rules! plot {
    ($p: expr) => {
        match $p {
            Ok(t) => Ok(t),
            Err(_) => Err("Unable to plot sample data"),
        }
    }
}

struct Boxplot {
    box_lo: f64,
    box_mid: f64,
    box_hi: f64,
    marker: f64,
    wh_lo: f64,
    wh_hi: f64,
}

impl Boxplot {
    fn from_summary(summary: &Summary) -> Self {
        let range = summary.range();
        let n = |x| (x - summary.min()) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min()),
            wh_hi: n(summary.max()),
        }
    }

    fn from_summary_no_outliers(summary: &Summary) -> Self {
        let min = summary.min_non_outlier().min(summary.mean());
        let max = summary.max_non_outlier().max(summary.mean());
        let range = max - min;
        let n = |x| (x - min) / range;

        Boxplot {
            box_lo: n(summary.lower_quartile()),
            box_mid: n(summary.median()),
            box_hi: n(summary.upper_quartile()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min_non_outlier()),
            wh_hi: n(summary.max_non_outlier()),
        }
    }
}

struct BoxplotCols {
    box_lo: usize,
    box_mid: usize,
    box_hi: usize,
    marker: usize,
    wh_lo: usize,
    wh_hi: usize,
}

impl BoxplotCols {
    fn new(data: &Boxplot, width: usize) -> Self {
        let max_col = (width - 1) as f64;
        let to_col = |x: f64| (x * max_col).floor() as usize;

        BoxplotCols {
            box_lo: to_col(data.box_lo),
            box_mid: to_col(data.box_mid),
            box_hi: to_col(data.box_hi),
            marker: to_col(data.marker),
            wh_lo: to_col(data.wh_lo),
            wh_hi: to_col(data.wh_hi),
        }
    }
}

struct RowChars {
    wh_lo: &'static str,
    wh_lo_box_lo_fill: &'static str,
    box_lo: &'static str,
    box_lo_box_mid_fill: &'static str,
    box_mid: &'static str,
    box_mid_box_hi_fill: &'static str,
    box_hi: &'static str,
    box_hi_wh_hi_fill: &'static str,
    wh_hi: &'static str,
}

impl RowChars {
    pub fn render(&self, row: &mut Vec<String>, cols: &BoxplotCols) {
        // Lower whisker extent.
        for i in (cols.wh_lo + 1)..cols.box_lo {
            row[i] = self.wh_lo_box_lo_fill.to_string();
        }

        // Upper whisker extent.
        for i in (cols.box_hi + 1)..cols.wh_hi {
            row[i] = self.box_hi_wh_hi_fill.to_string();
        }

        // Lower box extent.
        for i in (cols.box_lo + 1)..cols.box_mid {
            row[i] = self.box_lo_box_mid_fill.to_string();
        }

        // Upper box extent.
        for i in (cols.box_mid + 1)..cols.box_hi {
            row[i] = self.box_mid_box_hi_fill.to_string();
        }

        // Lower box end.
        row[cols.box_lo] = self.box_lo.to_string();

        // Upper box end.
        row[cols.box_hi] = self.box_hi.to_string();

        // Lower whisker end.
        row[cols.wh_lo] = self.wh_lo.to_string();

        // Upper whisker end.
        row[cols.wh_hi] = self.wh_hi.to_string();

        // Middle line.
        row[cols.box_mid] = self.box_mid.to_string();
    }
}

struct BoxplotChars {
    marker: &'static str,
    rows: [RowChars; 3],
}

impl BoxplotChars {
    pub fn render(&self, summary: &Summary, width: usize, outliers: bool)
                  -> Result<String, &'static str> {
        let data = if outliers {
            Boxplot::from_summary(summary)
        } else {
            Boxplot::from_summary_no_outliers(summary)
        };
        let cols = BoxplotCols::new(&data, width);
        let mut plot = Plot::new(width);

        self.rows[0].render(&mut plot.0, &cols);
        self.rows[1].render(&mut plot.1, &cols);
        self.rows[2].render(&mut plot.2, &cols);

        let no_marker = plot.render();

        let base = plot!(stamp::Stamp::new(&no_marker))?;
        let marker = plot!(stamp::Stamp::new(self.marker))?;
        let layered = plot!(base.layer(&marker, cols.marker, 1))?;

        Ok(layered.render())
    }
}

static ASCII_CHARS: BoxplotChars = BoxplotChars {
    marker: "x",
    rows: [
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
        RowChars {
            wh_lo: "|",
            wh_lo_box_lo_fill: "-",
            box_lo: "|",
            box_lo_box_mid_fill: " ",
            box_mid: "|",
            box_mid_box_hi_fill: " ",
            box_hi: "|",
            box_hi_wh_hi_fill: "-",
            wh_hi: "|",
        },
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
    ],
};

static UNICODE_CHARS: BoxplotChars = BoxplotChars {
    marker: "✕",
    rows: [
        RowChars {
            wh_lo: "┬",
            wh_lo_box_lo_fill: " ",
            box_lo: "┌",
            box_lo_box_mid_fill: "─",
            box_mid: "┬",
            box_mid_box_hi_fill: "─",
            box_hi: "┐",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┬",
        },
        RowChars {
            wh_lo: "├",
            wh_lo_box_lo_fill: "─",
            box_lo: "┤",
            box_lo_box_mid_fill: " ",
            box_mid: "│",
            box_mid_box_hi_fill: " ",
            box_hi: "├",
            box_hi_wh_hi_fill: "─",
            wh_hi: "┤",
        },
        RowChars {
            wh_lo: "┴",
            wh_lo_box_lo_fill: " ",
            box_lo: "└",
            box_lo_box_mid_fill: "─",
            box_mid: "┴",
            box_mid_box_hi_fill: "─",
            box_hi: "┘",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┴",
        },
    ],
};

fn make_row(width: usize) -> Vec<String> {
    use std::iter::repeat;

    let mut row = vec![];
    row.extend(repeat(String::from(" ")).take(width));

    row
}

struct Plot(Vec<String>, Vec<String>, Vec<String>);

impl Plot {
    fn new(width: usize) -> Self {
        Plot(make_row(width), make_row(width), make_row(width))
    }

    fn render(&self) -> String {
        let rows = vec![
            self.0.join(""),
            self.1.join(""),
            self.2.join(""),
        ];

        rows.join("\n")
    }
}

pub fn summary_plot(summary: &Summary, width: usize, ascii: bool, outliers: bool)
                    -> Result<String, &'static str> {
    let plot_style = if ascii { &ASCII_CHARS } else { &UNICODE_CHARS };

    plot_style.render(summary, width, outliers)
}

pub fn comparison_plot(
    summaries: &[&Summary],
    width: usize,
    ascii: bool,
    border: bool,
    outliers: bool,
) -> Result<String, &'static str> {
    if summaries.is_empty() {
        return Err("Cannot plot empty list of summaries");
    }

    let padding = if border { 2 } else { 0 };
    let content_width = (width - 2 * padding) as f64;
    let border_style = if ascii {
        figure::ASCII_BORDER
    } else {
        figure::UNICODE_BORDER
    };

    use std::f64;

    let plot_min = |s: &Summary| if outliers {
        s.min()
    } else {
        s.min_non_outlier().min(s.mean())
    };
    let min = summaries
        .iter()
        .map(|s| plot_min(s))
        .fold(f64::MAX, |x, y| x.min(y));

    let plot_max = |s: &Summary| if outliers {
        s.max()
    } else {
        s.max_non_outlier().max(s.mean())
    };
    let max = summaries
        .iter()
        .map(|s| plot_max(s))
        .fold(f64::MIN, |x, y| x.max(y));

    // Used to compute relative widths of boxplots from their own ranges.
    let range = max - min;

    let mut plots = vec![];

    for s in summaries {
        let s_min = if outliers {
            s.min()
        } else {
            s.min_non_outlier().min(s.mean())
        };
        let s_max = if outliers {
            s.max()
        } else {
            s.max_non_outlier().max(s.mean())
        };

        // Proportion of total content width spanned by this plot.
        let p = (s_max - s_min) / range;

        // Boxplot content width in cols.
        let w = (content_width * p).floor().max(1.0);
        assert!(1.0 <= w);
        assert!(w <= content_width);

        let plot = plot!(stamp::Stamp::new(&summary_plot(s, w as usize, ascii, outliers)?))?;

        assert!(min <= s_min);
        let offset_p = (s_min - min) / range;

        let offset = (offset_p * content_width).min(content_width - w);
        assert!(offset + w <= content_width);

        plots.push((plot, padding + (offset as usize)));
    }

    let height = &plots
        .iter()
        .map(|&(ref p, _)| p.height())
        .sum() + (padding * 2);

    let base = if border {
        figure::Border::new(border_style, width, height).render()
    } else {
        figure::Filled::blank(width, height).render()
    };

    let mut all_plots = plot!(stamp::Stamp::new(&base))?;

    for (i, &(ref plot, left_offset)) in plots.iter().enumerate() {
        all_plots = plot!(all_plots.layer(&plot, left_offset, padding + i * plot.height()))?;
    }

    Ok(all_plots.render())
}
<|endoftext|>


<commit_before>use raft::storage::Storage;

/// Raft log implementation
pub struct RaftLog {
    store: Storage,
}
<commit_msg>raft: fix compile error<commit_after>use raft::storage::Storage;

/// Raft log implementation
pub struct RaftLog<T: Storage> {
    store: T,
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use raft::errors::{Result, Error};
use raft::Storage;
use protobuf::{self, RepeatedField};
use proto::raftpb::{HardState, Entry, EntryType, Message, Snapshot, MessageType, ConfChange,
                    ConfChangeType, ConfState};
use raft::raft::{Config, Raft, SoftState, INVALID_ID};
use raft::Status;
use std::sync::Arc;

#[derive(Debug, Default)]
pub struct Peer {
    pub id: u64,
    pub context: Option<Vec<u8>>,
}

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SnapshotStatus {
    SnapshotFinish,
    SnapshotFailure,
}

fn is_local_msg(m: &Message) -> bool {
    match m.get_msg_type() {
        MessageType::MsgHup |
        MessageType::MsgBeat |
        MessageType::MsgUnreachable |
        MessageType::MsgSnapStatus |
        MessageType::MsgCheckQuorum => true,
        _ => false,
    }
}

fn is_response_msg(m: &Message) -> bool {
    match m.get_msg_type() {
        MessageType::MsgAppendResponse |
        MessageType::MsgRequestVoteResponse |
        MessageType::MsgHeartbeatResponse |
        MessageType::MsgUnreachable => true,
        _ => false,
    }
}

pub fn is_empty_snap(s: &Snapshot) -> bool {
    s.get_metadata().get_index() == 0
}

// Ready encapsulates the entries and messages that are ready to read,
// be saved to stable storage, committed or sent to other peers.
// All fields in Ready are read-only.
#[derive(Default, Debug, PartialEq)]
pub struct Ready {
    // The current volatile state of a Node.
    // SoftState will be nil if there is no update.
    // It is not required to consume or store SoftState.
    pub ss: Option<SoftState>,

    // The current state of a Node to be saved to stable storage BEFORE
    // Messages are sent.
    // HardState will be equal to empty state if there is no update.
    pub hs: Option<HardState>,

    // Entries specifies entries to be saved to stable storage BEFORE
    // Messages are sent.
    pub entries: Vec<Entry>,

    // Snapshot specifies the snapshot to be saved to stable storage.
    pub snapshot: Snapshot,

    // CommittedEntries specifies entries to be committed to a
    // store/state-machine. These have previously been committed to stable
    // store.
    pub committed_entries: Vec<Entry>,

    // Messages specifies outbound messages to be sent AFTER Entries are
    // committed to stable storage.
    // If it contains a MsgSnap message, the application MUST report back to raft
    // when the snapshot has been received or has failed by calling ReportSnapshot.
    pub messages: Vec<Message>,
}

impl Ready {
    fn new<T: Storage>(raft: &mut Raft<T>, prev_ss: &SoftState, prev_hs: &HardState) -> Ready {
        let mut rd = Ready {
            entries: raft.raft_log.unstable_entries().unwrap_or(&[]).to_vec(),
            committed_entries: raft.raft_log.next_entries().unwrap_or_else(Vec::new),
            messages: raft.msgs.drain(..).collect(),
            ..Default::default()
        };
        let ss = raft.soft_state();
        if &ss != prev_ss {
            rd.ss = Some(ss);
        }
        let hs = raft.hard_state();
        if &hs != prev_hs {
            rd.hs = Some(hs);
        }
        if raft.raft_log.get_unstable().snapshot.is_some() {
            rd.snapshot = raft.raft_log.get_unstable().snapshot.clone().unwrap();
        }
        rd
    }
}

// RawNode is a thread-unsafe Node.
// The methods of this struct correspond to the methods of Node and are described
// more fully there.
pub struct RawNode<T: Storage> {
    pub raft: Raft<T>,
    prev_ss: SoftState,
    prev_hs: HardState,
}

impl<T: Storage> RawNode<T> {
    // NewRawNode returns a new RawNode given configuration and a list of raft peers.
    pub fn new(config: &Config, store: Arc<T>, peers: &[Peer]) -> Result<RawNode<T>> {
        assert!(config.id != 0, "config.id must not be zero");
        let r = Raft::new(config, store);
        let mut rn = RawNode {
            raft: r,
            prev_hs: Default::default(),
            prev_ss: Default::default(),
        };
        let last_index = rn.raft.get_store().last_index().expect("");
        if last_index == 0 {
            rn.raft.become_follower(1, INVALID_ID);
            let mut ents = Vec::with_capacity(peers.len());
            for (i, peer) in peers.iter().enumerate() {
                let mut cc = ConfChange::new();
                cc.set_change_type(ConfChangeType::ConfChangeAddNode);
                cc.set_node_id(peer.id);
                if peer.context.is_some() {
                    cc.set_context(peer.context.as_ref().unwrap().clone());
                }
                let data = protobuf::Message::write_to_bytes(&cc)
                               .expect("unexpected marshal error");
                let mut e = Entry::new();
                e.set_entry_type(EntryType::EntryConfChange);
                e.set_term(1);
                e.set_index(i as u64 + 1);
                e.set_data(data);
                ents.push(e);
            }
            rn.raft.raft_log.append(&ents);
            rn.raft.raft_log.committed = ents.len() as u64;
            for peer in peers {
                rn.raft.add_node(peer.id);
            }
        }
        rn.prev_ss = rn.raft.soft_state();
        rn.prev_hs = rn.raft.hard_state();
        Ok(rn)
    }

    fn commit_ready(&mut self, rd: Ready) {
        if rd.ss.is_some() {
            self.prev_ss = rd.ss.unwrap();
        }
        if let Some(e) = rd.hs {
            if e != HardState::new() {
                self.prev_hs = e;
            }
        }
        if self.prev_hs.get_commit() != 0 {
            // In most cases, prevHardSt and rd.HardState will be the same
            // because when there are new entries to apply we just sent a
            // HardState with an updated Commit value. However, on initial
            // startup the two are different because we don't send a HardState
            // until something changes, but we do send any un-applied but
            // committed entries (and previously-committed entries may be
            // incorporated into the snapshot, even if rd.CommittedEntries is
            // empty). Therefore we mark all committed entries as applied
            // whether they were included in rd.HardState or not.
            self.raft.raft_log.applied_to(self.prev_hs.get_commit());
        }
        if !rd.entries.is_empty() {
            let e = rd.entries.last().unwrap();
            self.raft.raft_log.stable_to(e.get_index(), e.get_term());
        }
        if rd.snapshot != Snapshot::new() {
            self.raft.raft_log.stable_snap_to(rd.snapshot.get_metadata().get_index());
        }
    }

    // Tick advances the internal logical clock by a single tick.
    pub fn tick(&mut self) {
        self.raft.tick();
    }

    // Campaign causes this RawNode to transition to candidate state.
    pub fn campaign(&mut self) -> Result<()> {
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgHup);
        self.raft.step(m)
    }

    // Propose proposes data be appended to the raft log.
    pub fn propose(&mut self, data: Vec<u8>) -> Result<()> {
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgPropose);
        m.set_from(self.raft.id);
        let mut e = Entry::new();
        e.set_data(data);
        m.set_entries(RepeatedField::from_vec(vec![e]));
        self.raft.step(m)
    }

    // ProposeConfChange proposes a config change.
    pub fn propose_conf_change(&mut self, cc: ConfChange) -> Result<()> {
        let data = try!(protobuf::Message::write_to_bytes(&cc));
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgPropose);
        let mut e = Entry::new();
        e.set_entry_type(EntryType::EntryConfChange);
        e.set_data(data);
        m.set_entries(RepeatedField::from_vec(vec![e]));
        self.raft.step(m)
    }

    pub fn apply_conf_change(&mut self, cc: ConfChange) -> ConfState {
        if cc.get_node_id() == INVALID_ID {
            self.raft.reset_pending_conf();
            let mut cs = ConfState::new();
            cs.set_nodes(self.raft.nodes());
            return cs;
        }
        let nid = cc.get_node_id();
        assert!(cc.has_change_type(), "unexpected conf type");
        match cc.get_change_type() {
            ConfChangeType::ConfChangeAddNode => self.raft.add_node(nid),
            ConfChangeType::ConfChangeRemoveNode => self.raft.remove_node(nid),
            ConfChangeType::ConfChangeUpdateNode => self.raft.reset_pending_conf(),
        }
        let mut cs = ConfState::new();
        cs.set_nodes(self.raft.nodes());
        cs
    }

    // Step advances the state machine using the given message.
    pub fn step(&mut self, m: Message) -> Result<()> {
        // ignore unexpected local messages receiving over network
        if is_local_msg(&m) {
            return Err(Error::StepLocalMsg);
        }
        if self.raft.prs.contains_key(&m.get_from()) || !is_response_msg(&m) {
            return self.raft.step(m);
        }
        Err(Error::StepPeerNotFound)
    }

    // Ready returns the current point-in-time state of this RawNode.
    pub fn ready(&mut self) -> Ready {
        Ready::new(&mut self.raft, &self.prev_ss, &self.prev_hs)
    }

    // HasReady called when RawNode user need to check if any Ready pending.
    // Checking logic in this method should be consistent with Ready.containsUpdates().
    pub fn has_ready(&self) -> bool {
        let raft = &self.raft;
        if raft.soft_state() != self.prev_ss {
            return true;
        }
        let hs = raft.hard_state();
        if hs != HardState::new() && hs != self.prev_hs {
            return true;
        }
        if Some(true) == raft.raft_log.get_unstable().snapshot.as_ref().map(|s| !is_empty_snap(s)) {
            return true;
        }
        if !raft.msgs.is_empty() || raft.raft_log.unstable_entries().is_some() ||
           raft.raft_log.has_next_entries() {
            return true;
        }
        false
    }

    // Advance notifies the RawNode that the application has applied and saved progress in the
    // last Ready results.
    pub fn advance(&mut self, rd: Ready) {
        self.commit_ready(rd);
    }

    // Status returns the current status of the given group.
    pub fn status(&self) -> Status {
        Status::new(&self.raft)
    }

    // ReportUnreachable reports the given node is not reachable for the last send.
    pub fn report_unreachable(&mut self, id: u64) {
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgUnreachable);
        m.set_from(id);
        // we don't care if it is ok actually
        self.raft.step(m).is_ok();
    }

    // ReportSnapshot reports the status of the sent snapshot.
    pub fn report_snapshot(&mut self, id: u64, status: SnapshotStatus) {
        let rej = status == SnapshotStatus::SnapshotFailure;
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgSnapStatus);
        m.set_from(id);
        m.set_reject(rej);
        // we don't care if it is ok actually
        self.raft.step(m).is_ok();
    }
}
<commit_msg>*: update clippy, resolve warnings<commit_after>#![allow(dead_code)]
use raft::errors::{Result, Error};
use raft::Storage;
use protobuf::{self, RepeatedField};
use proto::raftpb::{HardState, Entry, EntryType, Message, Snapshot, MessageType, ConfChange,
                    ConfChangeType, ConfState};
use raft::raft::{Config, Raft, SoftState, INVALID_ID};
use raft::Status;
use std::sync::Arc;

#[derive(Debug, Default)]
pub struct Peer {
    pub id: u64,
    pub context: Option<Vec<u8>>,
}

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SnapshotStatus {
    Finish,
    Failure,
}

fn is_local_msg(m: &Message) -> bool {
    match m.get_msg_type() {
        MessageType::MsgHup |
        MessageType::MsgBeat |
        MessageType::MsgUnreachable |
        MessageType::MsgSnapStatus |
        MessageType::MsgCheckQuorum => true,
        _ => false,
    }
}

fn is_response_msg(m: &Message) -> bool {
    match m.get_msg_type() {
        MessageType::MsgAppendResponse |
        MessageType::MsgRequestVoteResponse |
        MessageType::MsgHeartbeatResponse |
        MessageType::MsgUnreachable => true,
        _ => false,
    }
}

pub fn is_empty_snap(s: &Snapshot) -> bool {
    s.get_metadata().get_index() == 0
}

// Ready encapsulates the entries and messages that are ready to read,
// be saved to stable storage, committed or sent to other peers.
// All fields in Ready are read-only.
#[derive(Default, Debug, PartialEq)]
pub struct Ready {
    // The current volatile state of a Node.
    // SoftState will be nil if there is no update.
    // It is not required to consume or store SoftState.
    pub ss: Option<SoftState>,

    // The current state of a Node to be saved to stable storage BEFORE
    // Messages are sent.
    // HardState will be equal to empty state if there is no update.
    pub hs: Option<HardState>,

    // Entries specifies entries to be saved to stable storage BEFORE
    // Messages are sent.
    pub entries: Vec<Entry>,

    // Snapshot specifies the snapshot to be saved to stable storage.
    pub snapshot: Snapshot,

    // CommittedEntries specifies entries to be committed to a
    // store/state-machine. These have previously been committed to stable
    // store.
    pub committed_entries: Vec<Entry>,

    // Messages specifies outbound messages to be sent AFTER Entries are
    // committed to stable storage.
    // If it contains a MsgSnap message, the application MUST report back to raft
    // when the snapshot has been received or has failed by calling ReportSnapshot.
    pub messages: Vec<Message>,
}

impl Ready {
    fn new<T: Storage>(raft: &mut Raft<T>, prev_ss: &SoftState, prev_hs: &HardState) -> Ready {
        let mut rd = Ready {
            entries: raft.raft_log.unstable_entries().unwrap_or(&[]).to_vec(),
            committed_entries: raft.raft_log.next_entries().unwrap_or_else(Vec::new),
            messages: raft.msgs.drain(..).collect(),
            ..Default::default()
        };
        let ss = raft.soft_state();
        if &ss != prev_ss {
            rd.ss = Some(ss);
        }
        let hs = raft.hard_state();
        if &hs != prev_hs {
            rd.hs = Some(hs);
        }
        if raft.raft_log.get_unstable().snapshot.is_some() {
            rd.snapshot = raft.raft_log.get_unstable().snapshot.clone().unwrap();
        }
        rd
    }
}

// RawNode is a thread-unsafe Node.
// The methods of this struct correspond to the methods of Node and are described
// more fully there.
pub struct RawNode<T: Storage> {
    pub raft: Raft<T>,
    prev_ss: SoftState,
    prev_hs: HardState,
}

impl<T: Storage> RawNode<T> {
    // NewRawNode returns a new RawNode given configuration and a list of raft peers.
    pub fn new(config: &Config, store: Arc<T>, peers: &[Peer]) -> Result<RawNode<T>> {
        assert!(config.id != 0, "config.id must not be zero");
        let r = Raft::new(config, store);
        let mut rn = RawNode {
            raft: r,
            prev_hs: Default::default(),
            prev_ss: Default::default(),
        };
        let last_index = rn.raft.get_store().last_index().expect("");
        if last_index == 0 {
            rn.raft.become_follower(1, INVALID_ID);
            let mut ents = Vec::with_capacity(peers.len());
            for (i, peer) in peers.iter().enumerate() {
                let mut cc = ConfChange::new();
                cc.set_change_type(ConfChangeType::ConfChangeAddNode);
                cc.set_node_id(peer.id);
                if peer.context.is_some() {
                    cc.set_context(peer.context.as_ref().unwrap().clone());
                }
                let data = protobuf::Message::write_to_bytes(&cc)
                               .expect("unexpected marshal error");
                let mut e = Entry::new();
                e.set_entry_type(EntryType::EntryConfChange);
                e.set_term(1);
                e.set_index(i as u64 + 1);
                e.set_data(data);
                ents.push(e);
            }
            rn.raft.raft_log.append(&ents);
            rn.raft.raft_log.committed = ents.len() as u64;
            for peer in peers {
                rn.raft.add_node(peer.id);
            }
        }
        rn.prev_ss = rn.raft.soft_state();
        rn.prev_hs = rn.raft.hard_state();
        Ok(rn)
    }

    fn commit_ready(&mut self, rd: Ready) {
        if rd.ss.is_some() {
            self.prev_ss = rd.ss.unwrap();
        }
        if let Some(e) = rd.hs {
            if e != HardState::new() {
                self.prev_hs = e;
            }
        }
        if self.prev_hs.get_commit() != 0 {
            // In most cases, prevHardSt and rd.HardState will be the same
            // because when there are new entries to apply we just sent a
            // HardState with an updated Commit value. However, on initial
            // startup the two are different because we don't send a HardState
            // until something changes, but we do send any un-applied but
            // committed entries (and previously-committed entries may be
            // incorporated into the snapshot, even if rd.CommittedEntries is
            // empty). Therefore we mark all committed entries as applied
            // whether they were included in rd.HardState or not.
            self.raft.raft_log.applied_to(self.prev_hs.get_commit());
        }
        if !rd.entries.is_empty() {
            let e = rd.entries.last().unwrap();
            self.raft.raft_log.stable_to(e.get_index(), e.get_term());
        }
        if rd.snapshot != Snapshot::new() {
            self.raft.raft_log.stable_snap_to(rd.snapshot.get_metadata().get_index());
        }
    }

    // Tick advances the internal logical clock by a single tick.
    pub fn tick(&mut self) {
        self.raft.tick();
    }

    // Campaign causes this RawNode to transition to candidate state.
    pub fn campaign(&mut self) -> Result<()> {
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgHup);
        self.raft.step(m)
    }

    // Propose proposes data be appended to the raft log.
    pub fn propose(&mut self, data: Vec<u8>) -> Result<()> {
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgPropose);
        m.set_from(self.raft.id);
        let mut e = Entry::new();
        e.set_data(data);
        m.set_entries(RepeatedField::from_vec(vec![e]));
        self.raft.step(m)
    }

    // ProposeConfChange proposes a config change.
    pub fn propose_conf_change(&mut self, cc: ConfChange) -> Result<()> {
        let data = try!(protobuf::Message::write_to_bytes(&cc));
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgPropose);
        let mut e = Entry::new();
        e.set_entry_type(EntryType::EntryConfChange);
        e.set_data(data);
        m.set_entries(RepeatedField::from_vec(vec![e]));
        self.raft.step(m)
    }

    pub fn apply_conf_change(&mut self, cc: ConfChange) -> ConfState {
        if cc.get_node_id() == INVALID_ID {
            self.raft.reset_pending_conf();
            let mut cs = ConfState::new();
            cs.set_nodes(self.raft.nodes());
            return cs;
        }
        let nid = cc.get_node_id();
        assert!(cc.has_change_type(), "unexpected conf type");
        match cc.get_change_type() {
            ConfChangeType::ConfChangeAddNode => self.raft.add_node(nid),
            ConfChangeType::ConfChangeRemoveNode => self.raft.remove_node(nid),
            ConfChangeType::ConfChangeUpdateNode => self.raft.reset_pending_conf(),
        }
        let mut cs = ConfState::new();
        cs.set_nodes(self.raft.nodes());
        cs
    }

    // Step advances the state machine using the given message.
    pub fn step(&mut self, m: Message) -> Result<()> {
        // ignore unexpected local messages receiving over network
        if is_local_msg(&m) {
            return Err(Error::StepLocalMsg);
        }
        if self.raft.prs.contains_key(&m.get_from()) || !is_response_msg(&m) {
            return self.raft.step(m);
        }
        Err(Error::StepPeerNotFound)
    }

    // Ready returns the current point-in-time state of this RawNode.
    pub fn ready(&mut self) -> Ready {
        Ready::new(&mut self.raft, &self.prev_ss, &self.prev_hs)
    }

    // HasReady called when RawNode user need to check if any Ready pending.
    // Checking logic in this method should be consistent with Ready.containsUpdates().
    pub fn has_ready(&self) -> bool {
        let raft = &self.raft;
        if raft.soft_state() != self.prev_ss {
            return true;
        }
        let hs = raft.hard_state();
        if hs != HardState::new() && hs != self.prev_hs {
            return true;
        }
        if Some(true) == raft.raft_log.get_unstable().snapshot.as_ref().map(|s| !is_empty_snap(s)) {
            return true;
        }
        if !raft.msgs.is_empty() || raft.raft_log.unstable_entries().is_some() ||
           raft.raft_log.has_next_entries() {
            return true;
        }
        false
    }

    // Advance notifies the RawNode that the application has applied and saved progress in the
    // last Ready results.
    pub fn advance(&mut self, rd: Ready) {
        self.commit_ready(rd);
    }

    // Status returns the current status of the given group.
    pub fn status(&self) -> Status {
        Status::new(&self.raft)
    }

    // ReportUnreachable reports the given node is not reachable for the last send.
    pub fn report_unreachable(&mut self, id: u64) {
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgUnreachable);
        m.set_from(id);
        // we don't care if it is ok actually
        self.raft.step(m).is_ok();
    }

    // ReportSnapshot reports the status of the sent snapshot.
    pub fn report_snapshot(&mut self, id: u64, status: SnapshotStatus) {
        let rej = status == SnapshotStatus::Failure;
        let mut m = Message::new();
        m.set_msg_type(MessageType::MsgSnapStatus);
        m.set_from(id);
        m.set_reject(rej);
        // we don't care if it is ok actually
        self.raft.step(m).is_ok();
    }
}
<|endoftext|>


<commit_before>// During tests, we disable the renderer and that causes a bunch of warnings that we just want
// to get rid of.
// See Cargo.toml for an explanation of this attribute
#![cfg_attr(any(feature = "test", test), allow(dead_code, unused_variables))]

use std::env;
use std::thread;
use std::process;
use std::sync::mpsc::{self, TryRecvError};

use piston_window::{
    PistonWindow,
    WindowSettings,
    G2d,
    context,
    clear,
    line,
    polygon,
};

use app::{TurtleApp, ReadOnly};
use extensions::ConvertScreenCoordinates;
use state::{Path, Polygon, Pen, TurtleState, DrawingState};
use query::DrawingCommand;
use event::{Event, from_piston_event};
use types::Point;
use color::{self, Color};
use server;

/// Setup the turtle window in advance
///
/// You must call this function at the beginning of `main()` if you do not create the
/// turtle immediately using [`Turtle::new()`].
///
/// It's a good idea to call this function before any other code runs in `main()`. Programs that
/// parse command line arguments or look at environment variables may **fail** to start if this
/// function is not called right at the beginning of the program. Programs that perform any
/// expensive computations may experience delayed start up problems unless they call this
/// function first.
///
/// The [`Turtle::new()`] method will call this function for you so that you don't need to worry
/// about this unless you are doing something before that.
///
/// # Example
/// ```rust,no_run
/// # #![allow(unused_variables, unused_mut)]
/// extern crate turtle;
/// use turtle::Turtle;
///
/// fn main() {
///     // Initializes the turtle renderer first so that there is less delay when a Turtle
///     // is created and so that there are no conflicts with command line arguments or
///     // environment variables.
///     // Not required if Turtle::new() is already at the top of main.
///     turtle::setup();
///
///     // Do all kinds of expensive work here...
///     // Feel free to check environment variables, command line arguments, etc.
///
///     // Create the turtle when you are ready
///     // Turtle::new() will also call setup(), but calling it twice doesn't matter
///     let mut turtle = Turtle::new();
///     // Do things with the turtle...
/// }
/// ```
///
/// [`Turtle::new()`]: struct.Turtle.html#method.new
pub fn setup() {
    // If this environment variable is present, this process is taken over so that no other
    // code runs after main(). This allows us to ship one executable that appears to
    // have two separate processes.
    // We run the renderer loop and then immediately exit.
    if env::var("RUN_TURTLE_CANVAS").unwrap_or_else(|_| "".to_owned()) == "true" {
        // This code MUST be run on the main thread.

        // Run the canvas process
        main();
        unreachable!("bug: renderer loop did not exit after finishing");
    }
}

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    let app = TurtleApp::new();
    let read_only = app.read_only();
    let (drawing_tx, drawing_rx) = mpsc::channel();
    let (events_tx, events_rx) = mpsc::channel();

    // The running channel is entirely for checking if the other thread is still active.
    // We need to check because while we can exit while that thread is still running, we want
    // any errors caused in that thread to be reported.
    let (running_tx, running_rx) = mpsc::channel();
    let handle = thread::spawn(move || {
        server::run(app, drawing_tx, events_rx, running_tx);
    });

    // Renderer MUST run on the main thread or else it will panic on MacOS
    #[cfg(not(any(feature = "test", test)))]
    Renderer::new().run(drawing_rx, events_tx, read_only);

    // Quit immediately when the window is closed

    // Check if an error has occurred on the thread
    match running_rx.try_recv() {
        Ok(_) => unreachable!("bug: running channel should always be empty"),
        // The thread was still running, exit normally
        Err(mpsc::TryRecvError::Empty) => process::exit(0),
        Err(mpsc::TryRecvError::Disconnected) => match handle.join() {
            Ok(_) => process::exit(0),
            // The other thread must have panicked
            Err(_) => process::exit(1),
        },
    }
}

#[derive(Debug)]
pub enum Drawing {
    Path(Path),
    Polygon(Polygon),
}

pub struct Renderer {
    drawings: Vec<Drawing>,
    /// Polygon that is currently in the process of being filled
    /// Removed when EndFill is sent
    fill_polygon: Option<(Vec<Path>, Polygon)>,
}

impl Renderer {
    pub fn new() -> Renderer {
        Self {
            drawings: Vec::new(),
            fill_polygon: None,
        }
    }

    pub fn run(
        &mut self,
        drawing_rx: mpsc::Receiver<DrawingCommand>,
        events_tx: mpsc::Sender<Event>,
        state: ReadOnly,
    ) {
        let mut window: PistonWindow = WindowSettings::new(
            "Turtle", [800, 600]
        ).exit_on_esc(true).build().unwrap();

        let mut center = [0.0, 0.0];

        'renderloop:
        while let Some(e) = window.next() {
            if let Some(event) = from_piston_event(&e, |pt| pt.to_local_coords(center)) {
                match events_tx.send(event) {
                    Ok(_) => {},
                    // Quit - the server thread must have quit
                    Err(_) => break,
                }
            }

            // Need to handle all of the queries we receive at once so that any lag caused by
            // how long rendering takes doesn't cause any problems
            loop {
                match drawing_rx.try_recv() {
                    Ok(cmd) => self.handle_drawing_command(cmd),
                    Err(TryRecvError::Empty) => break, // Do nothing
                    Err(TryRecvError::Disconnected) => break 'renderloop, // Quit
                }
            }

            window.draw_2d(&e, |c, g| {
                let view = c.get_view_size();
                let width = view[0] as f64;
                let height = view[1] as f64;
                center = [width * 0.5, height * 0.5];

                // We clone the relevant state before rendering so that the rendering thread
                // doesn't need to keep locking, waiting or making the main thread wait
                let drawing = state.drawing().clone();
                let temporary_path = state.temporary_path().clone();
                let turtle = state.turtle().clone();

                self.render(c, g, center, &drawing, &temporary_path, &turtle);
            });
        }
    }

    /// Handles a drawing command sent from the main thread
    fn handle_drawing_command(&mut self, command: DrawingCommand) {
        //NOTE: Do not pass the ReadOnly state to this function. By the time a DrawingCommand is
        // handled, that state may be completely out of date
        use self::DrawingCommand::*;
        match command {
            StorePath(path) => {
                if self.fill_polygon.is_some() {
                    let &mut (ref mut border, ref mut poly) = self.fill_polygon.as_mut().unwrap();
                    border.push(path.clone());

                    let Path {start, end, ..} = path;
                    if poly.vertices.last().map_or(true, |&v| v != start) {
                        poly.vertices.push(start);
                    }
                    poly.vertices.push(end);
                }
                else if path.pen.enabled {
                    self.drawings.push(Drawing::Path(path));
                }
            },
            BeginFill(fill_color) => {
                // Calling begin_fill multiple times is okay, it just won't do anything until
                // end_fill is called
                self.fill_polygon = self.fill_polygon.take().or_else(|| Some((Vec::new(), Polygon {
                    vertices: Vec::new(),
                    fill_color: fill_color,
                })));
            },
            // Calling end_fill multiple times is not a problem
            EndFill => if let Some((border, poly)) = self.fill_polygon.take() {
                // Always add the border over the filled polygon so the border is drawn on top
                self.drawings.push(Drawing::Polygon(poly));
                self.drawings.extend(border.into_iter().filter_map(|p| if p.pen.enabled {
                    Some(Drawing::Path(p))
                } else { None }));
            },
            Clear => {
                self.drawings.clear();
                // Because clear doesn't actually shorten the capacity of the vector
                // We don't want to leak memory by leaving this Vec at whatever its size was before
                self.drawings.shrink_to_fit();
                self.fill_polygon.take();
            }
        }
    }

    /// The main rendering route. Dispatches to other functions as needed.
    fn render(&self, c: context::Context, g: &mut G2d, center: Point,
        drawing: &DrawingState, temporary_path: &Option<Path>, turtle: &TurtleState) {
        let background = drawing.background;
        clear(background.into(), g);

        for drawing in &self.drawings {
            match *drawing {
                Drawing::Path(ref path) => self.render_path(c, g, center, path),
                Drawing::Polygon(ref poly) => self.render_polygon(c, g, center,
                    poly.fill_color, poly.vertices.iter()),
            }
        }

        if let Some(&(ref border, ref poly)) = self.fill_polygon.as_ref() {
            // If the temporary_path is not None, we need to add it to the polygon being
            // filled or else the polygon will fall one edge behind in the animation
            let extra = temporary_path.as_ref().map_or(Vec::new(), |&Path {start, end, ..}| {
                if poly.vertices.last().map_or(true, |&v| v != start) {
                    vec![start, end]
                }
                else {
                    vec![end]
                }
            });
            self.render_polygon(c, g, center, poly.fill_color,
                poly.vertices.iter().chain(extra.iter()));

            for path in border {
                if path.pen.enabled {
                    self.render_path(c, g, center, path);
                }
            }
        }

        if let Some(ref path) = *temporary_path {
            if path.pen.enabled {
                self.render_path(c, g, center, path);
            }
        }

        self.render_shell(c, g, center, turtle);
    }

    /// Render a path assuming that its pen is enabled
    fn render_path(&self, c: context::Context, g: &mut G2d, center: Point, path: &Path) {
        let &Path {start, end, ref pen} = path;
        let &Pen {thickness, color, enabled} = pen;
        debug_assert!(enabled, "bug: attempt to render path when pen was not enabled");

        let start = start.to_screen_coords(center);
        let end = end.to_screen_coords(center);

        line(color.into(), thickness,
            [start[0], start[1], end[0], end[1]],
            c.transform, g);
    }

    /// Render a polygon given its vertices
    fn render_polygon<'a, T: Iterator<Item=&'a Point>>(
        &self,
        c: context::Context,
        g: &mut G2d,
        center: Point,
        fill_color: Color,
        verts: T,
    ) {
        // Performance note: Why make this function generic instead of just taking Polygon?
        // Answer: render_polygon is called multiple times. It's called repeatedly in a loop, and
        // it is also called when drawing the current fill_polygon. Rather than moving the
        // code to *maybe* add the temporary_path to the rendered polygon into this method, we
        // avoid branching unnecessarily by allowing the repeated caller to do what is fast, then
        // doing the slow thing (checking if the path is None) only once when it is needed.
        //
        // We pass in the points as an Iterator so that they do not need to be collected into any
        // struct. This avoids an allocation that really isn't needed since these are all temporary
        // anyway. Everything is going to get copied anyway on the next line. No need to do it
        // twice.
        //
        // See the commit before this comment was added for the approach that would have required
        // branching in every iteration of the loop where render_polygon is called over and over
        // again.
        let verts = verts.map(|p| p.to_screen_coords(center)).collect::<Vec<_>>();
        polygon(fill_color.into(), &verts, c.transform, g);
    }

    /// Draw the turtle's shell
    fn render_shell(&self, c: context::Context, g: &mut G2d, center: Point,
        &TurtleState {position, heading, visible, ..}: &TurtleState) {
        // Calculate all the points on the shell by rotating the shell shape by the turtle's
        // heading and moving it to the turtle's position
        if !visible {
            return;
        }

        let cos = heading.cos();
        let sin = heading.sin();
        let turtle_x = position[0];
        let turtle_y = position[1];
        let shell: Vec<_> = [
            [0., 15.],
            [10., 0.],
            [0., -15.],
        ].into_iter().map(|pt| {
            // Rotate each point by the heading and add the current turtle position
            let x = cos * pt[0] - sin * pt[1] + turtle_x;
            let y = sin * pt[0] + cos * pt[1] + turtle_y;
            [x, y].to_screen_coords(center)
        }).collect();

        // Draw the turtle shell with its background first, then its border
        polygon(color::WHITE.into(), &shell, c.transform, g);
        for i in 0..shell.len() {
            let start = shell[i];
            let end = shell[(i + 1) % shell.len()];

            line(color::BLACK.into(), 1.,
            [start[0], start[1], end[0], end[1]],
            c.transform, g);
        }
    }
}
<commit_msg>Fixed comment that was still referring to "canvas process" instead of "renderer process"<commit_after>// During tests, we disable the renderer and that causes a bunch of warnings that we just want
// to get rid of.
// See Cargo.toml for an explanation of this attribute
#![cfg_attr(any(feature = "test", test), allow(dead_code, unused_variables))]

use std::env;
use std::thread;
use std::process;
use std::sync::mpsc::{self, TryRecvError};

use piston_window::{
    PistonWindow,
    WindowSettings,
    G2d,
    context,
    clear,
    line,
    polygon,
};

use app::{TurtleApp, ReadOnly};
use extensions::ConvertScreenCoordinates;
use state::{Path, Polygon, Pen, TurtleState, DrawingState};
use query::DrawingCommand;
use event::{Event, from_piston_event};
use types::Point;
use color::{self, Color};
use server;

/// Setup the turtle window in advance
///
/// You must call this function at the beginning of `main()` if you do not create the
/// turtle immediately using [`Turtle::new()`].
///
/// It's a good idea to call this function before any other code runs in `main()`. Programs that
/// parse command line arguments or look at environment variables may **fail** to start if this
/// function is not called right at the beginning of the program. Programs that perform any
/// expensive computations may experience delayed start up problems unless they call this
/// function first.
///
/// The [`Turtle::new()`] method will call this function for you so that you don't need to worry
/// about this unless you are doing something before that.
///
/// # Example
/// ```rust,no_run
/// # #![allow(unused_variables, unused_mut)]
/// extern crate turtle;
/// use turtle::Turtle;
///
/// fn main() {
///     // Initializes the turtle renderer first so that there is less delay when a Turtle
///     // is created and so that there are no conflicts with command line arguments or
///     // environment variables.
///     // Not required if Turtle::new() is already at the top of main.
///     turtle::setup();
///
///     // Do all kinds of expensive work here...
///     // Feel free to check environment variables, command line arguments, etc.
///
///     // Create the turtle when you are ready
///     // Turtle::new() will also call setup(), but calling it twice doesn't matter
///     let mut turtle = Turtle::new();
///     // Do things with the turtle...
/// }
/// ```
///
/// [`Turtle::new()`]: struct.Turtle.html#method.new
pub fn setup() {
    // If this environment variable is present, this process is taken over so that no other
    // code runs after main(). This allows us to ship one executable that appears to
    // have two separate processes.
    // We run the renderer loop and then immediately exit.
    if env::var("RUN_TURTLE_CANVAS").unwrap_or_else(|_| "".to_owned()) == "true" {
        // This code MUST be run on the main thread.

        // Run the renderer process
        main();
        unreachable!("bug: renderer loop did not exit after finishing");
    }
}

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    let app = TurtleApp::new();
    let read_only = app.read_only();
    let (drawing_tx, drawing_rx) = mpsc::channel();
    let (events_tx, events_rx) = mpsc::channel();

    // The running channel is entirely for checking if the other thread is still active.
    // We need to check because while we can exit while that thread is still running, we want
    // any errors caused in that thread to be reported.
    let (running_tx, running_rx) = mpsc::channel();
    let handle = thread::spawn(move || {
        server::run(app, drawing_tx, events_rx, running_tx);
    });

    // Renderer MUST run on the main thread or else it will panic on MacOS
    #[cfg(not(any(feature = "test", test)))]
    Renderer::new().run(drawing_rx, events_tx, read_only);

    // Quit immediately when the window is closed

    // Check if an error has occurred on the thread
    match running_rx.try_recv() {
        Ok(_) => unreachable!("bug: running channel should always be empty"),
        // The thread was still running, exit normally
        Err(mpsc::TryRecvError::Empty) => process::exit(0),
        Err(mpsc::TryRecvError::Disconnected) => match handle.join() {
            Ok(_) => process::exit(0),
            // The other thread must have panicked
            Err(_) => process::exit(1),
        },
    }
}

#[derive(Debug)]
pub enum Drawing {
    Path(Path),
    Polygon(Polygon),
}

pub struct Renderer {
    drawings: Vec<Drawing>,
    /// Polygon that is currently in the process of being filled
    /// Removed when EndFill is sent
    fill_polygon: Option<(Vec<Path>, Polygon)>,
}

impl Renderer {
    pub fn new() -> Renderer {
        Self {
            drawings: Vec::new(),
            fill_polygon: None,
        }
    }

    pub fn run(
        &mut self,
        drawing_rx: mpsc::Receiver<DrawingCommand>,
        events_tx: mpsc::Sender<Event>,
        state: ReadOnly,
    ) {
        let mut window: PistonWindow = WindowSettings::new(
            "Turtle", [800, 600]
        ).exit_on_esc(true).build().unwrap();

        let mut center = [0.0, 0.0];

        'renderloop:
        while let Some(e) = window.next() {
            if let Some(event) = from_piston_event(&e, |pt| pt.to_local_coords(center)) {
                match events_tx.send(event) {
                    Ok(_) => {},
                    // Quit - the server thread must have quit
                    Err(_) => break,
                }
            }

            // Need to handle all of the queries we receive at once so that any lag caused by
            // how long rendering takes doesn't cause any problems
            loop {
                match drawing_rx.try_recv() {
                    Ok(cmd) => self.handle_drawing_command(cmd),
                    Err(TryRecvError::Empty) => break, // Do nothing
                    Err(TryRecvError::Disconnected) => break 'renderloop, // Quit
                }
            }

            window.draw_2d(&e, |c, g| {
                let view = c.get_view_size();
                let width = view[0] as f64;
                let height = view[1] as f64;
                center = [width * 0.5, height * 0.5];

                // We clone the relevant state before rendering so that the rendering thread
                // doesn't need to keep locking, waiting or making the main thread wait
                let drawing = state.drawing().clone();
                let temporary_path = state.temporary_path().clone();
                let turtle = state.turtle().clone();

                self.render(c, g, center, &drawing, &temporary_path, &turtle);
            });
        }
    }

    /// Handles a drawing command sent from the main thread
    fn handle_drawing_command(&mut self, command: DrawingCommand) {
        //NOTE: Do not pass the ReadOnly state to this function. By the time a DrawingCommand is
        // handled, that state may be completely out of date
        use self::DrawingCommand::*;
        match command {
            StorePath(path) => {
                if self.fill_polygon.is_some() {
                    let &mut (ref mut border, ref mut poly) = self.fill_polygon.as_mut().unwrap();
                    border.push(path.clone());

                    let Path {start, end, ..} = path;
                    if poly.vertices.last().map_or(true, |&v| v != start) {
                        poly.vertices.push(start);
                    }
                    poly.vertices.push(end);
                }
                else if path.pen.enabled {
                    self.drawings.push(Drawing::Path(path));
                }
            },
            BeginFill(fill_color) => {
                // Calling begin_fill multiple times is okay, it just won't do anything until
                // end_fill is called
                self.fill_polygon = self.fill_polygon.take().or_else(|| Some((Vec::new(), Polygon {
                    vertices: Vec::new(),
                    fill_color: fill_color,
                })));
            },
            // Calling end_fill multiple times is not a problem
            EndFill => if let Some((border, poly)) = self.fill_polygon.take() {
                // Always add the border over the filled polygon so the border is drawn on top
                self.drawings.push(Drawing::Polygon(poly));
                self.drawings.extend(border.into_iter().filter_map(|p| if p.pen.enabled {
                    Some(Drawing::Path(p))
                } else { None }));
            },
            Clear => {
                self.drawings.clear();
                // Because clear doesn't actually shorten the capacity of the vector
                // We don't want to leak memory by leaving this Vec at whatever its size was before
                self.drawings.shrink_to_fit();
                self.fill_polygon.take();
            }
        }
    }

    /// The main rendering route. Dispatches to other functions as needed.
    fn render(&self, c: context::Context, g: &mut G2d, center: Point,
        drawing: &DrawingState, temporary_path: &Option<Path>, turtle: &TurtleState) {
        let background = drawing.background;
        clear(background.into(), g);

        for drawing in &self.drawings {
            match *drawing {
                Drawing::Path(ref path) => self.render_path(c, g, center, path),
                Drawing::Polygon(ref poly) => self.render_polygon(c, g, center,
                    poly.fill_color, poly.vertices.iter()),
            }
        }

        if let Some(&(ref border, ref poly)) = self.fill_polygon.as_ref() {
            // If the temporary_path is not None, we need to add it to the polygon being
            // filled or else the polygon will fall one edge behind in the animation
            let extra = temporary_path.as_ref().map_or(Vec::new(), |&Path {start, end, ..}| {
                if poly.vertices.last().map_or(true, |&v| v != start) {
                    vec![start, end]
                }
                else {
                    vec![end]
                }
            });
            self.render_polygon(c, g, center, poly.fill_color,
                poly.vertices.iter().chain(extra.iter()));

            for path in border {
                if path.pen.enabled {
                    self.render_path(c, g, center, path);
                }
            }
        }

        if let Some(ref path) = *temporary_path {
            if path.pen.enabled {
                self.render_path(c, g, center, path);
            }
        }

        self.render_shell(c, g, center, turtle);
    }

    /// Render a path assuming that its pen is enabled
    fn render_path(&self, c: context::Context, g: &mut G2d, center: Point, path: &Path) {
        let &Path {start, end, ref pen} = path;
        let &Pen {thickness, color, enabled} = pen;
        debug_assert!(enabled, "bug: attempt to render path when pen was not enabled");

        let start = start.to_screen_coords(center);
        let end = end.to_screen_coords(center);

        line(color.into(), thickness,
            [start[0], start[1], end[0], end[1]],
            c.transform, g);
    }

    /// Render a polygon given its vertices
    fn render_polygon<'a, T: Iterator<Item=&'a Point>>(
        &self,
        c: context::Context,
        g: &mut G2d,
        center: Point,
        fill_color: Color,
        verts: T,
    ) {
        // Performance note: Why make this function generic instead of just taking Polygon?
        // Answer: render_polygon is called multiple times. It's called repeatedly in a loop, and
        // it is also called when drawing the current fill_polygon. Rather than moving the
        // code to *maybe* add the temporary_path to the rendered polygon into this method, we
        // avoid branching unnecessarily by allowing the repeated caller to do what is fast, then
        // doing the slow thing (checking if the path is None) only once when it is needed.
        //
        // We pass in the points as an Iterator so that they do not need to be collected into any
        // struct. This avoids an allocation that really isn't needed since these are all temporary
        // anyway. Everything is going to get copied anyway on the next line. No need to do it
        // twice.
        //
        // See the commit before this comment was added for the approach that would have required
        // branching in every iteration of the loop where render_polygon is called over and over
        // again.
        let verts = verts.map(|p| p.to_screen_coords(center)).collect::<Vec<_>>();
        polygon(fill_color.into(), &verts, c.transform, g);
    }

    /// Draw the turtle's shell
    fn render_shell(&self, c: context::Context, g: &mut G2d, center: Point,
        &TurtleState {position, heading, visible, ..}: &TurtleState) {
        // Calculate all the points on the shell by rotating the shell shape by the turtle's
        // heading and moving it to the turtle's position
        if !visible {
            return;
        }

        let cos = heading.cos();
        let sin = heading.sin();
        let turtle_x = position[0];
        let turtle_y = position[1];
        let shell: Vec<_> = [
            [0., 15.],
            [10., 0.],
            [0., -15.],
        ].into_iter().map(|pt| {
            // Rotate each point by the heading and add the current turtle position
            let x = cos * pt[0] - sin * pt[1] + turtle_x;
            let y = sin * pt[0] + cos * pt[1] + turtle_y;
            [x, y].to_screen_coords(center)
        }).collect();

        // Draw the turtle shell with its background first, then its border
        polygon(color::WHITE.into(), &shell, c.transform, g);
        for i in 0..shell.len() {
            let start = shell[i];
            let end = shell[(i + 1) % shell.len()];

            line(color::BLACK.into(), 1.,
            [start[0], start[1], end[0], end[1]],
            c.transform, g);
        }
    }
}
<|endoftext|>


<commit_before>use std::process;
use std::sync::mpsc::{self, TryRecvError};

use piston_window::{
    PistonWindow,
    G2d,
    context,
    clear,
    line,
    polygon,
};

use turtle_window::ReadOnly;
use extensions::ConvertScreenCoordinates;
use state::{Path, Polygon, Pen, TurtleState};
use event::from_piston_event;
use {Point, Event, color};

pub enum DrawingCommand {
    /// When a path is finished being animated, it needs to be persisted in the renderer
    /// so it can be redrawn every frame
    StorePath(Path),
    /// Begins filling with the current fill color from the next path onwards. If temporary_path is
    /// set, it is included in the fill shape. Any paths sent via StorePath will be added to the
    /// filled shape.
    BeginFill,
    /// Send EndFill to finish the filled shape.
    EndFill,
    /// Clears the image completely
    ///
    /// Panics if temporary_path is not None
    Clear,
}

pub enum Drawing {
    Path(Path),
    Polygon(Polygon),
}

pub struct Renderer {
    drawings: Vec<Drawing>,
    /// Polygon that is currently in the process of being filled
    /// Removed when EndFill is sent
    fill_polygon: Option<(Vec<Path>, Polygon)>,
}

impl Renderer {
    pub fn new() -> Renderer {
        Self {
            drawings: Vec::new(),
            fill_polygon: None,
        }
    }

    pub fn run(
        &mut self,
        window: &mut PistonWindow,
        drawing_rx: mpsc::Receiver<DrawingCommand>,
        events_tx: mpsc::Sender<Event>,
        state: ReadOnly,
    ) {
        let mut center = [0.0, 0.0];

        'renderloop:
        while let Some(e) = window.next() {
            if let Some(event) = from_piston_event(&e, |pt| pt.to_local_coords(center)) {
                match events_tx.send(event) {
                    Ok(_) => {},
                    // Quit
                    Err(_) => break,
                }
            }

            loop {
                match drawing_rx.try_recv() {
                    Ok(cmd) => self.handle_drawing_command(cmd, &state),
                    Err(TryRecvError::Empty) => break, // Do nothing
                    Err(TryRecvError::Disconnected) => break 'renderloop, // Quit
                }
            }

            window.draw_2d(&e, |c, g| {
                let background = state.drawing().background;
                clear(background.into(), g);

                let view = c.get_view_size();
                let width = view[0] as f64;
                let height = view[1] as f64;
                center = [width * 0.5, height * 0.5];

                for drawing in &self.drawings {
                    match *drawing {
                        Drawing::Path(ref path) => self.render_path(c, g, center, path),
                        Drawing::Polygon(ref poly) => self.render_polygon(c, g, center, poly),
                    }
                }

                if let Some(&(ref border, ref poly)) = self.fill_polygon.as_ref() {
                    self.render_polygon(c, g, center, poly);
                    for path in border {
                        self.render_path(c, g, center, path);
                    }
                }
                //TODO: Render the temporary_path as part of the polygon when fill_polygon.is_some()

                if let Some(ref path) = *state.temporary_path() {
                    if path.pen.enabled {
                        self.render_path(c, g, center, path);
                    }
                }

                self.render_shell(c, g, center, &state);
            });
        }

        // Quit immediately when the window is closed
        process::exit(0);
    }

    /// Handles a drawing command sent from the main thread
    fn handle_drawing_command(&mut self, command: DrawingCommand, state: &ReadOnly) {
        use self::DrawingCommand::*;
        match command {
            StorePath(path) => {
                if self.fill_polygon.is_some() {
                    let &mut (ref mut border, ref mut poly) = self.fill_polygon.as_mut().unwrap();
                    border.push(path.clone());

                    let Path {start, end, ..} = path;
                    if poly.vertices.last().map_or(true, |&v| v != path.start) {
                        poly.vertices.push(start);
                    }
                    poly.vertices.push(end);
                }
                else if path.pen.enabled {
                    self.drawings.push(Drawing::Path(path));
                }
            },
            BeginFill => {
                // Calling begin_fill multiple times is okay, it just won't do anything until
                // end_fill is called
                self.fill_polygon = self.fill_polygon.take().or_else(|| Some((Vec::new(), Polygon {
                    vertices: Vec::new(),
                    fill_color: state.drawing().fill_color,
                })));
            },
            // Calling end_fill multiple times is not a problem
            EndFill => if let Some((border, poly)) = self.fill_polygon.take() {
                // Always add the border over the filled polygon so the border is drawn on top
                self.drawings.push(Drawing::Polygon(poly));
                self.drawings.extend(border.into_iter().map(Drawing::Path));
            },
            Clear => {
                assert!(state.temporary_path().is_none());
                unimplemented!(); //TODO
            }
        }
    }

    /// Render a path assuming that its pen is enabled
    fn render_path(&self, c: context::Context, g: &mut G2d, center: Point, path: &Path) {
        let &Path {start, end, ref pen} = path;
        let &Pen {thickness, color, enabled} = pen;
        debug_assert!(enabled, "bug: attempt to render path when pen was not enabled");

        let start = start.to_screen_coords(center);
        let end = end.to_screen_coords(center);

        line(color.into(), thickness,
            [start[0], start[1], end[0], end[1]],
            c.transform, g);
    }

    /// Render a polygon
    fn render_polygon(&self, c: context::Context, g: &mut G2d, center: Point, poly: &Polygon) {
        let verts = poly.vertices.iter().map(|p| p.to_screen_coords(center)).collect::<Vec<_>>();
        polygon(poly.fill_color.into(), &verts, c.transform, g);
    }

    /// Draw the turtle's shell
    fn render_shell(&self, c: context::Context, g: &mut G2d, center: Point, state: &ReadOnly) {
        // Calculate all the points on the shell by rotating the shell shape by the turtle's
        // heading and moving it to the turtle's position
        let TurtleState {position, heading, visible, ..} = *state.turtle();
        if !visible {
            return;
        }

        let cos = heading.cos();
        let sin = heading.sin();
        let turtle_x = position[0];
        let turtle_y = position[1];
        let shell: Vec<_> = [
            [0., 15.],
            [10., 0.],
            [0., -15.],
        ].into_iter().map(|pt| {
            // Rotate each point by the heading and add the current turtle position
            let x = cos * pt[0] - sin * pt[1] + turtle_x;
            let y = sin * pt[0] + cos * pt[1] + turtle_y;
            [x, y].to_screen_coords(center)
        }).collect();

        // Draw the turtle shell with its background first, then its border
        polygon(color::WHITE.into(), &shell, c.transform, g);
        for i in 0..shell.len() {
            let start = shell[i];
            let end = shell[(i + 1) % shell.len()];

            line(color::BLACK.into(), 1.,
            [start[0], start[1], end[0], end[1]],
            c.transform, g);
        }
    }
}
<commit_msg>Fixed rendering bug that was causing paths that were not enabled to still get put into render_path calls<commit_after>use std::process;
use std::sync::mpsc::{self, TryRecvError};

use piston_window::{
    PistonWindow,
    G2d,
    context,
    clear,
    line,
    polygon,
};

use turtle_window::ReadOnly;
use extensions::ConvertScreenCoordinates;
use state::{Path, Polygon, Pen, TurtleState};
use event::from_piston_event;
use {Point, Event, color};

pub enum DrawingCommand {
    /// When a path is finished being animated, it needs to be persisted in the renderer
    /// so it can be redrawn every frame
    StorePath(Path),
    /// Begins filling with the current fill color from the next path onwards. If temporary_path is
    /// set, it is included in the fill shape. Any paths sent via StorePath will be added to the
    /// filled shape.
    BeginFill,
    /// Send EndFill to finish the filled shape.
    EndFill,
    /// Clears the image completely
    ///
    /// Panics if temporary_path is not None
    Clear,
}

pub enum Drawing {
    Path(Path),
    Polygon(Polygon),
}

pub struct Renderer {
    drawings: Vec<Drawing>,
    /// Polygon that is currently in the process of being filled
    /// Removed when EndFill is sent
    fill_polygon: Option<(Vec<Path>, Polygon)>,
}

impl Renderer {
    pub fn new() -> Renderer {
        Self {
            drawings: Vec::new(),
            fill_polygon: None,
        }
    }

    pub fn run(
        &mut self,
        window: &mut PistonWindow,
        drawing_rx: mpsc::Receiver<DrawingCommand>,
        events_tx: mpsc::Sender<Event>,
        state: ReadOnly,
    ) {
        let mut center = [0.0, 0.0];

        'renderloop:
        while let Some(e) = window.next() {
            if let Some(event) = from_piston_event(&e, |pt| pt.to_local_coords(center)) {
                match events_tx.send(event) {
                    Ok(_) => {},
                    // Quit
                    Err(_) => break,
                }
            }

            loop {
                match drawing_rx.try_recv() {
                    Ok(cmd) => self.handle_drawing_command(cmd, &state),
                    Err(TryRecvError::Empty) => break, // Do nothing
                    Err(TryRecvError::Disconnected) => break 'renderloop, // Quit
                }
            }

            window.draw_2d(&e, |c, g| {
                let background = state.drawing().background;
                clear(background.into(), g);

                let view = c.get_view_size();
                let width = view[0] as f64;
                let height = view[1] as f64;
                center = [width * 0.5, height * 0.5];

                for drawing in &self.drawings {
                    match *drawing {
                        Drawing::Path(ref path) => self.render_path(c, g, center, path),
                        Drawing::Polygon(ref poly) => self.render_polygon(c, g, center, poly),
                    }
                }

                if let Some(&(ref border, ref poly)) = self.fill_polygon.as_ref() {
                    self.render_polygon(c, g, center, poly);
                    for path in border {
                        if path.pen.enabled {
                            self.render_path(c, g, center, path);
                        }
                    }
                }
                //TODO: Render the temporary_path as part of the polygon when fill_polygon.is_some()

                if let Some(ref path) = *state.temporary_path() {
                    if path.pen.enabled {
                        self.render_path(c, g, center, path);
                    }
                }

                self.render_shell(c, g, center, &state);
            });
        }

        // Quit immediately when the window is closed
        process::exit(0);
    }

    /// Handles a drawing command sent from the main thread
    fn handle_drawing_command(&mut self, command: DrawingCommand, state: &ReadOnly) {
        use self::DrawingCommand::*;
        match command {
            StorePath(path) => {
                if self.fill_polygon.is_some() {
                    let &mut (ref mut border, ref mut poly) = self.fill_polygon.as_mut().unwrap();
                    border.push(path.clone());

                    let Path {start, end, ..} = path;
                    if poly.vertices.last().map_or(true, |&v| v != path.start) {
                        poly.vertices.push(start);
                    }
                    poly.vertices.push(end);
                }
                else if path.pen.enabled {
                    self.drawings.push(Drawing::Path(path));
                }
            },
            BeginFill => {
                // Calling begin_fill multiple times is okay, it just won't do anything until
                // end_fill is called
                self.fill_polygon = self.fill_polygon.take().or_else(|| Some((Vec::new(), Polygon {
                    vertices: Vec::new(),
                    fill_color: state.drawing().fill_color,
                })));
            },
            // Calling end_fill multiple times is not a problem
            EndFill => if let Some((border, poly)) = self.fill_polygon.take() {
                // Always add the border over the filled polygon so the border is drawn on top
                self.drawings.push(Drawing::Polygon(poly));
                self.drawings.extend(border.into_iter().filter_map(|p| if p.pen.enabled {
                    Some(Drawing::Path(p))
                } else { None }));
            },
            Clear => {
                assert!(state.temporary_path().is_none());
                unimplemented!(); //TODO
            }
        }
    }

    /// Render a path assuming that its pen is enabled
    fn render_path(&self, c: context::Context, g: &mut G2d, center: Point, path: &Path) {
        let &Path {start, end, ref pen} = path;
        let &Pen {thickness, color, enabled} = pen;
        debug_assert!(enabled, "bug: attempt to render path when pen was not enabled");

        let start = start.to_screen_coords(center);
        let end = end.to_screen_coords(center);

        line(color.into(), thickness,
            [start[0], start[1], end[0], end[1]],
            c.transform, g);
    }

    /// Render a polygon
    fn render_polygon(&self, c: context::Context, g: &mut G2d, center: Point, poly: &Polygon) {
        let verts = poly.vertices.iter().map(|p| p.to_screen_coords(center)).collect::<Vec<_>>();
        polygon(poly.fill_color.into(), &verts, c.transform, g);
    }

    /// Draw the turtle's shell
    fn render_shell(&self, c: context::Context, g: &mut G2d, center: Point, state: &ReadOnly) {
        // Calculate all the points on the shell by rotating the shell shape by the turtle's
        // heading and moving it to the turtle's position
        let TurtleState {position, heading, visible, ..} = *state.turtle();
        if !visible {
            return;
        }

        let cos = heading.cos();
        let sin = heading.sin();
        let turtle_x = position[0];
        let turtle_y = position[1];
        let shell: Vec<_> = [
            [0., 15.],
            [10., 0.],
            [0., -15.],
        ].into_iter().map(|pt| {
            // Rotate each point by the heading and add the current turtle position
            let x = cos * pt[0] - sin * pt[1] + turtle_x;
            let y = sin * pt[0] + cos * pt[1] + turtle_y;
            [x, y].to_screen_coords(center)
        }).collect();

        // Draw the turtle shell with its background first, then its border
        polygon(color::WHITE.into(), &shell, c.transform, g);
        for i in 0..shell.len() {
            let start = shell[i];
            let end = shell[(i + 1) % shell.len()];

            line(color::BLACK.into(), 1.,
            [start[0], start[1], end[0], end[1]],
            c.transform, g);
        }
    }
}
<|endoftext|>


<commit_before>#![allow(unused_imports)]

use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
use std::borrow::Cow;
use std::convert::Into;
use std::ffi::CStr;
use std::mem;
use sys::{MTLCPUCacheMode, MTLPurgeableState, MTLResource, MTLResourceCPUCacheModeDefaultCache,
          MTLResourceCPUCacheModeShift, MTLResourceCPUCacheModeWriteCombined,
          MTLResourceOptionCPUCacheModeDefault, MTLResourceOptionCPUCacheModeWriteCombined,
          MTLResourceOptions, MTLResourceStorageModePrivate, MTLResourceStorageModeShared,
          MTLResourceStorageModeShift, MTLStorageMode};
use Device;

pub struct Resource(id);

impl Resource {
    pub fn cpu_cache_mode(self) -> CpuCacheMode {
        unsafe { self.0.cpuCacheMode().into() }
    }

    pub fn storage_mode(self) -> StorageMode {
        unsafe { self.0.storageMode().into() }
    }

    pub fn device(&self) -> &Device {
        unsafe { mem::transmute(self.0.device()) }
    }

    pub fn label(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.label().UTF8String()).to_string_lossy() }
    }

    pub fn set_label(&mut self, label: &str) {
        unsafe { self.0.setLabel(NSString::alloc(nil).init_str(label)) }
    }

    pub fn set_purgeable_state(self, state: PurgeableState) -> PurgeableState {
        unsafe { self.0.setPurgeableState(state.into()).into() }
    }
}

impl_from_into_raw!(Resource, of protocol "MTLResource");

// TODO(burtonageo): Implement properly
pub struct ResourceOptions;

// TODO(burtonageo): Implement properly
impl Into<MTLResourceOptions> for ResourceOptions {
    fn into(self) -> MTLResourceOptions {
        MTLResourceOptionCPUCacheModeDefault
    }
}

convertible_enum! {
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    pub enum StorageMode: MTLStorageMode {
        Shared => MTLStorageModeShared,
        Private => MTLStorageModePrivate
    }
}

convertible_enum! {
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    pub enum CpuCacheMode: MTLCPUCacheMode {
        DefaultCache => MTLCPUCacheModeDefaultCache,
        WriteCombined => MTLCPUCacheModeWriteCombined
    }
}

convertible_enum! {
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    pub enum PurgeableState: MTLPurgeableState {
       KeepCurrent => MTLPurgeableStateKeepCurrent,
       NonVolatile => MTLPurgeableStateNonVolatile,
       Volatile => MTLPurgeableStateVolatile,
       Empty => MTLPurgeableStateEmpty
    }
}
<commit_msg>Add impl From<MTLResourceOptions> for ResourceOptions<commit_after>#![allow(unused_imports)]

use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
use std::borrow::Cow;
use std::convert::Into;
use std::ffi::CStr;
use std::mem;
use sys::{MTLCPUCacheMode, MTLPurgeableState, MTLResource, MTLResourceCPUCacheModeDefaultCache,
          MTLResourceCPUCacheModeShift, MTLResourceCPUCacheModeWriteCombined,
          MTLResourceOptionCPUCacheModeDefault, MTLResourceOptionCPUCacheModeWriteCombined,
          MTLResourceOptions, MTLResourceStorageModePrivate, MTLResourceStorageModeShared,
          MTLResourceStorageModeShift, MTLStorageMode};
use Device;

pub struct Resource(id);

impl Resource {
    pub fn cpu_cache_mode(self) -> CpuCacheMode {
        unsafe { self.0.cpuCacheMode().into() }
    }

    pub fn storage_mode(self) -> StorageMode {
        unsafe { self.0.storageMode().into() }
    }

    pub fn device(&self) -> &Device {
        unsafe { mem::transmute(self.0.device()) }
    }

    pub fn label(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.label().UTF8String()).to_string_lossy() }
    }

    pub fn set_label(&mut self, label: &str) {
        unsafe { self.0.setLabel(NSString::alloc(nil).init_str(label)) }
    }

    pub fn set_purgeable_state(self, state: PurgeableState) -> PurgeableState {
        unsafe { self.0.setPurgeableState(state.into()).into() }
    }
}

impl_from_into_raw!(Resource, of protocol "MTLResource");

// TODO(burtonageo): Implement properly
pub struct ResourceOptions;

// TODO(burtonageo): Implement properly
impl Into<MTLResourceOptions> for ResourceOptions {
    fn into(self) -> MTLResourceOptions {
        MTLResourceOptionCPUCacheModeDefault
    }
}

// TODO(burtonageo): Implement properly
impl From<MTLResourceOptions> for ResourceOptions {
    fn from(_options: MTLResourceOptions) -> Self {
        ResourceOptions
    }
}

convertible_enum! {
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    pub enum StorageMode: MTLStorageMode {
        Shared => MTLStorageModeShared,
        Private => MTLStorageModePrivate
    }
}

convertible_enum! {
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    pub enum CpuCacheMode: MTLCPUCacheMode {
        DefaultCache => MTLCPUCacheModeDefaultCache,
        WriteCombined => MTLCPUCacheModeWriteCombined
    }
}

convertible_enum! {
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    pub enum PurgeableState: MTLPurgeableState {
       KeepCurrent => MTLPurgeableStateKeepCurrent,
       NonVolatile => MTLPurgeableStateNonVolatile,
       Volatile => MTLPurgeableStateVolatile,
       Empty => MTLPurgeableStateEmpty
    }
}
<|endoftext|>