-
Notifications
You must be signed in to change notification settings - Fork 281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inset feature #50
Inset feature #50
Changes from all commits
76da9a0
75447e4
00c57c8
3ae10fb
bcce66e
38c5283
ecdf96c
036bc76
f946699
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,8 @@ pub enum LabelAreaPosition { | |
/// With the hlep of this object, we can convert a basic drawing area into a chart context, which | ||
/// allows the high-level chartting API beening used on the drawing area. | ||
pub struct ChartBuilder<'a, 'b, DB: DrawingBackend> { | ||
label_area_size: [u32; 4], // [upper, lower, left, right] | ||
label_area_size: [i32; 4], // [upper, lower, left, right] | ||
label_area_inset: [bool; 4], | ||
root_area: &'a DrawingArea<DB, Shift>, | ||
title: Option<(String, TextStyle<'b>)>, | ||
margin: [u32; 4], | ||
|
@@ -31,6 +32,7 @@ impl<'a, 'b, DB: DrawingBackend> ChartBuilder<'a, 'b, DB> { | |
pub fn on(root: &'a DrawingArea<DB, Shift>) -> Self { | ||
Self { | ||
label_area_size: [0; 4], | ||
label_area_inset: [false; 4], | ||
root_area: root, | ||
title: None, | ||
margin: [0; 4], | ||
|
@@ -74,36 +76,56 @@ impl<'a, 'b, DB: DrawingBackend> ChartBuilder<'a, 'b, DB> { | |
|
||
/// Set the size of X label area | ||
/// - `size`: The height of the x label area, if x is 0, the chart doesn't have the X label area | ||
pub fn x_label_area_size(&mut self, size: u32) -> &mut Self { | ||
pub fn x_label_area_size(&mut self, size: i32) -> &mut Self { | ||
self.label_area_size[1] = size; | ||
self | ||
} | ||
|
||
pub fn inset_x_labels(&mut self) -> &mut Self { | ||
self.label_area_inset[1] = true; | ||
self | ||
} | ||
|
||
/// Set the size of the Y label area | ||
/// - `size`: The width of the Y label area. If size is 0, the chart doesn't have Y label area | ||
pub fn y_label_area_size(&mut self, size: u32) -> &mut Self { | ||
pub fn y_label_area_size(&mut self, size: i32) -> &mut Self { | ||
self.label_area_size[2] = size; | ||
self | ||
} | ||
|
||
pub fn inset_y_labels(&mut self) -> &mut Self { | ||
self.label_area_inset[2] = true; | ||
self | ||
} | ||
|
||
/// Set the size of X label area on the top of the chart | ||
/// - `size`: The height of the x label area, if x is 0, the chart doesn't have the X label area | ||
pub fn top_x_label_area_size(&mut self, size: u32) -> &mut Self { | ||
pub fn top_x_label_area_size(&mut self, size: i32) -> &mut Self { | ||
self.label_area_size[0] = size; | ||
self | ||
} | ||
|
||
pub fn inset_top_x_labels(&mut self) -> &mut Self { | ||
self.label_area_inset[0] = true; | ||
self | ||
} | ||
|
||
/// Set the size of the Y label area on the right side | ||
/// - `size`: The width of the Y label area. If size is 0, the chart doesn't have Y label area | ||
pub fn right_y_label_area_size(&mut self, size: u32) -> &mut Self { | ||
pub fn right_y_label_area_size(&mut self, size: i32) -> &mut Self { | ||
self.label_area_size[3] = size; | ||
self | ||
} | ||
|
||
pub fn inset_right_y_labels(&mut self) -> &mut Self { | ||
self.label_area_inset[3] = true; | ||
self | ||
} | ||
|
||
/// Set a label area size | ||
/// - `pos`: THe position where the label area locted | ||
/// - `size`: The size of the label area size | ||
pub fn set_label_area_size(&mut self, pos: LabelAreaPosition, size: u32) -> &mut Self { | ||
pub fn set_label_area_size(&mut self, pos: LabelAreaPosition, size: i32) -> &mut Self { | ||
self.label_area_size[pos as usize] = size; | ||
self | ||
} | ||
|
@@ -157,10 +179,9 @@ impl<'a, 'b, DB: DrawingBackend> ChartBuilder<'a, 'b, DB> { | |
let mut actual_drawing_area_pos = [0, h as i32, 0, w as i32]; | ||
|
||
for (idx, (dx, dy)) in (0..4).map(|idx| (idx, [(0, -1), (0, 1), (-1, 0), (1, 0)][idx])) { | ||
let size = self.label_area_size[idx] as i32; | ||
|
||
let split_point = if dx + dy < 0 { size } else { -size }; | ||
|
||
//let size = if self.label_area_size[idx] <= 0 { 0 } else { self.label_area_size[idx] }; | ||
let size = self.label_area_size[idx]; | ||
let split_point = if !self.label_area_inset[idx] { if dx + dy < 0 { size } else { -size } } else { 0 }; | ||
actual_drawing_area_pos[idx] += split_point; | ||
} | ||
|
||
|
@@ -180,7 +201,20 @@ impl<'a, 'b, DB: DrawingBackend> ChartBuilder<'a, 'b, DB> { | |
} | ||
} | ||
|
||
std::mem::swap(&mut drawing_area, splitted[4].as_mut().unwrap()); | ||
for (id, (_, size)) in self.label_area_inset.iter().zip(self.label_area_size.iter()).enumerate().filter(|(_,(inset, size))| **inset && **size != 0) { | ||
let area = splitted[4].as_ref().unwrap(); | ||
let (w, h) = area.dim_in_pixel(); | ||
let mut new_area = match id { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I am planning to do is uses the We can first clone the parent drawing area and then shrink it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, we can still use the split, but we can actually discard the one in the middle and use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Feel free to copy, change any part. Its important for me that it do the trick.
As i mentoined earlier negative size is useful itself without labels being inset. |
||
0 => area.clone().alter_new((None,None),(None,Some(*size))), | ||
2 => area.clone().alter_new((None,None),(Some(*size),None)), | ||
1 => area.clone().alter_new((None,Some(h as i32 - *size)),(None,Some(h as i32 - *size))), | ||
3 => area.clone().alter_new((Some(w as i32 - *size),None),(None,None)), | ||
_ => unreachable!(), | ||
}.make_inset(); | ||
std::mem::swap(&mut label_areas[id], &mut Some(new_area)); | ||
} | ||
|
||
std::mem::swap(&mut drawing_area, &mut splitted[4].as_mut().unwrap()); | ||
|
||
let mut pixel_range = drawing_area.get_pixel_range(); | ||
pixel_range.1 = pixel_range.1.end..pixel_range.1.start; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -268,18 +268,25 @@ impl<'a, DB: DrawingBackend, X: Ranged, Y: Ranged> ChartContext<'a, DB, RangedCo | |
} | ||
|
||
if let Some(style) = axis_style { | ||
let mut x0 = if orientation.0 > 0 { 0 } else { tw as i32 }; | ||
let mut y0 = if orientation.1 > 0 { 0 } else { th as i32 }; | ||
let mut x0 = if orientation.0 > 0 { 0 } else { tw as i32 }; | ||
let mut y0 = if orientation.1 > 0 { 0 } else { th as i32 }; | ||
let mut x1 = if orientation.0 >= 0 { 0 } else { tw as i32 }; | ||
let mut y1 = if orientation.1 >= 0 { 0 } else { th as i32 }; | ||
|
||
|
||
if orientation.0 == 0 { | ||
x0 = axis_range.start; | ||
x1 = axis_range.end; | ||
} else { | ||
y0 = axis_range.start; | ||
y1 = axis_range.end; | ||
} | ||
|
||
if area.is_inset() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just little bit worry about the logic coupling. Since the drawing area is originally designed not to include too many details about chart. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since my design needs to be implemented at builder stage it seemed logical to set inset field in |
||
// FIXME: looks like truncate func works strange with right side | ||
if x0 == x1 { if x0 > 0 { x0=1; x1=1; } else { x0 = 10_000; x1 = 10_000; } }; | ||
if y0 == y1 { if y0 > 0 { y0=1; y1=1; } else { y0 = 10_000; y1 = 10_000; } }; | ||
} | ||
area.draw(&Path::new(vec![(x0, y0), (x1, y1)], style.clone()))?; | ||
} | ||
|
||
|
@@ -313,20 +320,20 @@ impl<'a, DB: DrawingBackend, X: Ranged, Y: Ranged> ChartContext<'a, DB, RangedCo | |
.estimate_text_size(&t, &label_style.font) | ||
.unwrap_or((0, 0)); | ||
|
||
// TODO: need some adjustments here for inset labels | ||
let (cx, cy) = match orientation { | ||
(dx, dy) if dx > 0 && dy == 0 => (right_most - w as i32, *p - y0), | ||
(dx, dy) if dx < 0 && dy == 0 => (tw as i32 - label_dist - w as i32, *p - y0), | ||
(dx, dy) if dx == 0 && dy > 0 => (*p - x0, label_dist + h as i32), | ||
(dx, dy) if dx == 0 && dy < 0 => (*p - x0, th as i32 - label_dist - h as i32), | ||
_ => panic!("Bug: Invlid orientation specification"), | ||
_ => panic!("Bug: Invalid orientation specification"), | ||
}; | ||
|
||
let should_draw = if orientation.0 == 0 { | ||
cx >= 0 && cx + label_offset + w as i32 / 2 <= tw as i32 | ||
} else { | ||
cy >= 0 && cy + label_offset + h as i32 / 2 <= th as i32 | ||
}; | ||
|
||
if should_draw { | ||
let (text_x, text_y) = if orientation.0 == 0 { | ||
(cx - w as i32 / 2 + label_offset, cy) | ||
|
@@ -335,17 +342,25 @@ impl<'a, DB: DrawingBackend, X: Ranged, Y: Ranged> ChartContext<'a, DB, RangedCo | |
}; | ||
|
||
area.draw_text(&t, label_style, (text_x, text_y))?; | ||
|
||
// TODO: need some adjustments here | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I am thinking about do the both thing:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part of code i was most confused here i need just mirror all Path Lines coordinates. |
||
if let Some(style) = axis_style { | ||
let (kx0, ky0, kx1, ky1) = match orientation { | ||
(dx, dy) if dx > 0 && dy == 0 => (0, *p - y0, tick_size, *p - y0), | ||
(dx, dy) if dx < 0 && dy == 0 => { | ||
// --- | ||
(dx, dy) if dx > 0 && dy == 0 => if area.is_inset() { | ||
(tw as i32 - tick_size, *p - y0, tw as i32, *p - y0) | ||
} | ||
(dx, dy) if dx == 0 && dy > 0 => (*p - x0, 0, *p - x0, tick_size), | ||
(dx, dy) if dx == 0 && dy < 0 => { | ||
} else{ (0, *p - y0, tick_size, *p - y0) }, | ||
// --- left labels | ||
(dx, dy) if dx < 0 && dy == 0 => if area.is_inset() { | ||
(0, *p - y0, tick_size, *p - y0) | ||
} else {(tw as i32 - tick_size, *p - y0, tw as i32, *p - y0) }, | ||
// --- | ||
(dx, dy) if dx == 0 && dy > 0 => if area.is_inset() { | ||
(*p - x0, th as i32 - tick_size, *p - x0, th as i32) | ||
} | ||
} else { (*p - x0, 0, *p - x0, tick_size) }, | ||
// --- | ||
(dx, dy) if dx == 0 && dy < 0 => if area.is_inset() { | ||
(*p - x0, 0, *p - x0, tick_size) | ||
} else { (*p - x0, th as i32 - tick_size, *p - x0, th as i32) }, | ||
_ => panic!("Bug: Invlid orientation specification"), | ||
}; | ||
let line = Path::new(vec![(kx0, ky0), (kx1, ky1)], style.clone()); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,7 @@ struct Rect { | |
} | ||
|
||
impl Rect { | ||
/// Split the rectangle into a few smaller rectnagles | ||
/// Split the rectangle into a few smaller rectangles | ||
fn split<'a, BPI: IntoIterator<Item = &'a i32> + 'a>( | ||
&'a self, | ||
break_points: BPI, | ||
|
@@ -106,6 +106,7 @@ pub struct DrawingArea<DB: DrawingBackend, CT: CoordTranslate> { | |
backend: Rc<RefCell<DB>>, | ||
rect: Rect, | ||
coord: CT, | ||
inset: bool, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like I said, it's a little bit coupling between the drawing and the chart context here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good for me, but you are master. Do as you wish. |
||
} | ||
|
||
impl<DB: DrawingBackend, CT: CoordTranslate + Clone> Clone for DrawingArea<DB, CT> { | ||
|
@@ -114,6 +115,7 @@ impl<DB: DrawingBackend, CT: CoordTranslate + Clone> Clone for DrawingArea<DB, C | |
backend: self.copy_backend_ref(), | ||
rect: self.rect.clone(), | ||
coord: self.coord.clone(), | ||
inset: self.inset, | ||
} | ||
} | ||
} | ||
|
@@ -220,6 +222,7 @@ impl<DB: DrawingBackend, CT: CoordTranslate> DrawingArea<DB, CT> { | |
rect: self.rect.clone(), | ||
backend: self.copy_backend_ref(), | ||
coord: Shift((self.rect.x0, self.rect.y0)), | ||
inset: self.inset, | ||
} | ||
} | ||
|
||
|
@@ -331,6 +334,7 @@ impl<DB: DrawingBackend> DrawingArea<DB, Shift> { | |
}, | ||
backend, | ||
coord: Shift((0, 0)), | ||
inset: false, | ||
} | ||
} | ||
|
||
|
@@ -357,6 +361,7 @@ impl<DB: DrawingBackend> DrawingArea<DB, Shift> { | |
rect: self.rect.clone(), | ||
backend: self.copy_backend_ref(), | ||
coord: coord_spec, | ||
inset: self.inset, | ||
} | ||
} | ||
|
||
|
@@ -371,6 +376,7 @@ impl<DB: DrawingBackend> DrawingArea<DB, Shift> { | |
}, | ||
backend: self.copy_backend_ref(), | ||
coord: Shift((self.rect.x0 + left, self.rect.y0 + top)), | ||
inset: self.inset, | ||
} | ||
} | ||
|
||
|
@@ -381,6 +387,7 @@ impl<DB: DrawingBackend> DrawingArea<DB, Shift> { | |
rect: rect.clone(), | ||
backend: self.copy_backend_ref(), | ||
coord: Shift((rect.x0, rect.y0)), | ||
inset: self.inset, | ||
}); | ||
|
||
(ret.next().unwrap(), ret.next().unwrap()) | ||
|
@@ -393,6 +400,7 @@ impl<DB: DrawingBackend> DrawingArea<DB, Shift> { | |
rect: rect.clone(), | ||
backend: self.copy_backend_ref(), | ||
coord: Shift((rect.x0, rect.y0)), | ||
inset: self.inset, | ||
}); | ||
|
||
(ret.next().unwrap(), ret.next().unwrap()) | ||
|
@@ -406,6 +414,7 @@ impl<DB: DrawingBackend> DrawingArea<DB, Shift> { | |
rect: rect.clone(), | ||
backend: self.copy_backend_ref(), | ||
coord: Shift((rect.x0, rect.y0)), | ||
inset: self.inset, | ||
}) | ||
.collect() | ||
} | ||
|
@@ -422,6 +431,7 @@ impl<DB: DrawingBackend> DrawingArea<DB, Shift> { | |
rect: rect.clone(), | ||
backend: self.copy_backend_ref(), | ||
coord: Shift((rect.x0, rect.y0)), | ||
inset: self.inset, | ||
}) | ||
.collect() | ||
} | ||
|
@@ -466,9 +476,49 @@ impl<DB: DrawingBackend> DrawingArea<DB, Shift> { | |
}, | ||
backend: self.copy_backend_ref(), | ||
coord: Shift((self.rect.x0, self.rect.y0 + 10 + text_h as i32)), | ||
inset: self.inset, | ||
}) | ||
} | ||
|
||
/// Alter area bounds | ||
pub fn alter_diff(self, diff_tl: (i32,i32), diff_rb: (i32,i32)) -> Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, show me how :) |
||
Self { | ||
backend: self.backend, | ||
rect: Rect{ | ||
x0: self.rect.x0 + diff_tl.0, | ||
y0: self.rect.y0 + diff_tl.1, | ||
x1: self.rect.x1 + diff_rb.0, | ||
y1: self.rect.y1 + diff_rb.1, | ||
}, | ||
coord: self.coord, | ||
inset: self.inset, | ||
} | ||
} | ||
/// Update area bounds | ||
pub fn alter_new(self, tl: (Option<i32>,Option<i32>), rb: (Option<i32>,Option<i32>)) -> Self { | ||
Self { | ||
backend: self.backend, | ||
rect: Rect{ | ||
x0: tl.0.unwrap_or(self.rect.x0), | ||
y0: tl.1.unwrap_or(self.rect.y0), | ||
x1: rb.0.unwrap_or(self.rect.x1), | ||
y1: rb.1.unwrap_or(self.rect.y1), | ||
}, | ||
coord: self.coord, | ||
inset: self.inset, | ||
} | ||
} | ||
/// Make area inset | ||
pub fn make_inset(self) -> Self { | ||
Self { | ||
backend: self.backend, | ||
rect: self.rect, | ||
coord: self.coord, | ||
inset: true, | ||
} | ||
} | ||
/// Get area's inset value | ||
pub fn is_inset(&self) -> bool { self.inset } | ||
/// Draw text on the drawing area | ||
pub fn draw_text( | ||
&self, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,8 @@ pub trait BackendStyle { | |
fn stroke_width(&self) -> u32 { | ||
1 | ||
} | ||
|
||
fn title(&self) -> &str { "" } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And why there's a title here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems the title is not actually used by any of the backend. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahhh... i have heavy customized Webgl backend, and when dealing with all Shders, Vertexes and Fragments you need to know what kind of lines you are drawing, sometimes you need to pull through additional color to make some other stuff more smooth. So just treat it like helpful stuff. I'm using it heavily. |
||
} | ||
|
||
impl<T: Color> BackendStyle for T { | ||
|
@@ -56,6 +58,7 @@ impl BackendStyle for ShapeStyle { | |
fn stroke_width(&self) -> u32 { | ||
self.stroke_width | ||
} | ||
fn title(&self) -> &'static str { self.title } | ||
} | ||
|
||
/// The drawing backend trait, which implemenets the low-level drawing APIs. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just want to discuss with you. Since what I am planning to do is allow developer give a negative label area size, and it will automatically inverts the direction of the labels. Is there any particular reason for explicitly setting this ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was first option i wanted to implement, but then i realized that negative width is usefult itself. It gives more control over drawing areas. It allows to overlay one drawing area onto one with negative size.