Skip to content

Commit

Permalink
#23 caching scatter plots
Browse files Browse the repository at this point in the history
  • Loading branch information
ilya-epifanov committed Mar 9, 2019
1 parent c083b3d commit b8effcf
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 57 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
* Improved installation guide in `README.md`
* Better text rendering on the graph
* Caching scatter plots #23

## [0.2.1] - 2019-03-07
### Added
Expand Down
14 changes: 11 additions & 3 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ extern crate criterion;

use std::time::Duration;

use criterion::{black_box, BatchSize, Criterion};
use criterion::black_box;
use criterion::BatchSize;
use criterion::Criterion;

use ks_curve_tracer::gui::TraceWithModel;
use ks_curve_tracer::trace::file::ImportableTrace;
use ks_curve_tracer::AreaOfInterest;
use ks_curve_tracer::DeviceType;
use ks_curve_tracer::TwoTerminalDeviceType;
use ks_curve_tracer::TwoTerminalTrace;

fn criterion_config() -> Criterion {
Expand All @@ -19,8 +24,11 @@ fn criterion_benchmark(c: &mut Criterion) {
c.bench_function_over_inputs(
"Shockley model",
move |b, trace_name| {
let trace = TwoTerminalTrace::from_csv(format!("res/{}.csv", trace_name))
.expect("Can't read the test trace");
let trace = TwoTerminalTrace::from_csv(
format!("res/{}.csv", trace_name),
AreaOfInterest::from(DeviceType::TwoTerminal(TwoTerminalDeviceType::Diode)),
)
.expect("Can't read the test trace");
b.iter_batched_ref(
|| trace.clone(),
|trace| {
Expand Down
3 changes: 2 additions & 1 deletion src/backend/ad2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use crate::backend::Backend;
use crate::backend::BiasedTrace;
use crate::backend::RawTrace;
use crate::util::Try;
use crate::{ThreeTerminalDeviceType, TwoTerminalDeviceType};
use crate::ThreeTerminalDeviceType;
use crate::TwoTerminalDeviceType;

pub struct AD2 {
device: Device,
Expand Down
5 changes: 5 additions & 0 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct RawTrace {
voltage: Vec<f64>,
}

#[allow(clippy::len_without_is_empty)]
impl RawTrace {
pub fn new(current: Vec<f64>, voltage: Vec<f64>) -> Self {
assert_eq!(current.len(), voltage.len());
Expand All @@ -30,6 +31,10 @@ impl RawTrace {
.cloned()
.zip(self.current.iter().cloned())
}

pub fn len(&self) -> usize {
self.voltage.len()
}
}

pub trait Backend {
Expand Down
34 changes: 25 additions & 9 deletions src/bin/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,14 @@ use ks_curve_tracer::gui::GuiTrace;
use ks_curve_tracer::options::GuiOpt;
use ks_curve_tracer::options::Opt;
use ks_curve_tracer::trace::file::ImportableTrace;
use ks_curve_tracer::AreaOfInterest;
use ks_curve_tracer::DeviceType;
use ks_curve_tracer::NullTrace;
use ks_curve_tracer::Result;
use ks_curve_tracer::ThreeTerminalDeviceType;
use ks_curve_tracer::ThreeTerminalTrace;
use ks_curve_tracer::TwoTerminalDeviceType;
use ks_curve_tracer::TwoTerminalTrace;
use ks_curve_tracer::{NullTrace, ThreeTerminalDeviceType, TwoTerminalDeviceType};

struct Model {
relm: Relm<Win>,
Expand Down Expand Up @@ -150,7 +153,10 @@ impl Update for Win {
match self.model.device_type {
DeviceType::TwoTerminal(device_type) => match device.trace_2(device_type) {
Ok(trace) => {
self.model.trace = Box::new(TwoTerminalTrace::from(trace));
self.model.trace = Box::new(TwoTerminalTrace::from_raw_trace(
trace,
AreaOfInterest::from(self.model.device_type),
));
info!("Got the trace");

self.widgets.model_text.set_markup("");
Expand All @@ -165,9 +171,15 @@ impl Update for Win {
DeviceType::ThreeTerminal(device_type) => match device.trace_3(device_type) {
Ok(traces) => {
self.model.trace = Box::new(ThreeTerminalTrace::from(
traces
.into_iter()
.map(|BiasedTrace { bias, trace }| (bias, trace)),
traces.into_iter().map(|BiasedTrace { bias, trace }| {
(
bias,
TwoTerminalTrace::from_raw_trace(
trace,
AreaOfInterest::from(self.model.device_type),
),
)
}),
));
info!("Got the trace");

Expand Down Expand Up @@ -352,11 +364,15 @@ impl Update for Win {
if let Some(filename) = dialog.get_filename() {
let res = try {
self.model.trace = match self.model.device_type {
DeviceType::TwoTerminal(_) => {
Box::new(TwoTerminalTrace::from_csv(filename)?)
}
DeviceType::TwoTerminal(_) => Box::new(TwoTerminalTrace::from_csv(
filename,
AreaOfInterest::from(self.model.device_type),
)?),
DeviceType::ThreeTerminal(_) => {
Box::new(ThreeTerminalTrace::from_csv(filename)?)
Box::new(ThreeTerminalTrace::from_csv(
filename,
AreaOfInterest::from(self.model.device_type),
)?)
}
};
info!("Got the trace");
Expand Down
121 changes: 97 additions & 24 deletions src/gui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@ use std::f64::consts::PI;
use std::sync::Arc;

use cairo::Context;
use cairo::Format;
use cairo::ImageSurface;
use cairo::Operator;
use itertools::Itertools;
use itertools_num::linspace;

use crate::model::diode::diode_model;
use crate::trace::file::ExportableTrace;
use crate::DeviceType;
use crate::NullTrace;
use crate::Result;
use crate::ThreeTerminalDeviceType;
use crate::ThreeTerminalTrace;
use crate::TwoTerminalDeviceType;
use crate::TwoTerminalTrace;
use crate::{DeviceType, NullTrace, ThreeTerminalDeviceType, TwoTerminalDeviceType};
use noisy_float::prelude::r64;

const MASK_WIDTH: i32 = 10000;
const MASK_HEIGHT: i32 = 2500;
const SCATTER_PLOT_ALPHA: f64 = 0.05;

const COLORS: [(u8, u8, u8); 8] = [
(57, 106, 177),
Expand Down Expand Up @@ -51,6 +63,10 @@ pub trait TraceWithModel {
fn model_report(&self) -> String;
}

pub trait TraceWithScatterPlot {
fn apply_mask(&self, cr: &Context) -> Result<()>;
}

pub trait DrawableTrace: TraceWithModel {
fn draw(&self, cr: &Context, v_factor: f64, i_factor: f64, height: f64);
fn draw_model(&self, cr: &Context, v_factor: f64, i_factor: f64, height: f64);
Expand Down Expand Up @@ -85,17 +101,23 @@ impl TraceWithModel for TwoTerminalTrace {

impl DrawableTrace for TwoTerminalTrace {
fn draw(&self, cr: &Context, v_factor: f64, i_factor: f64, height: f64) {
cr.set_source_rgba(0.0, 0.0, 0.0, 0.05);
for (v, i) in self.trace.iter() {
cr.arc(
v * v_factor,
height - 20.0 - i * i_factor,
1.0,
0.0,
PI * 2.0,
);
cr.fill();
}
let w = f64::from(MASK_WIDTH);
let h = f64::from(MASK_HEIGHT);

let v_k = w / (self.aoi.max_v - self.aoi.min_v);
let v_b = self.aoi.min_v * v_k;

let i_k = h / (self.aoi.max_i - self.aoi.min_i);
let i_b = self.aoi.min_i * i_k;

cr.save();
cr.set_source_rgba(0.0, 0.0, 0.0, 1.0);
cr.translate(0.0, height - 20.0);
cr.scale(v_factor / v_k, i_factor / -i_k);
cr.translate(v_b, i_b);
self.apply_mask(cr).unwrap();
cr.fill();
cr.restore();
}

fn draw_model(&self, cr: &Context, v_factor: f64, i_factor: f64, height: f64) {
Expand All @@ -115,6 +137,45 @@ impl DrawableTrace for TwoTerminalTrace {
}
}

impl TraceWithScatterPlot for TwoTerminalTrace {
fn apply_mask(&self, cr: &Context) -> Result<()> {
let mut scatter_plot_mask = self.scatter_plot_mask.borrow_mut();
if scatter_plot_mask.is_none() {
let w = MASK_WIDTH;
let h = MASK_HEIGHT;
let surface = ImageSurface::create(Format::A8, w, h)
.map_err(|_| failure::err_msg("Can't create an off-screen surface"))?;
let cr = Context::new(&surface);
cr.save();
cr.set_source_rgba(0.0, 0.0, 0.0, 0.0);
cr.set_operator(Operator::Source);
cr.paint();
cr.restore();

cr.set_operator(Operator::Over);
cr.set_source_rgba(0.0, 0.0, 0.0, SCATTER_PLOT_ALPHA);

let v_k = f64::from(w) / (self.aoi.max_v - self.aoi.min_v);
let v_b = -self.aoi.min_v * v_k;

let i_k = f64::from(h) / (self.aoi.max_i - self.aoi.min_i);
let i_b = -self.aoi.min_i * i_k;

for (v, i) in self.trace.iter() {
cr.arc(v_b + v * v_k, i_b + i * i_k, 1.0, 0.0, PI * 2.0);
cr.fill();
}
drop(cr);
*scatter_plot_mask = Some(surface);
}

if let Some(mask) = &*scatter_plot_mask {
cr.mask_surface(&mask, 0.0, 0.0);
}
Ok(())
}
}

impl TraceWithModel for ThreeTerminalTrace {
fn fill_model(&mut self) {}
fn model_report(&self) -> String {
Expand All @@ -124,18 +185,30 @@ impl TraceWithModel for ThreeTerminalTrace {

impl DrawableTrace for ThreeTerminalTrace {
fn draw(&self, cr: &Context, v_factor: f64, i_factor: f64, height: f64) {
for ((_, trace), color) in self.traces.iter().zip(COLORS_F64.iter()) {
cr.set_source_rgba(color.0, color.1, color.2, 0.05);
for (v, i) in trace.trace.iter() {
cr.arc(
v * v_factor,
height - 20.0 - i * i_factor,
1.0,
0.0,
PI * 2.0,
);
cr.fill();
}
// FIXME: trace should know it's very own device type
let traces = if *self.traces.iter().last().unwrap().0 < r64(0.0) {
self.traces.iter().rev().collect_vec()
} else {
self.traces.iter().collect_vec()
};
for ((_, trace), color) in traces.iter().zip(COLORS_F64.iter()) {
let w = f64::from(MASK_WIDTH);
let h = f64::from(MASK_HEIGHT);

let v_k = w / (trace.aoi.max_v - trace.aoi.min_v);
let v_b = trace.aoi.min_v * v_k;

let i_k = h / (trace.aoi.max_i - trace.aoi.min_i);
let i_b = trace.aoi.min_i * i_k;

cr.save();
cr.set_source_rgba(color.0, color.1, color.2, 1.0);
cr.translate(0.0, height - 20.0);
cr.scale(v_factor / v_k, i_factor / -i_k);
cr.translate(v_b, i_b);
trace.apply_mask(cr).unwrap();
cr.fill();
cr.restore();
}
}

Expand Down
Loading

0 comments on commit b8effcf

Please sign in to comment.