Skip to content

Commit

Permalink
LrmScale: fix typo + doc on comment
Browse files Browse the repository at this point in the history
  • Loading branch information
louisgreiner committed Apr 5, 2024
1 parent 979f393 commit 5984ce5
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 65 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ The schema is described in [schema/lrs.fbs](schema/lrs.fbs). The library is writ

If your contribution changes the schema, you will need to generate the file with flatc. The version must be the release 23.5.26. Do not use a version built from master.

`flatc -o src --rust schema/lrs.fbs`
`flatc -o src --rust schema/lrs.fbs`

## Norms

### Comment convention

See [How to write documentation in Rust](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html) to keep the code clean and clear (also [this](https://github.com/rust-lang/rfcs/blob/master/text/1574-more-api-documentation-conventions.md#appendix-a-full-conventions-text) for other examples).
127 changes: 63 additions & 64 deletions src/lrm_scale.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,49 @@
//! A LRM (linear reference model) is an abstract representation
//! where the geometry and real distances are not considered
//! where the geometry and real distances are not considered.
use thiserror::Error;

/// Measurement along the curve. Typically in meters
/// Measurement along the `Curve`. Typically in meters.
pub type CurvePosition = f64;

/// Measurement along the scale. Often in meters, but it could be anything
/// Measurement along the `LrmScale`. Often in meters, but it could be anything.
pub type ScalePosition = f64;

/// Errors when manipulating a LrmScale
/// Errors when manipulating a `LrmScale`.
#[derive(Error, Debug, PartialEq)]
pub enum LrmScaleError {
/// Returned when building a scale from a builder and less than 2 named anchors were provided
/// Returned when building a `LrmScale` from a builder and less than 2 [NamedAnchor] objects were provided.
#[error("a scale needs at least two named anchor")]
NoEnoughNamedAnchor,
/// All the named anchors must be unique within a same scale
/// All the [NamedAnchor] objects must be unique within a same `LrmScale`.
#[error("duplicated anchor: {0}")]
DuplicatedAnchorName(String),
/// Could not find the position on the curve as the anchor is not known
/// Could not find the position on the `Curve` as the [Anchor] is not known.
#[error("anchor is unknown in the LrmScale")]
UnknownAnchorName,
/// Could not find an anchor that matches a given offset
/// Could not find an [Anchor] that matches a given offset.
#[error("no anchor found")]
NoAnchorFound,
}

/// An anchor is a reference point that is well known from which the location is computed
/// An `Anchor` is a reference point that is well known from which the location is computed.
#[derive(PartialEq, Debug)]

pub struct Anchor {
/// Some anchors might not be named
/// e.g. the first anchor of the LRM
/// Some `Anchor` objects might not be named,
/// e.g. the first anchor of the LRM.
pub id: Option<String>,

/// Distance from the start of the scale in the scale space. Can be negative
/// Distance from the start of the scale in the scale space, can be negative.
pub scale_position: ScalePosition,

/// Real distance from the start of the curve
/// The curve might not start at the same 0 (e.g. the curve is longer than the scale)
/// or the curve might not progress at the same rate (e.g. the curve is a schematic representation that distorts distances)
/// Real distance from the start of the `Curve`.
/// The `Curve` might not start at the same 0 (e.g. the `Curve` is longer than the scale),
/// or the `Curve` might not progress at the same rate (e.g. the `Curve` is a schematic representation that distorts distances)
pub curve_position: CurvePosition,
}

impl Anchor {
/// Build a named anchor
/// Builds a named `Anchor`
pub fn new(name: &str, scale_position: ScalePosition, curve_position: CurvePosition) -> Self {
Self {
id: Some(name.to_owned()),
Expand All @@ -53,7 +52,7 @@ impl Anchor {
}
}

/// Build an unnamed anchor
/// Builds an unnamed `Anchor`.
pub fn new_unnamed(scale_position: ScalePosition, curve_position: CurvePosition) -> Self {
Self {
id: None,
Expand All @@ -71,43 +70,43 @@ impl Anchor {
}
}

// Private struct to be used when we only deal with anchor that have names
// Private struct to be used when we only deal with Anchor that has name.
struct NamedAnchor {
id: String,
scale_position: ScalePosition,
curve_position: CurvePosition,
}

/// A helper to build a scale by adding consecutives anchors with relative distances
/// When having all the anchors and their distances in both scale and real position,
/// it is simpler to directly build the LrmScale from an Vec<Anchor>
/// Using the builder will however ensure that the scale is valid
/// An helper to build a scale by adding consecutive [Anchor] objects with relative distances.
/// When having all the `Anchor` objects and their distances in both scale and real position,
/// it is simpler to directly build the [LrmScale] from an `Vec<Anchor>`.
/// Using the builder will however ensure that the scale is valid.
pub struct ScaleBuilder {
anchors: Vec<Anchor>,
}

impl ScaleBuilder {
/// Create a new scale with an initial anchor
/// Creates a new scale with an initial [Anchor].
pub fn new(anchor: Anchor) -> Self {
Self {
anchors: vec![anchor],
}
}

/// Builds a named anchor and adds it to the scale builder
/// Distances are relative to previous anchor
/// Builds a named [Anchor] and adds it to the `ScaleBuilder`.
/// Distances are relative to previous `Anchor`.
pub fn add_named(self, id: &str, scale_dist: ScalePosition, curve_dist: CurvePosition) -> Self {
self.add(Some(id.to_owned()), scale_dist, curve_dist)
}

/// Builds an unnamed anchor and adds it to the scale builder
/// Distances are relative to previous anchor
/// Builds an unnamed [Anchor] and adds it to the `ScaleBuilder`.
/// Distances are relative to previous `Anchor`.
pub fn add_unnamed(self, scale_dist: ScalePosition, curve_dist: CurvePosition) -> Self {
self.add(None, scale_dist, curve_dist)
}

/// Builds an anchor and adds it to the scale builder
/// Distances are relative to previous anchor
/// Builds an [Anchor] and adds it to the `ScaleBuilder`.
/// Distances are relative to previous `Anchor`.
pub fn add(
mut self,
id: Option<String>,
Expand All @@ -127,9 +126,9 @@ impl ScaleBuilder {
self
}

/// Requires at least one named anchor
/// Will fail if none is present and if the anchors names are duplicated
/// This will consume the builder that can not be used after
/// Requires at least one named [Anchor].
/// Will fail if none is present and if the `Anchor` names are duplicated.
/// This will consume the `ScaleBuilder` that can not be used after.
pub fn build(self, id: &str) -> Result<LrmScale, LrmScaleError> {
let mut names = std::collections::HashSet::new();
for anchor in self.anchors.iter() {
Expand All @@ -151,20 +150,20 @@ impl ScaleBuilder {
}
}

/// A measure defines a location on the LRM scale
/// It is given as an anchor name and an offset on that scale
/// It is often represented as 12+100 to say “100 scale units after the anchor 12
/// A measure defines a location on the [LrmScale].
/// It is given as an [Anchor] name and an `offset` on that scale.
/// It is often represented as `12+100` to say `“100 scale units after the Anchor 12`”.
pub struct LrmScaleMeasure {
/// Name of the anchor. While it is often named after a kilometer position
/// it can be anything (a letter, a landmark)
/// `Name` of the [Anchor]. While it is often named after a kilometer position,
/// it can be anything (a letter, a landmark).
pub anchor_name: String,
/// The offset from the anchor in the scale units
/// there is no guarantee that its value matches actual distance on the curve and is defined in scale units
/// The `offset` from the anchor in the scale units.
/// there is no guarantee that its value matches actual distance on the `Curve` and is defined in scale units.
pub scale_offset: ScalePosition,
}

impl LrmScaleMeasure {
/// Build a new LrmMeasure from an anchor name and the offset on the scale
/// Builds a new `LrmMeasure` from an [Anchor] `name` and the `offset` on the [LrmScale].
pub fn new(anchor_name: &str, scale_offset: ScalePosition) -> Self {
Self {
anchor_name: anchor_name.to_owned(),
Expand All @@ -173,18 +172,18 @@ impl LrmScaleMeasure {
}
}

/// Represents an a LRM Scale and allows to map [Measure] to a position along a curve
/// Represents an `LrmScale` and allows to map [Measure] to a position along a `Curve`.
#[derive(PartialEq, Debug)]
pub struct LrmScale {
/// Unique identifier
pub id: String,
/// The anchors are reference points on the scale from which relative distances are used
/// The [Anchor] objects are reference points on the scale from which relative distances are used.
pub anchors: Vec<Anchor>,
}

impl LrmScale {
/// Locates a point along a curve given an anchor and a offset
/// The offset might be negative
/// Locates a point along a `Curve` given an [Anchor] and an `offset`,
/// which might be negative.
pub fn locate_point(&self, measure: &LrmScaleMeasure) -> Result<CurvePosition, LrmScaleError> {
let named_anchor = self
.iter_named()
Expand All @@ -200,20 +199,20 @@ impl LrmScale {
Ok(named_anchor.curve_position + curve_interval * measure.scale_offset / scale_interval)
}

/// Returns a measure given a distance along the curve
/// The corresponding anchor is the named anchor that gives the smallest positive offset
/// If such an anchor does not exists, the first named anchor is used
/// Returns a measure given a distance along the `Curve`.
/// The corresponding [Anchor] is the named `Anchor` that gives the smallest positive `offset`.
/// If such an `Anchor` does not exists, the first named `Anchor` is used.
pub fn locate_anchor(
&self,
curve_position: CurvePosition,
) -> Result<LrmScaleMeasure, LrmScaleError> {
// First we find the nearest named anchor to the curve
// First, we find the nearest named Anchor to the Curve.
let named_anchor = self
.nearest_named(curve_position)
.ok_or(LrmScaleError::NoAnchorFound)?;

// Then we search the nearest anchor that will be the reference
// to convert from curve units to scale units
// Then we search the nearest Anchor that will be the reference
// to convert from Curve units to scale units
let nearest_anchor = if named_anchor.curve_position < curve_position {
self.next_anchor(&named_anchor.id)
.or(self.previous_anchor(&named_anchor.id))
Expand All @@ -233,12 +232,12 @@ impl LrmScale {
}

fn nearest_named(&self, curve_position: CurvePosition) -> Option<NamedAnchor> {
// Tries to find the anchor whose curve_position is the biggest possible, yet smaller than curve position
// Tries to find the Anchor whose curve_position is the biggest possible, yet smaller than Curve position
// Otherwise take the first named
// Anchor names ----A----B----
// Curve positions 2 3
// With curve position = 2.1, we want A
// With curve position = 2.9, we want A
// With Curve position = 2.1, we want A
// With Curve position = 2.9, we want A
// 3.5, we want B
// 1.5, we want A
self.iter_named()
Expand All @@ -247,7 +246,7 @@ impl LrmScale {
.or_else(|| self.iter_named().next())
}

// Find the closest anchor before the anchor having the name `name`
// Finds the closest Anchor before the Anchor having the name `name`
fn previous_anchor(&self, name: &str) -> Option<&Anchor> {
self.anchors
.iter()
Expand All @@ -256,15 +255,15 @@ impl LrmScale {
.nth(1)
}

// Find the closest anchor after the anchor having the name `name`
// Finds the closest Anchor after the Anchor having the name `name`
fn next_anchor(&self, name: &str) -> Option<&Anchor> {
self.anchors
.iter()
.skip_while(|anchor| anchor.id.as_deref() != Some(name))
.nth(1)
}

// Iterates only on named anchors
// Iterates only on named Anchor objects
fn iter_named(&self) -> impl DoubleEndedIterator<Item = NamedAnchor> + '_ {
self.anchors.iter().filter_map(|anchor| anchor.as_named())
}
Expand All @@ -287,12 +286,12 @@ mod tests {
assert_eq!(scale.anchors[0].curve_position, 0.);
assert_eq!(scale.anchors[1].curve_position, 100.);

// Missing named anchor
// Missing named Anchor
let b = ScaleBuilder::new(Anchor::new_unnamed(0., 0.));
let scale = b.build("id");
assert_eq!(scale, Err(LrmScaleError::NoEnoughNamedAnchor));

// Duplicated name
// Duplicated names
let scale = ScaleBuilder::new(Anchor::new("a", 0., 0.))
.add_named("a", 100., 100.)
.build("id");
Expand All @@ -316,7 +315,7 @@ mod tests {
Ok(-50.)
);

// Unknown anchor
// Unknown Anchor
assert_eq!(
scale.locate_point(&LrmScaleMeasure::new("c", 5.)),
Err(LrmScaleError::UnknownAnchorName)
Expand Down Expand Up @@ -366,22 +365,22 @@ mod tests {
.build("id")
.unwrap();

// Unamed----position----Named
// Unnamed----position----Named
let measure = scale.locate_anchor(150.).unwrap();
assert_eq!(measure.anchor_name, "a");
assert_eq!(measure.scale_offset, -0.5);

// position----Unamed----Named
// position----Unnamed----Named
let measure = scale.locate_anchor(50.).unwrap();
assert_eq!(measure.anchor_name, "a");
assert_eq!(measure.scale_offset, -1.5);

// Unamed----Named----position----Unamed
// Unnamed----Named----position----Unnamed
let measure = scale.locate_anchor(350.).unwrap();
assert_eq!(measure.anchor_name, "b");
assert_eq!(measure.scale_offset, 0.5);

// Unamed----Named----Unamed----position
// Unnamed----Named----Unnamed----position
let measure = scale.locate_anchor(500.).unwrap();
assert_eq!(measure.anchor_name, "b");
assert_eq!(measure.scale_offset, 2.);
Expand Down

0 comments on commit 5984ce5

Please sign in to comment.