Commit 8fdc879d authored by Imran Salam's avatar Imran Salam
Browse files
parents abfeafbc e09d8827
use std::fmt;
use composit::Zoom;
use types::{ Dot, Direction, LEFT, RIGHT, UP, DOWN };
use types::{ Dot, Direction, LEFT, RIGHT, UP, DOWN, IN, OUT, AxisSense };
pub struct Position {
pos: Dot<f64,f64>,
......@@ -55,12 +55,38 @@ impl Position {
self.pos.1 = self.pos.1.round();
}
pub fn middle_to_edge(&self, which: &Direction) -> f64 {
let bp = self.get_screen_in_bp();
match *which {
LEFT => - bp/2. + self.px_to_bp(self.min_x_bumper),
RIGHT => bp/2. - self.px_to_bp(self.max_x_bumper),
UP => - self.screen_size.1 as f64/2.,
DOWN => self.screen_size.1 as f64/2.,
IN|OUT => 0.
}
}
pub fn get_edge(&self, which: &Direction) -> f64 {
let delta = self.middle_to_edge(which);
match *which {
LEFT|RIGHT => self.pos.0 + delta,
UP|DOWN => self.pos.1 + delta,
IN|OUT => self.zoom.get_zoom()
}
}
pub fn get_limit_of_middle(&self, which: &Direction) -> f64 {
self.get_limit_of_edge(which) - self.middle_to_edge(which)
}
pub fn get_limit_of_edge(&self, which: &Direction) -> f64 {
match *which {
LEFT => self.pos.0 - self.screen_size.0 as f64/2.,
RIGHT => self.pos.0 + self.screen_size.0 as f64/2.,
UP => self.pos.1 - self.screen_size.1 as f64/2.,
DOWN => self.pos.1 + self.screen_size.1 as f64/2.
LEFT => self.min_x,
RIGHT => self.max_x,
DOWN => self.max_y as f64,
UP => 0.,
IN => self.zoom.get_limit(&AxisSense::Pos),
OUT => self.zoom.get_limit(&AxisSense::Neg),
}
}
......@@ -68,13 +94,16 @@ impl Position {
Dot(self.pos.0,self.pos.1)
}
fn px_to_bp(&self, px: f64) -> f64 {
px / self.screen_size.0 as f64 * self.zoom.get_screen_in_bp()
}
fn set_limit_min_zoom(&mut self) {
let max_bp = /* maximum "displayed" bp is ... */
self.max_x-self.min_x+1. /* ... available bp on stick ... */
+ (self.min_x_bumper+self.max_x_bumper) /* ... plus x bumpers (in px) ... */
/ self.screen_size.0 as f64 /* ... px->screen ... */
* self.zoom.get_screen_in_bp() /* ... screen->bp... */
;
let max_bp =
self.get_limit_of_edge(&RIGHT) - self.get_limit_of_edge(&LEFT)
+ 1.
+ self.px_to_bp(self.min_x_bumper)
+ self.px_to_bp(self.max_x_bumper);
self.zoom.set_max_bp(max_bp);
}
......@@ -82,8 +111,8 @@ impl Position {
match *which {
LEFT => self.min_x = val,
RIGHT => self.max_x = val,
UP => (),
DOWN => self.max_y = val as i32
DOWN => self.max_y = val as i32,
_ => (),
}
self.set_limit_min_zoom();
self.check_own_limits();
......@@ -99,50 +128,12 @@ impl Position {
self.check_own_limits();
}
fn limit_min_y(&self, pos: &mut Dot<f64,f64>) {
let min_dy = (self.screen_size.1 as f64/2.).max(0.);
pos.1 = pos.1.max(min_dy);
}
fn limit_max_y(&self, pos: &mut Dot<f64,f64>) {
let max_dy = (self.max_y as f64 - self.screen_size.1 as f64/2.).max(0.);
pos.1 = pos.1.min(max_dy);
}
fn limit_min_x(&self, pos: &mut Dot<f64,f64>) {
let min_dx = /* minimum x-coordinate in bp for centre is ... */
/* ... specified min (in bp, wrt left edge) ... */
self.min_x
/* ... moved to centre ... */
+ self.zoom.get_screen_in_bp()/2.
/* ... minus left bumper (in px) ... */
- self.min_x_bumper
/ self.screen_size.0 as f64 /* ... px->screen ... */
* self.zoom.get_screen_in_bp() /* ... screen->bp */
;
pos.0 = pos.0.max(min_dx);
}
fn limit_max_x(&self, pos: &mut Dot<f64,f64>) {
let max_dx = /* maximum x-coordinate in bp for centre is ... */
/* ... specified max (in bp, wrt right edge) ... */
self.max_x
/* ... moved to centre ... */
- self.zoom.get_screen_in_bp()/2.
/* ... plus right bumper (in px) ... */
+ self.max_x_bumper
/ self.screen_size.0 as f64 /* ... px->screen ... */
* self.zoom.get_screen_in_bp() /* ... screen->bp */
;
pos.0 = pos.0.min(max_dx);
}
fn check_limits(&self, pos: &mut Dot<f64,f64>) {
/* minima always "win" when in conflict => max fn's called first */
self.limit_max_x(pos);
self.limit_min_x(pos);
self.limit_max_y(pos);
self.limit_min_y(pos);
pos.0 = pos.0.min(self.get_limit_of_middle(&RIGHT));
pos.0 = pos.0.max(self.get_limit_of_middle(&LEFT));
pos.1 = pos.1.min(self.get_limit_of_middle(&DOWN));
pos.1 = pos.1.max(self.get_limit_of_middle(&UP));
}
fn check_own_limits(&mut self) {
......
......@@ -3,7 +3,10 @@ use std::collections::HashMap;
use composit::{ Leaf, Position, Wrapping };
use controller::output::Report;
use program::UniformValue;
use types::{CPixel, cpixel, Move, Dot, Direction, LEFT, RIGHT };
use types::{
CPixel, cpixel, Move, Dot, Direction,
LEFT, RIGHT, UP, DOWN, IN, OUT
};
// XXX TODO avoid big-minus-big type calculations which accumulate error
......@@ -18,18 +21,28 @@ pub struct Stage {
impl Stage {
pub fn new() -> Stage {
let size = cpixel(0,0);
let mut out = Stage {
Stage {
pos: Position::new(Dot(0.,0.),size),
mouse_pos: Dot(0,0),
base: 0.,
dims: size,
};
out
}
}
fn bumped(&self, direction: &Direction) -> bool {
self.pos.get_edge(direction).floor() == self.pos.get_limit_of_edge(direction).floor()
}
pub fn update_report(&self, report: &Report) {
report.set_status("start",&self.pos.get_edge(&LEFT).floor().to_string());
report.set_status("end",&self.pos.get_edge(&RIGHT).ceil().to_string());
let (left,right) = (self.pos.get_edge(&LEFT),self.pos.get_edge(&RIGHT));
report.set_status("start",&left.floor().to_string());
report.set_status("end",&right.ceil().to_string());
report.set_status_bool("bumper-left",self.bumped(&LEFT));
report.set_status_bool("bumper-right",self.bumped(&RIGHT));
report.set_status_bool("bumper-top",self.bumped(&UP));
report.set_status_bool("bumper-bottom",self.bumped(&DOWN));
report.set_status_bool("bumper-in",self.bumped(&IN));
report.set_status_bool("bumper-out",self.bumped(&OUT));
}
pub fn set_wrapping(&mut self, w: &Wrapping) {
......
use std::fmt;
use types::AxisSense;
#[derive(Clone,Copy)]
pub struct Zoom {
zoom: f64,
......@@ -21,20 +23,19 @@ impl Zoom {
self.max_bp = bp;
}
fn check_min_limit(&self, val: f64) -> f64 {
if val < -self.max_bp.log10() {
return -self.max_bp.log10();
} else {
return val;
pub fn get_limit(&self, min_max: &AxisSense) -> f64 {
match *min_max {
AxisSense::Neg => -self.max_bp.log10(),
AxisSense::Pos => -MAX_LIMIT_BP.log10()
}
}
fn check_min_limit(&self, val: f64) -> f64 {
val.max(self.get_limit(&AxisSense::Neg))
}
fn check_max_limit(&self, val: f64) -> f64 {
if val > -MAX_LIMIT_BP.log10() {
return -MAX_LIMIT_BP.log10();
} else {
return val;
}
val.min(self.get_limit(&AxisSense::Pos))
}
pub fn set_zoom(&mut self, val: f64) {
......
use std::sync::{ Arc, Mutex };
use stdweb::web::{ Element, HtmlElement };
use stdweb::web::HtmlElement;
use stdweb::unstable::TryInto;
use serde_json::Value as JSONValue;
use composit::{ Compositor, StateManager, Stage };
use controller::input::{ Event, events_run, startup_events };
......@@ -43,7 +44,11 @@ impl App {
out.run_events(&startup_events());
out
}
pub fn send_report(&self, value: &JSONValue) {
domutil::send_custom_event(&self.browser_el.clone().into(),"bpane-out",value);
}
pub fn get_report(&self) -> &Report { &self.report.as_ref().unwrap() }
pub fn set_report(&mut self, report: Report) {
......
use std::sync::{ Arc, Mutex, Weak };
use stdweb::web::{ Element, HtmlElement };
use stdweb::web::HtmlElement;
use composit::{ register_compositor_ticks };
use controller::global::{ App, GlobalWeak };
......@@ -104,7 +104,7 @@ impl AppRunner {
/* register canvas-bound events */
{
let mut el = self.0.lock().unwrap().el.clone();
let el = self.0.lock().unwrap().el.clone();
register_user_events(self,&el);
register_direct_events(self,&el);
register_dom_events(self,&el);
......
......@@ -13,9 +13,9 @@ use composit::{
};
use controller::input::{ register_startup_events, initial_events, events_run };
use controller::global::AppRunner;
use debug::{ DebugComponentSource, DebugBling, create_interactors };
use debug::{ DebugComponentSource, DebugBling, MiniBling, create_interactors };
use debug::debug_stick_manager;
use dom::{ domutil, NoBling, Bling };
use dom::{ domutil, Bling };
pub struct GlobalImpl {
apps: HashMap<String,AppRunner>,
......@@ -94,7 +94,7 @@ impl Global {
let bling : Box<Bling> = if debug {
Box::new(DebugBling::new(create_interactors()))
} else {
Box::new(NoBling::new())
Box::new(MiniBling::new())
};
let ar = AppRunner::new(&GlobalWeak::new(&self),&el,bling);
{
......
......@@ -29,6 +29,7 @@ fn exe_move_event(app: &App, v: Move<f64,f64>) {
let v = match v.direction().0 {
Axis::Horiz => v.convert(Units::Bases,s),
Axis::Vert => v.convert(Units::Pixels,s),
Axis::Zoom => v // TODO invalid pre-unification
};
s.inc_pos(&v);
s.get_pos_middle()
......
......@@ -2,12 +2,12 @@ use std::sync::{ Arc, Mutex };
use stdweb::unstable::TryInto;
use composit::StateValue;
use controller::global::Global;
use controller::input::Event;
use dom::domutil;
use dom::event::{ EventListener, EventType, EventData, EventControl, Target };
use dom::AppEventData;
use types::Dot;
pub struct StartupEventListener {
......@@ -52,6 +52,10 @@ pub fn register_startup_events(g: &Arc<Mutex<Global>>) {
pub fn initial_events() -> Vec<Event> {
vec! {
Event::AddComponent("internal:debug-main".to_string()),
Event::AddComponent("internal:debug-odd".to_string()),
Event::AddComponent("internal:debug-even".to_string()),
Event::SetState("even".to_string(),StateValue::On()),
Event::SetState("odd".to_string(),StateValue::On()),
Event::SetStick("polar".to_string()),
Event::Pos(Dot(0_f64,0_f64),None),
Event::ZoomTo(-5.)
......
......@@ -3,14 +3,41 @@ use std::sync::{ Arc, Mutex };
use controller::global::{ App, AppRunner };
struct Status {
value: String,
last_value: Option<String>,
use serde_json::Map as JSONMap;
use serde_json::Value as JSONValue;
use serde_json::Number as JSONNumber;
#[derive(Clone)]
pub enum StatusJigsawType {
Number,
String,
Boolean
}
#[derive(Clone)]
pub enum StatusJigsaw {
Atom(String,StatusJigsawType),
Array(Vec<StatusJigsaw>),
Object(HashMap<String,StatusJigsaw>)
}
struct StatusOutput {
last_value: Option<JSONValue>,
jigsaw: StatusJigsaw,
last_sent: Option<f64>,
send_every: Option<f64>
send_every: Option<f64>
}
impl Status {
impl StatusOutput {
fn new(jigsaw: StatusJigsaw) -> StatusOutput {
StatusOutput {
jigsaw,
last_sent: None,
send_every: Some(0.),
last_value: None,
}
}
fn is_send_now(&self, key: &str, t: f64) -> bool {
if let Some(interval) = self.send_every {
if interval == 0. { return true; }
......@@ -22,78 +49,141 @@ impl Status {
}
return false;
}
fn send_now(&mut self, key: &str, t: f64) -> bool {
if let Some(ref last_value) = self.last_value {
if *last_value == self.value { return false; }
}
if self.is_send_now(key,t) {
self.last_sent = Some(t);
self.last_value = Some(self.value.clone());
return true;
}
return false;
}
fn set_interval(&mut self, interval: Option<f64>) {
self.send_every = interval;
}
}
const REPORT_CONFIG: &[(&str,Option<f64>)] = &[
("stick",Some(500.)),
("start",Some(500.)),
("end",Some(500.)),
];
lazy_static! {
static ref REPORT_CONFIG:
Vec<(&'static str,StatusJigsaw,Option<f64>)> = vec!{
("location",StatusJigsaw::Array(vec!{
StatusJigsaw::Atom("stick".to_string(),StatusJigsawType::String),
StatusJigsaw::Atom("start".to_string(),StatusJigsawType::Number),
StatusJigsaw::Atom("end".to_string(),StatusJigsawType::Number),
}),Some(500.)),
("bumper",StatusJigsaw::Array(vec!{
StatusJigsaw::Atom("bumper-top".to_string(),StatusJigsawType::Boolean),
StatusJigsaw::Atom("bumper-right".to_string(),StatusJigsawType::Boolean),
StatusJigsaw::Atom("bumper-bottom".to_string(),StatusJigsawType::Boolean),
StatusJigsaw::Atom("bumper-left".to_string(),StatusJigsawType::Boolean),
StatusJigsaw::Atom("bumper-out".to_string(),StatusJigsawType::Boolean),
StatusJigsaw::Atom("bumper-in".to_string(),StatusJigsawType::Boolean),
}),Some(500.))
};
}
pub struct ReportImpl {
statuses: HashMap<String,Status>
pieces: HashMap<String,String>,
outputs: HashMap<String,StatusOutput>
}
impl ReportImpl {
pub fn new() -> ReportImpl {
let out = ReportImpl {
statuses: HashMap::<String,Status>::new()
pieces: HashMap::<String,String>::new(),
outputs: HashMap::<String,StatusOutput>::new()
};
out
}
fn get_entry(&mut self, key: &str) -> &mut Status {
self.statuses.entry(key.to_string()).or_insert_with(||
Status {
value: "".to_string(),
last_value: None,
last_sent: None,
send_every: Some(0.)
}
)
fn get_piece(&mut self, key: &str) -> Option<String> {
self.pieces.get(key).map(|s| s.clone())
}
fn get_output(&mut self, key: &str) -> Option<&mut StatusOutput> {
self.outputs.get_mut(key)
}
fn add_output(&mut self, key: &str, jigsaw: StatusJigsaw) {
self.outputs.insert(key.to_string(),StatusOutput::new(jigsaw));
}
pub fn set_status(&mut self, key: &str, value: &str) {
let s = self.get_entry(key);
if s.value == value { return; }
s.value = value.to_string();
self.pieces.insert(key.to_string(),value.to_string());
}
pub fn set_interval(&mut self, key: &str, interval: Option<f64>) {
let s = self.get_entry(key);
s.set_interval(interval);
if let Some(ref mut p) = self.get_output(key) {
p.send_every = interval;
}
}
fn make_number(&self, data: &str) -> JSONValue {
data.parse::<f64>()
.map(|v| JSONValue::Number(JSONNumber::from_f64(v).unwrap()))
.unwrap_or(JSONValue::Null)
}
fn make_bool(&self, data: &str) -> JSONValue {
data.parse::<bool>()
.map(|v| JSONValue::Bool(v))
.unwrap_or(JSONValue::Null)
}
fn new_report(&mut self, t: f64) -> HashMap<String,String> {
let mut out = HashMap::<String,String>::new();
for (k,s) in &mut self.statuses {
if s.send_now(k,t) {
out.insert(k.to_string(),s.value.clone());
fn make_atom(&self, key: &str, type_: &StatusJigsawType) -> JSONValue {
let v = self.pieces.get(key);
if let Some(ref v) = v {
match type_ {
StatusJigsawType::Number => self.make_number(v),
StatusJigsawType::String => JSONValue::String(v.to_string()),
StatusJigsawType::Boolean => self.make_bool(v)
}
} else {
JSONValue::Null
}
out
}
pub fn tick(&mut self, app: &App, t: f64) {
let out = self.new_report(t);
fn make_array(&self, values: &Vec<StatusJigsaw>) -> JSONValue {
let mut out = Vec::<JSONValue>::new();
for v in values {
out.push(self.make_value(v));
}
JSONValue::Array(out)
}
fn make_object(&self, values: &HashMap<String,StatusJigsaw>) -> JSONValue {
let mut out = JSONMap::<String,JSONValue>::new();
for (k,v) in values {
out.insert(k.to_string(),self.make_value(v));
}
JSONValue::Object(out)
}
fn make_value(&self, j: &StatusJigsaw) -> JSONValue {
match j {
StatusJigsaw::Atom(key,type_) => self.make_atom(key,type_),
StatusJigsaw::Array(values) => self.make_array(values),
StatusJigsaw::Object(values) => self.make_object(values)
}
}
fn new_report(&mut self, t: f64) -> Option<JSONValue> {
let mut out = JSONMap::<String,JSONValue>::new();
for (k,s) in &self.outputs {
let value = self.make_value(&s.jigsaw);
if let Some(ref last_value) = s.last_value {
if last_value == &value { continue; }
}
if s.is_send_now(k,t) {
out.insert(k.to_string(),value.clone());
}
}
for (k,v) in &out {
if let Some(ref mut p) = self.outputs.get_mut(k) {
p.last_value = Some(v.clone());
p.last_sent = Some(t);
}
}
if out.len() > 0 {
debug!("status","{:?}",out);
Some(JSONValue::Object(out))
} else {
None
}
}
pub fn tick(&mut self, app: &App, t: f64) {
if let Some(out) = self.new_report(t) {
app.send_report(&out);
debug!("status","{}",out.to_string());
}
}
}
......@@ -104,7 +194,11 @@ pub struct Report(Arc<Mutex<ReportImpl>>);
impl Report {
pub fn new(ar: &mut AppRunner) -> Report {
let mut out = Report(Arc::new(Mutex::new(ReportImpl::new())));
for (k,v) in REPORT_CONFIG {
for (k,j,v) in REPORT_CONFIG.iter() {
{
let mut imp = out.0.lock().unwrap();
imp.add_output(k,j.clone());
}
out.set_interval(k,*v);
}
ar.add_timer(enclose! { (out) move |app,t| {
......@@ -117,6 +211,10 @@ impl Report {
let mut imp = self.0.lock().unwrap();
imp.set_status(key,value);
}
pub fn set_status_bool(&self, key: &str, value: bool) {
self.set_status(key,&value.to_string());
}