Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions base/src/color.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::str::FromStr;
use std::{error, fmt};

mod named;
mod css4;
mod xkcd;

pub use named::*;
pub use css4::*;

pub trait ResolveColor<Color> {
fn resolve_color(&self, color: &Color) -> ColorU8;
Expand Down Expand Up @@ -201,6 +202,10 @@ const fn hex_to_u8(hex: u8) -> u8 {
}
}

const fn is_hex_char(c: u8) -> bool {
matches!(c, b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F')
}

/// Parsing error for ColorU8
#[derive(Debug)]
pub enum ParseError {
Expand Down Expand Up @@ -297,8 +302,11 @@ impl FromStr for ColorU8 {
let bytes = raw.as_bytes();
match bytes.len() {
4 | 5 | 7 | 9 => {
// from_html panics if first char != '#', but we checked it
Ok(ColorU8::from_html(bytes))
if bytes[1..].iter().all(|&c| is_hex_char(c)) {
Ok(ColorU8::from_html(bytes))
} else {
Err(ParseError::InvalidHex)
}
}
_ => Err(ParseError::InvalidHex),
}
Expand Down Expand Up @@ -328,7 +336,9 @@ impl FromStr for ColorU8 {
}
// named color
else {
if let Some(col) = named::lookup_name(raw) {
if let Some(col) = css4::lookup_name(raw) {
Ok(col)
} else if let Some(col) = xkcd::lookup_name(raw) {
Ok(col)
} else {
Err(ParseError::UnknownName)
Expand Down
File renamed without changes.
961 changes: 961 additions & 0 deletions base/src/color/xkcd.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/gauss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fn main() {
let dist_series = des::Series::Line(
des::series::Line::new(x.into(), y.into())
.with_name("distribution")
.with_line(style::series::Stroke {
.with_stroke(style::series::Stroke {
width: 4.0,
..style::Stroke::default()
}),
Expand Down
2 changes: 1 addition & 1 deletion examples/polars_sine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn main() {
let series = des::Series::Line(
des::series::Line::new(des::data_src_ref("x"), des::data_src_ref("y"))
.with_name("y=sin(x)")
.with_line(style::series::Stroke::default().with_width(4.0)),
.with_stroke(style::series::Stroke::default().with_width(4.0)),
);

let plot = des::Plot::new(vec![series])
Expand Down
2 changes: 1 addition & 1 deletion examples/sine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod common;
fn main() {
let fig = des::series::Line::new(des::data_src_ref("x"), des::data_src_ref("y"))
.with_name("y=sin(x)")
.with_line(style::series::Stroke::default().with_width(4.0))
.with_stroke(style::series::Stroke::default().with_width(4.0))
.into_plot()
.with_x_axis(
des::Axis::new()
Expand Down
2 changes: 1 addition & 1 deletion iced/examples/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ fn build_figure() -> des::Figure {
des::DataCol::SrcRef("y".to_string()),
)
.with_name("y=sin(x)".to_string())
.with_line(style::series::Stroke::default().with_width(2.0)),
.with_stroke(style::series::Stroke::default().with_width(2.0)),
);

let plot = des::Plot::new(vec![series])
Expand Down
1 change: 1 addition & 0 deletions run_all_examples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ done
cargo run --example text_line --package plotive-text --features noto-sans
cargo run --example text_rich --package plotive-text --features noto-sans,noto-serif

cargo run --example area -- ${POS_ARGS[@]}
cargo run --example bars -- ${POS_ARGS[@]}
cargo run --example gauss -- ${POS_ARGS[@]}
cargo run --example sine -- ${POS_ARGS[@]}
Expand Down
85 changes: 59 additions & 26 deletions src/des/series.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ pub struct Line {
x_axis: axis::Ref,
y_axis: axis::Ref,
stroke: style::series::Stroke,
marker: Option<style::series::Marker>,
interpolation: Interpolation,
}

Expand All @@ -198,6 +199,7 @@ impl Line {
x_axis: Default::default(),
y_axis: Default::default(),
stroke: style::series::Stroke::default().with_width(defaults::SERIES_LINE_WIDTH),
marker: None,
interpolation: Interpolation::default(),
}
}
Expand Down Expand Up @@ -225,8 +227,14 @@ impl Line {
}

/// Set the line style and return self for chaining
pub fn with_line(mut self, line: style::series::Stroke) -> Self {
self.stroke = line;
pub fn with_stroke(mut self, stroke: style::series::Stroke) -> Self {
self.stroke = stroke;
self
}

/// Set the marker style and return self for chaining
pub fn with_marker(mut self, marker: style::series::Marker) -> Self {
self.marker = Some(marker);
self
}

Expand Down Expand Up @@ -266,6 +274,11 @@ impl Line {
&self.stroke
}

/// Get the marker style, if any
pub fn marker(&self) -> Option<&style::series::Marker> {
self.marker.as_ref()
}

/// Chaining helper to build a plot from this series
/// This can only be used if your plot contains a single series.
/// This is equivalent to `Plot::new(vec![self.into()])`
Expand Down Expand Up @@ -298,6 +311,13 @@ impl Line {
///
/// Plots data as individual scatter points without connecting them.
/// Useful for visualizing correlations, distributions, and discrete data points.
///
/// Optional sizes data column can be used to specify the size of each marker, for bubble charts.
/// If provided, the size field of the marker is ignored and the size of each marker
/// is determined by the corresponding value in the sizes data column.
/// Just like marker size, the size data is interpreted as an area.
/// (diameter = sqrt(size data) for circle marker).
/// The sizes data column must have the same length as the x and y data columns.
#[derive(Debug, Clone)]
pub struct Scatter {
x_data: DataCol,
Expand All @@ -307,6 +327,7 @@ pub struct Scatter {
x_axis: axis::Ref,
y_axis: axis::Ref,
marker: style::series::Marker,
sizes_data: Option<DataCol>,
}

impl Scatter {
Expand All @@ -320,6 +341,7 @@ impl Scatter {
x_axis: Default::default(),
y_axis: Default::default(),
marker: style::series::Marker::default(),
sizes_data: None,
}
}

Expand Down Expand Up @@ -351,6 +373,12 @@ impl Scatter {
self
}

/// Set the sizes data column and return self for chaining
pub fn with_sizes(mut self, sizes_data: DataCol) -> Self {
self.sizes_data = Some(sizes_data);
self
}

/// Get the x data column
pub fn x_data(&self) -> &DataCol {
&self.x_data
Expand Down Expand Up @@ -380,6 +408,11 @@ impl Scatter {
pub fn marker(&self) -> &style::series::Marker {
&self.marker
}

/// Get the sizes data column, if any
pub fn sizes_data(&self) -> Option<&DataCol> {
self.sizes_data.as_ref()
}
}

/// Definition for the `y2_data` field of the Area plot.
Expand Down Expand Up @@ -585,7 +618,7 @@ pub struct Histogram {
x_axis: axis::Ref,
y_axis: axis::Ref,
fill: style::series::Fill,
line: Option<style::series::Stroke>,
stroke: Option<style::series::Stroke>,
bins: u32,
density: bool,
}
Expand All @@ -600,7 +633,7 @@ impl Histogram {
x_axis: Default::default(),
y_axis: Default::default(),
fill: style::series::Fill::default(),
line: None,
stroke: None,
bins: 10,
density: false,
}
Expand Down Expand Up @@ -631,9 +664,9 @@ impl Histogram {
Self { fill, ..self }
}

/// Set the line style for the histogram outline and return self for chaining
pub fn with_line(mut self, line: style::series::Stroke) -> Self {
self.line = Some(line);
/// Set the stroke style for the histogram outline and return self for chaining
pub fn with_outline(mut self, stroke: style::series::Stroke) -> Self {
self.stroke = Some(stroke);
self
}

Expand Down Expand Up @@ -674,9 +707,9 @@ impl Histogram {
&self.fill
}

/// Get the line style, if any
pub fn line(&self) -> Option<&style::series::Stroke> {
self.line.as_ref()
/// Get the outline style, if any
pub fn outline(&self) -> Option<&style::series::Stroke> {
self.stroke.as_ref()
}

/// Get the number of bins
Expand Down Expand Up @@ -726,7 +759,7 @@ pub struct Bars {
x_axis: axis::Ref,
y_axis: axis::Ref,
fill: style::series::Fill,
line: Option<style::series::Stroke>,
stroke: Option<style::series::Stroke>,
position: BarsPosition,
}

Expand All @@ -741,7 +774,7 @@ impl Bars {
x_axis: Default::default(),
y_axis: Default::default(),
fill: style::series::Fill::default(),
line: None,
stroke: None,
position: BarsPosition::default(),
}
}
Expand All @@ -759,10 +792,10 @@ impl Bars {
Self { fill, ..self }
}

/// Set the line style for the bar outline and return self for chaining
pub fn with_line(self, line: style::series::Stroke) -> Self {
/// Set the stroke style for the bar outline and return self for chaining
pub fn with_outline(self, stroke: style::series::Stroke) -> Self {
Self {
line: Some(line),
stroke: Some(stroke),
..self
}
}
Expand Down Expand Up @@ -802,9 +835,9 @@ impl Bars {
&self.fill
}

/// Get the line style, if any
pub fn line(&self) -> Option<&style::series::Stroke> {
self.line.as_ref()
/// Get the outline style, if any
pub fn outline(&self) -> Option<&style::series::Stroke> {
self.stroke.as_ref()
}

/// Get the position configuration
Expand All @@ -823,7 +856,7 @@ pub struct BarSeries {

name: Option<String>,
fill: style::series::Fill,
line: Option<style::series::Stroke>,
stroke: Option<style::series::Stroke>,
}

impl BarSeries {
Expand All @@ -834,7 +867,7 @@ impl BarSeries {

name: None,
fill: style::series::Fill::default(),
line: None,
stroke: None,
}
}

Expand All @@ -851,10 +884,10 @@ impl BarSeries {
Self { fill, ..self }
}

/// Set the line style for the bar outline and return self for chaining
pub fn with_line(self, line: style::series::Stroke) -> Self {
/// Set the stroke style for the bar outline and return self for chaining
pub fn with_outline(self, stroke: style::series::Stroke) -> Self {
Self {
line: Some(line),
stroke: Some(stroke),
..self
}
}
Expand All @@ -874,9 +907,9 @@ impl BarSeries {
&self.fill
}

/// Get the line style, if any
pub fn line(&self) -> Option<&style::series::Stroke> {
self.line.as_ref()
/// Get the outline style, if any
pub fn outline(&self) -> Option<&style::series::Stroke> {
self.stroke.as_ref()
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/drawing/annot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,21 @@ impl Annot {
let (x, y) = marker.position();
let x = x_axis.coord_map().map_coord_num(x);
let y = y_axis.coord_map().map_coord_num(y);
let path = marker::marker_path(marker.marker());
let marker = marker.marker();
let path = marker::marker_path(marker.shape);
let scale = marker.size.to_visual_size();

let transform =
geom::Transform::from_translate(plot_rect.left() + x, plot_rect.bottom() - y);
geom::Transform::from_translate(plot_rect.left() + x, plot_rect.bottom() - y)
.pre_scale(scale, scale);

let rpath = render::Path {
path: &path,
fill: marker.marker().fill.as_ref().map(|f| f.as_paint(style)),
stroke: marker.marker().stroke.as_ref().map(|l| l.as_stroke(style)),
fill: marker.fill.as_ref().map(|f| f.as_paint(style)),
stroke: marker
.stroke
.as_ref()
.map(|l| l.as_stroke(style).with_multiplied_width(1.0 / scale)),
transform: Some(&transform),
};
surface.draw_path(&rpath);
Expand Down
11 changes: 8 additions & 3 deletions src/drawing/legend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,19 @@ impl LegendEntry {
surface.draw_path(&line);
}
Shape::Marker(marker) => {
let path = crate::drawing::marker::marker_path(&marker);
let path = crate::drawing::marker::marker_path(marker.shape);
let scale = style::MarkerSize::default().to_visual_size();
let transform =
geom::Transform::from_translate(shape_rect.center_x(), shape_rect.center_y());
geom::Transform::from_translate(shape_rect.center_x(), shape_rect.center_y())
.pre_scale(scale, scale);

let path = render::Path {
path: &path,
fill: marker.fill.as_ref().map(|f| f.as_paint(&rc)),
stroke: marker.stroke.as_ref().map(|s| s.as_stroke(&rc)),
stroke: marker
.stroke
.as_ref()
.map(|s| s.as_stroke(&rc).with_multiplied_width(1.0 / scale)),
transform: Some(&transform),
};
surface.draw_path(&path);
Expand Down
Loading
Loading