Skip to content
Open
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## [Unreleased]

### Added

- The line, path and segment layers now have an `arrow` setting, which
can be used to display arrowheads at the ends of the lines.

### Changed

- The reader/dialect layer was refactored around a central dialect registry with
Expand Down
36 changes: 25 additions & 11 deletions doc/syntax/layer/type/line.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ The following aesthetics are recognised by the line layer.
* `aggregate` Aggregation functions to apply per group:
* `null` apply no group aggregation (default).
* A single string or an array of strings. See an overview of aggregation function in [the `DRAW` documentation](../../clause/draw.qmd#aggregate) and more information in the *Data transformation* section below.
* `arrow` Arrow names to display at line ends. Broadly, these come in the following variants:
* Arrow heads: `'open'`, `'closed'`, `'stealth'`, `'latex'`, `'thin'` and `'wedge'`.
* Arrow tails: `'fishtail'`, `'fork'`, and `'feather'`.
* Symmetric: `'bar'`, `'bracket'`, `'cross'`, `'dot'`, `'square'` and `'diamond'`.

Arrow names can optionally be followed by a `:`-separated numeric suffix to set their size in points, e.g. `'open:10'`.
By default, arrows are sized by tripling the `linewidth` value.
Arrows can be declared in the following ways:
* `null` (default) do not display an arrow.
* A single string giving an arrow name to use at the end of the line.
* A `[start, end]` array of names or `null` to specify arrows for the start and end of the line.
To only set an arrow at the start, you could use `['open', null]`.


## Data transformation
This layer supports aggregation through the `aggregate` setting. Aggregation groups are defined by `PARTITION BY`, all discrete mappings, but also the primary axis. Within each group, every numeric mapping is replaced in place by its aggregated value. Use a default like `'mean'` or target individual aesthetics with `'<aes>:<func>'`. See [the `DRAW` documentation](../../clause/draw.qmd#aggregate) for the full setting shape.
Expand Down Expand Up @@ -71,11 +84,10 @@ DRAW line
When `stroke` or `opacity` varies, the properties of the preceding datapoint carry over. In the case below, we don't see the blue of the last datapoint.

```{ggsql}
WITH data(x, y, z) AS (VALUES
(1, 2, 1),
(2, 5, 3),
(3, 3, 5),
)
-- Small example dataset
CREATE TEMP TABLE data AS
SELECT * FROM (VALUES (1,2,1), (2,5,3), (3,3,5)) t(x,y,z)

VISUALISE x, y FROM data
DRAW line
MAPPING z AS stroke
Expand All @@ -85,18 +97,20 @@ SCALE stroke TO ('red', 'green', 'blue')
The `linewidth` aesthetic can vary point to point.

```{ggsql}
WITH data(x, y, z) AS (VALUES
(1, 2, 1),
(2, 5, 3),
(3, 3, 5),
)

VISUALISE x, y FROM data
DRAW line
MAPPING z AS linewidth
SCALE linewidth TO (0, 30)
```

Arrows applied to the start and end of the line.

```{ggsql}
VISUALISE x, y FROM data
DRAW line SETTING
arrow => ['dot:20', 'closed:15']
```

Use aggregation to draw min and max lines from a set of observations on a single layer. Targeting `y` twice produces one summary row per function within the same group. A synthetic `aggregate` column tags each row with the different function names, that you can remap to colour the lines distinctly:

```{ggsql}
Expand Down
20 changes: 20 additions & 0 deletions doc/syntax/layer/type/path.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ The following aesthetics are recognised by the path layer.

## Settings
* `position`: Position adjustment. One of `'identity'` (default), `'stack'`, `'dodge'`, or `'jitter'`
* `arrow` Arrow names to display at line ends. Broadly, these come in the following variants:
* Arrow heads: `'open'`, `'closed'`, `'stealth'`, `'latex'`, `'thin'` and `'wedge'`.
* Arrow tails: `'fishtail'`, `'fork'`, and `'feather'`.
* Symmetric: `'bar'`, `'bracket'`, `'cross'`, `'dot'`, `'square'` and `'diamond'`.

Arrow names can optionally be followed by a `:`-separated numeric suffix to set their size in points, e.g. `'open:10'`.
By default, arrows are sized by tripling the `linewidth` value.
Arrows can be declared in the following ways:
* `null` (default) do not display an arrow.
* A single string giving an arrow name to use at the end of the line.
* A `[start, end]` array of names or `null` to specify arrows for the start and end of the line.
To only set an arrow at the start, you could use `['open', null]`.

## Data transformation
The path layer does not transform its data but passes it through unchanged.
Expand Down Expand Up @@ -90,6 +102,14 @@ DRAW path
MAPPING 'Path' AS stroke
```

Setting an arrow.

```{ggsql}
VISUALISE x, y FROM df
DRAW path SETTING
arrow => 'closed:15'
```

When `stroke` or `opacity` varies, the properties of the preceding datapoint carry over. In the case below, we don't see the blue of the last datapoint.

```{ggsql}
Expand Down
29 changes: 26 additions & 3 deletions doc/syntax/layer/type/segment.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,19 @@ For axis-aligned intervals where one coordinate is shared between the start and
* `linetype`: The type of the line, i.e. the dashing pattern.

## Settings
* `position`: Position adjustment. One of `'identity'` (default), `'stack'`, `'dodge'`, or `'jitter'`
* `position`: Position adjustment. One of `'identity'` (default), `'stack'`, `'dodge'`, or `'jitter'`.
* `arrow` Arrow names to display at line ends. Broadly, these come in the following variants:
* Arrow heads: `'open'`, `'closed'`, `'stealth'`, `'latex'`, `'thin'` and `'wedge'`.
* Arrow tails: `'fishtail'`, `'fork'`, and `'feather'`.
* Symmetric: `'bar'`, `'bracket'`, `'cross'`, `'dot'`, `'square'` and `'diamond'`.

Arrow names can optionally be followed by a `:`-separated numeric suffix to set their size in points, e.g. `'open:10'`.
By default, arrows are sized by tripling the `linewidth` value.
Arrows can be declared in the following ways:
* `null` (default) do not display an arrow.
* A single string giving an arrow name to use at the end of the line.
* A `[start, end]` array of names or `null` to specify arrows for the start and end of the line.
To only set an arrow at the start, you could use `['open', null]`.
* `aggregate` Aggregation functions to apply per group:
* `null` apply no group aggregation (default).
* A single string or an array of strings. See an overview of aggregation function in [the `DRAW` documentation](../../clause/draw.qmd#aggregate) and more information in the *Data transformation* section below.
Expand All @@ -40,7 +52,8 @@ The segment layer has no orientations. The axes are treated symmetrically.
Segments are useful when you have known start and end points of the data. For example in a graph

```{ggsql}
WITH edges(x, y, xend, yend, type) AS (VALUES
CREATE TEMP TABLE edges AS
SELECT * FROM (VALUES
(0, 0, 1, 1, 'A'),
(1, 1, 2, 1, 'A'),
(2, 1, 3, 0, 'A'),
Expand All @@ -49,12 +62,22 @@ WITH edges(x, y, xend, yend, type) AS (VALUES
(2, 2, 3, 3, 'B'),
(1, 1, 1, 2, 'C'),
(2, 2, 2, 1, 'C')
)
) t(x, y, xend, yend, type);

VISUALISE x, y, xend, yend FROM edges
DRAW segment
MAPPING type AS stroke
```

For directional graphs, it makes sense to use arrows to indicate the direction.

```{ggsql}
VISUALISE x, y, xend, yend FROM edges
DRAW segment
MAPPING type AS stroke
SETTING arrow => ['fishtail:10', 'open:10']
```

Segments can also draw arbitrary diagonal connections between points. For example, marking a delta between two measurements:

```{ggsql}
Expand Down
4 changes: 2 additions & 2 deletions ggsql-vscode/syntaxes/ggsql.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@
{
"comment": "Geom types from grammar.js",
"name": "support.type.geom.ggsql",
"match": "\\b(point|line|path|bar|col|area|tile|polygon|ribbon|histogram|density|smooth|boxplot|violin|text|label|segment|arrow|rule|range|spatial)\\b"
"match": "\\b(point|line|path|bar|col|area|tile|polygon|ribbon|histogram|density|smooth|boxplot|violin|text|label|segment|rule|range|spatial)\\b"
},
{ "include": "#common-clause-patterns" }
]
Expand All @@ -334,7 +334,7 @@
"patterns": [
{
"name": "support.type.geom.ggsql",
"match": "\\b(point|line|path|bar|col|area|tile|polygon|ribbon|histogram|density|smooth|boxplot|violin|text|label|segment|arrow|rule|range|spatial)\\b"
"match": "\\b(point|line|path|bar|col|area|tile|polygon|ribbon|histogram|density|smooth|boxplot|violin|text|label|segment|rule|range|spatial)\\b"
},
{ "include": "#common-clause-patterns" }
]
Expand Down
1 change: 0 additions & 1 deletion src/parser/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,6 @@ fn parse_geom_type(text: &str) -> Result<Geom> {
"violin" => Ok(Geom::violin()),
"text" => Ok(Geom::text()),
"segment" => Ok(Geom::segment()),
"arrow" => Ok(Geom::arrow()),
"rule" => Ok(Geom::rule()),
"range" => Ok(Geom::range()),
"spatial" => Ok(Geom::spatial()),
Expand Down
2 changes: 1 addition & 1 deletion src/plot/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ plot/

`Layer` itself lives in `layer/mod.rs`. The two big substructures:

- **`layer/geom/`** — one `.rs` per geom plus `mod.rs` (registry) and `types.rs`. The current set: `area`, `arrow`, `bar`, `boxplot`, `density`, `histogram`, `line`, `path`, `point`, `polygon`, `range`, `ribbon`, `rule`, `segment`, `smooth`, `spatial`, `text`, `tile`, `violin`. Each implements `GeomTrait` (declares supported aesthetics, parameters, defaults, stat behaviour, and optional `apply_projection` for coord-specific transforms). User-facing semantics live in [`/doc/syntax/layer/type/`](../../doc/syntax/layer/type/).
- **`layer/geom/`** — one `.rs` per geom plus `mod.rs` (registry) and `types.rs`. The current set: `area`, `bar`, `boxplot`, `density`, `histogram`, `line`, `path`, `point`, `polygon`, `range`, `ribbon`, `rule`, `segment`, `smooth`, `spatial`, `text`, `tile`, `violin`. Each implements `GeomTrait` (declares supported aesthetics, parameters, defaults, stat behaviour, and optional `apply_projection` for coord-specific transforms). User-facing semantics live in [`/doc/syntax/layer/type/`](../../doc/syntax/layer/type/).
- **`layer/position/`** — `identity`, `stack`, `dodge`, `jitter`. Each implements `PositionTrait`. Semantics: [`/doc/syntax/layer/position/`](../../doc/syntax/layer/position/).

`layer/orientation.rs` decides whether a layer is "transposed" (horizontal bar vs vertical bar etc.) based on the mappings + geom type.
Expand Down
55 changes: 0 additions & 55 deletions src/plot/layer/geom/arrow.rs

This file was deleted.

5 changes: 5 additions & 0 deletions src/plot/layer/geom/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,15 @@ impl GeomTrait for Line {
constraint: ParamConstraint::string_option(ORIENTATION_VALUES),
},
super::types::AGGREGATE_PARAM,
super::types::ARROW_PARAM,
];
PARAMS
}

fn setup_layer(&self, _mappings: &mut Mappings, parameters: &mut Parameters) -> Result<()> {
super::types::validate_arrow_param(parameters).map_err(crate::GgsqlError::ValidationError)
}

fn aggregate_domain_aesthetics(&self) -> Option<&'static [&'static str]> {
Some(&["pos1"])
}
Expand Down
12 changes: 0 additions & 12 deletions src/plot/layer/geom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ pub mod types;

// Geom implementations
mod area;
mod arrow;
mod bar;
mod boxplot;
mod density;
Expand All @@ -57,7 +56,6 @@ pub use types::{

// Re-export geom structs for direct access if needed
pub use area::Area;
pub use arrow::Arrow;
pub use bar::Bar;
pub use boxplot::Boxplot;
pub use density::Density;
Expand Down Expand Up @@ -100,7 +98,6 @@ pub enum GeomType {
Violin,
Text,
Segment,
Arrow,
Rule,
Range,
Spatial,
Expand All @@ -124,7 +121,6 @@ impl std::fmt::Display for GeomType {
GeomType::Violin => "violin",
GeomType::Text => "text",
GeomType::Segment => "segment",
GeomType::Arrow => "arrow",
GeomType::Rule => "rule",
GeomType::Range => "range",
GeomType::Spatial => "spatial",
Expand Down Expand Up @@ -739,11 +735,6 @@ impl Geom {
Self(Arc::new(Segment))
}

/// Create an Arrow geom
pub fn arrow() -> Self {
Self(Arc::new(Arrow))
}

/// Create an Rule geom
pub fn rule() -> Self {
Self(Arc::new(Rule))
Expand Down Expand Up @@ -777,7 +768,6 @@ impl Geom {
GeomType::Violin => Self::violin(),
GeomType::Text => Self::text(),
GeomType::Segment => Self::segment(),
GeomType::Arrow => Self::arrow(),
GeomType::Rule => Self::rule(),
GeomType::Range => Self::range(),
GeomType::Spatial => Self::spatial(),
Expand Down Expand Up @@ -1050,7 +1040,6 @@ mod tests {
GeomType::Violin,
GeomType::Text,
GeomType::Segment,
GeomType::Arrow,
GeomType::Rule,
GeomType::Range,
GeomType::Spatial,
Expand All @@ -1074,7 +1063,6 @@ mod tests {
| GeomType::Violin
| GeomType::Text
| GeomType::Segment
| GeomType::Arrow
| GeomType::Rule
| GeomType::Range
| GeomType::Spatial => {}
Expand Down
19 changes: 13 additions & 6 deletions src/plot/layer/geom/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{
DefaultParamValue, GeomTrait, GeomType, ParamConstraint, ParamDefinition,
};
use crate::plot::projection::Projection;
use crate::plot::types::DefaultAestheticValue;
use crate::plot::types::{DefaultAestheticValue, Parameters};
use crate::reader::SqlDialect;
use crate::{Mappings, Result};

Expand All @@ -33,14 +33,21 @@ impl GeomTrait for Path {
}

fn default_params(&self) -> &'static [ParamDefinition] {
const PARAMS: &[ParamDefinition] = &[ParamDefinition {
name: "position",
default: DefaultParamValue::String("identity"),
constraint: ParamConstraint::string_option(POSITION_VALUES),
}];
const PARAMS: &[ParamDefinition] = &[
ParamDefinition {
name: "position",
default: DefaultParamValue::String("identity"),
constraint: ParamConstraint::string_option(POSITION_VALUES),
},
super::types::ARROW_PARAM,
];
PARAMS
}

fn setup_layer(&self, _mappings: &mut Mappings, parameters: &mut Parameters) -> Result<()> {
super::types::validate_arrow_param(parameters).map_err(crate::GgsqlError::ValidationError)
}

fn apply_projection(
&self,
query: &str,
Expand Down
7 changes: 6 additions & 1 deletion src/plot/layer/geom/segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{
DefaultParamValue, GeomTrait, GeomType, ParamConstraint, ParamDefinition,
};
use crate::plot::projection::Projection;
use crate::plot::types::{DefaultAestheticValue, ParameterValue};
use crate::plot::types::{DefaultAestheticValue, ParameterValue, Parameters};
use crate::reader::SqlDialect;
use crate::{naming, Mappings, Result};

Expand Down Expand Up @@ -42,10 +42,15 @@ impl GeomTrait for Segment {
constraint: ParamConstraint::string_option(POSITION_VALUES),
},
super::types::AGGREGATE_PARAM,
super::types::ARROW_PARAM,
];
PARAMS
}

fn setup_layer(&self, _mappings: &mut Mappings, parameters: &mut Parameters) -> Result<()> {
super::types::validate_arrow_param(parameters).map_err(crate::GgsqlError::ValidationError)
}

fn aggregate_domain_aesthetics(&self) -> Option<&'static [&'static str]> {
Some(&[])
}
Expand Down
Loading
Loading