Skip to content

Commit 8b10487

Browse files
committed
merge the 3d feature
1 parent af14084 commit 8b10487

File tree

10 files changed

+111
-54
lines changed

10 files changed

+111
-54
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ This is the next major release of Plotters, see [release notes](./RELEASE-NOTES.
1010
- Linspace coordinate type, which allows define a discrete coordinate on continous value types (such as f32, DateTime, etc) with a step specification
1111
- Nested coordinate type, now we support define pair of category and nested values as nested coordinate.
1212
- Introduce backend crates: plotters-bitmap, plotters-svg, plotters-cairo, plotters-canvas, plotters-console
13+
- 3D Plotting features
14+
1315
### Fixed
1416

1517
- Adjust Canvas backend size with DPR (Thanks to Marius-Mueller)

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ To view the source code for each example, please click on the example image.
117117
<img src="https://plotters-rs.github.io/plotters-doc-data/boxplot.svg" class="galleryItem" width=200px></img>
118118
</a>
119119

120+
<a href="https://github.com/38/plotters/blob/master/examples/3d-plot.rs">
121+
<img src="https://plotters-rs.github.io/plotters-doc-data/3d-plot.svg" class="galleryItem" width=200px></img>
122+
</a>
123+
120124

121125
## Table of Contents
122126
* [Gallery](#gallery)
@@ -416,7 +420,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
416420
.x_label_area_size(20)
417421
.y_label_area_size(40)
418422
// Finally attach a coordinate on the drawing area and make a chart context
419-
.build_ranged(0f32..10f32, 0f32..10f32)?;
423+
.build_cartesian_2d(0f32..10f32, 0f32..10f32)?;
420424

421425
// Then we can draw a mesh
422426
chart

RELEASE-NOTES.md

+1
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,6 @@ Plotters 0.3 ships with tons of improvement made in the coordinate abstraction a
7777
* Linspace coordinate, which can be used converting a continous coorindate into a discrete buckets
7878
* Nested coordinate
7979
* Slices can now be used as cooradnite specification, which allows people define an axis of category.
80+
* 3D Coordinate is now supported
8081

8182
## Fix bugs and improve testing

doc-template/examples/chart.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
1111
.x_label_area_size(20)
1212
.y_label_area_size(40)
1313
// Finally attach a coordinate on the drawing area and make a chart context
14-
.build_ranged(0f32..10f32, 0f32..10f32)?;
14+
.build_cartesian_2d(0f32..10f32, 0f32..10f32)?;
1515

1616
// Then we can draw a mesh
1717
chart

doc-template/readme/gallery

+4
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,7 @@ To view the source code for each example, please click on the example image.
8888
<a href="https://github.com/38/plotters/blob/master/examples/boxplot.rs">
8989
<img src="https://plotters-rs.github.io/plotters-doc-data/boxplot.svg" class="galleryItem" width=200px></img>
9090
</a>
91+
92+
<a href="https://github.com/38/plotters/blob/master/examples/3d-plot.rs">
93+
<img src="https://plotters-rs.github.io/plotters-doc-data/3d-plot.svg" class="galleryItem" width=200px></img>
94+
</a>

examples/chart.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
1616
.margin(5)
1717
.set_all_label_area_size(50)
1818
.caption("Sine and Cosine", ("sans-serif", 40).into_font())
19-
.build_cartesian_2d(x_axis.clone(), -1.2f32..1.2f32)?;
19+
.build_cartesian_2d(-3.4f32..3.4, -1.2f32..1.2f32)?;
2020

2121
cc.configure_mesh()
2222
.x_labels(20)

plotters-doc-data

src/coord/ranged1d/types/numeric.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,12 @@ make_numeric_coord!(
217217
impl_reverse_mapping_trait!(f32, RangedCoordf32);
218218
impl ValueFormatter<f32> for RangedCoordf32 {
219219
fn format(value: &f32) -> String {
220-
crate::data::float::pretty_print_float(*value as f64, false)
220+
crate::data::float::FloatPrettyPrinter {
221+
allow_scientific: false,
222+
min_decimal: 1,
223+
max_decimal: 5,
224+
}
225+
.print(*value as f64)
221226
}
222227
}
223228
make_numeric_coord!(
@@ -230,7 +235,12 @@ make_numeric_coord!(
230235
impl_reverse_mapping_trait!(f64, RangedCoordf64);
231236
impl ValueFormatter<f64> for RangedCoordf64 {
232237
fn format(value: &f64) -> String {
233-
crate::data::float::pretty_print_float(*value, false)
238+
crate::data::float::FloatPrettyPrinter {
239+
allow_scientific: false,
240+
min_decimal: 1,
241+
max_decimal: 5,
242+
}
243+
.print(*value)
234244
}
235245
}
236246
make_numeric_coord!(

src/data/float.rs

+83-47
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,91 @@ fn find_minimal_repr(n: f64, eps: f64) -> (f64, usize) {
1313
}
1414
}
1515

16-
fn float_to_string(n: f64, max_precision: usize) -> String {
17-
let (sign, n) = if n < 0.0 { ("-", -n) } else { ("", n) };
18-
let int_part = n.floor();
16+
fn float_to_string(n: f64, max_precision: usize, min_decimal: usize) -> String {
17+
let (mut result, mut count) = loop {
18+
let (sign, n) = if n < 0.0 { ("-", -n) } else { ("", n) };
19+
let int_part = n.floor();
1920

20-
let dec_part =
21-
((n.abs() - int_part.abs()) * (10.0f64).powf(max_precision as f64)).round() as u64;
21+
let dec_part =
22+
((n.abs() - int_part.abs()) * (10.0f64).powi(max_precision as i32)).round() as u64;
2223

23-
if dec_part == 0 || max_precision == 0 {
24-
return format!("{}{:.0}", sign, int_part);
25-
}
24+
if dec_part == 0 || max_precision == 0 {
25+
break (format!("{}{:.0}", sign, int_part), 0);
26+
}
2627

27-
let mut leading = "".to_string();
28-
let mut dec_result = format!("{}", dec_part);
28+
let mut leading = "".to_string();
29+
let mut dec_result = format!("{}", dec_part);
2930

30-
for _ in 0..(max_precision - dec_result.len()) {
31-
leading.push('0');
32-
}
31+
for _ in 0..(max_precision - dec_result.len()) {
32+
leading.push('0');
33+
}
3334

34-
while let Some(c) = dec_result.pop() {
35-
if c != '0' {
36-
dec_result.push(c);
37-
break;
35+
while let Some(c) = dec_result.pop() {
36+
if c != '0' {
37+
dec_result.push(c);
38+
break;
39+
}
3840
}
41+
42+
break (
43+
format!("{}{:.0}.{}{}", sign, int_part, leading, dec_result),
44+
leading.len() + dec_result.len(),
45+
);
46+
};
47+
48+
if count == 0 && min_decimal > 0 {
49+
result.push('.');
50+
}
51+
52+
while count < min_decimal {
53+
result.push('0');
54+
count += 1;
3955
}
56+
result
57+
}
58+
59+
pub struct FloatPrettyPrinter {
60+
pub allow_scientific: bool,
61+
pub min_decimal: i32,
62+
pub max_decimal: i32,
63+
}
64+
65+
impl FloatPrettyPrinter {
66+
pub fn print(&self, n: f64) -> String {
67+
let (n, p) = find_minimal_repr(n, (10f64).powi(-self.max_decimal));
68+
let d_repr = float_to_string(n, p, self.min_decimal as usize);
69+
if !self.allow_scientific {
70+
d_repr
71+
} else {
72+
if n == 0.0 {
73+
return "0".to_string();
74+
}
75+
76+
let mut idx = n.abs().log10().floor();
77+
let mut exp = (10.0f64).powf(idx);
4078

41-
format!("{}{:.0}.{}{}", sign, int_part, leading, dec_result)
79+
if n.abs() / exp + 1e-5 >= 10.0 {
80+
idx += 1.0;
81+
exp *= 10.0;
82+
}
83+
84+
if idx.abs() < 3.0 {
85+
return d_repr;
86+
}
87+
88+
let (sn, sp) = find_minimal_repr(n / exp, 1e-5);
89+
let s_repr = format!(
90+
"{}e{}",
91+
float_to_string(sn, sp, self.min_decimal as usize),
92+
float_to_string(idx, 0, 0)
93+
);
94+
if s_repr.len() + 1 < d_repr.len() {
95+
s_repr
96+
} else {
97+
d_repr
98+
}
99+
}
100+
}
42101
}
43102

44103
/// The function that pretty prints the floating number
@@ -50,35 +109,12 @@ fn float_to_string(n: f64, max_precision: usize) -> String {
50109
/// - `allow_sn`: Should we use scientific notation when possible
51110
/// - **returns**: The pretty printed string
52111
pub fn pretty_print_float(n: f64, allow_sn: bool) -> String {
53-
let (n, p) = find_minimal_repr(n, 1e-10);
54-
let d_repr = float_to_string(n, p);
55-
if !allow_sn {
56-
d_repr
57-
} else {
58-
if n == 0.0 {
59-
return "0".to_string();
60-
}
61-
62-
let mut idx = n.abs().log10().floor();
63-
let mut exp = (10.0f64).powf(idx);
64-
65-
if n.abs() / exp + 1e-5 >= 10.0 {
66-
idx += 1.0;
67-
exp *= 10.0;
68-
}
69-
70-
if idx.abs() < 3.0 {
71-
return d_repr;
72-
}
73-
74-
let (sn, sp) = find_minimal_repr(n / exp, 1e-5);
75-
let s_repr = format!("{}e{}", float_to_string(sn, sp), float_to_string(idx, 0));
76-
if s_repr.len() + 1 < d_repr.len() {
77-
s_repr
78-
} else {
79-
d_repr
80-
}
81-
}
112+
(FloatPrettyPrinter {
113+
allow_scientific: allow_sn,
114+
min_decimal: 0,
115+
max_decimal: 10,
116+
})
117+
.print(n)
82118
}
83119

84120
#[cfg(test)]

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
537537
.x_label_area_size(20)
538538
.y_label_area_size(40)
539539
// Finally attach a coordinate on the drawing area and make a chart context
540-
.build_ranged(0f32..10f32, 0f32..10f32)?;
540+
.build_cartesian_2d(0f32..10f32, 0f32..10f32)?;
541541
542542
// Then we can draw a mesh
543543
chart

0 commit comments

Comments
 (0)