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
11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,17 @@ cxx = { version = "1.0", optional = true }
delaunator = "1.0"
eyre = "0.6.12"
geo = "0.31"
hex = "0.4"
itertools = "0.14"
kdtree = "0.7"
noise = "0.9"
petgraph = "0.8"
rand = "0.9"
rand_distr = "0.5"
rectangle-pack = "0.4"
rhai = { version = "1.23", features = ["only_i64", "no_index", "no_object", "no_time", "no_function", "no_module", "no_custom_syntax", "sync"] }
rune = "0.14"
svg = "0.18"
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
wkb = "0.7" # Hard to update. See #183
wkt = "0.14"

[build-dependencies]
Expand All @@ -117,3 +115,10 @@ cxx-tests = ["cxx"]
cxx-bindings = ["cxx", "dep:cxx", "dep:cxx-build"]

default = ["cxx-bindings"]

[patch.crates-io]
# Unreleased changes add support for Trig functions
#
# Unfortunately 0.14.1 was a hotfix branch, and master is still 0.14.0 with breaking API changes
# since 0.14.0 was released.
rune = { git = "https://github.com/rune-rs/rune.git", branch = "main" }
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,9 @@ $ point-cloud \
--max-y=1 \
--delta-h=0.1 \
--time-steps=20 \
--function "let temp = sqrt(x ** 2.0 + y ** 2.0 + 4.0); x = -sin(x) / temp; y = y / temp;" \
--function "let temp = f64::sqrt(x.powf(2.0) + y.powf(2.0) + 4.0);" \
--function "let x_new = -f64::sin(x) / temp;" \
--function "let y_new = y / temp;" \
--draw-vector-field \
--vector-field-style="STROKE(gray)" \
--vector-field-style="STROKEDASHARRAY(1)" \
Expand Down
62 changes: 31 additions & 31 deletions examples/streamline/field2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion examples/streamline/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ point-cloud \
--max-y=1 \
--delta-h=0.1 \
--time-steps=20 \
--function "let temp = sqrt(x ** 2.0 + y ** 2.0 + 4.0); x = -sin(x) / temp; y = y / temp;" \
--function "let temp = f64::sqrt(x.powf(2.0) + y.powf(2.0) + 4.0);" \
--function "let x_new = -f64::sin(x) / temp;" \
--function "let y_new = y / temp;" \
--draw-vector-field \
--vector-field-style="STROKE(gray)" \
--vector-field-style="STROKEDASHARRAY(1)" \
Expand Down
2 changes: 1 addition & 1 deletion generative/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ pub use stdio::{get_input_reader, get_output_writer};
pub use tgf::{GraphFormat, read_tgf_graph, write_graph, write_tgf_graph};

pub use self::wkt::{
GeometryAndStyle, GeometryFormat, SvgStyle, read_geometries, read_wkt_geometries,
GeometryAndStyle, SvgStyle, read_geometries, read_wkt_geometries,
read_wkt_geometries_and_styles, write_geometries, write_wkt_geometries,
};
240 changes: 4 additions & 236 deletions generative/io/wkt.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,12 @@
use std::io::{BufRead, BufReader, Lines, Read, Write};
use std::str::FromStr;

use clap::ValueEnum;
use geo::{
CoordNum, Geometry, GeometryCollection, Line, LineString, MultiLineString, MultiPoint,
MultiPolygon, Point, Polygon, Rect, Triangle,
};
use hex::{decode, encode_upper};
use wkb::{geom_to_wkb, wkb_to_geom, write_geom_to_wkb};
use wkt::{ToWkt, Wkt};

#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum GeometryFormat {
/// One WKT geometry per line. Ignores trailing garbage; does not skip over leading garbage.
Wkt,
/// Stringified hex encoded WKB, one geometry per line
WkbHex,
/// Raw WKB bytes with no separator between geometries
WkbRaw,
// TODO: Flat?
// TODO: Splines?
}

impl std::fmt::Display for GeometryFormat {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
// important: Should match clap::ValueEnum format
GeometryFormat::Wkt => write!(f, "wkt"),
GeometryFormat::WkbHex => write!(f, "wkb-hex"),
GeometryFormat::WkbRaw => write!(f, "wkb-raw"),
}
}
}

#[derive(PartialEq, Clone, Debug)]
pub enum SvgStyle {
PointRadius(f64),
Expand Down Expand Up @@ -152,30 +126,19 @@ where
}
}

pub fn read_geometries<R>(
reader: R,
format: &GeometryFormat,
) -> Box<dyn Iterator<Item = Geometry<f64>>>
pub fn read_geometries<R>(reader: R) -> Box<dyn Iterator<Item = Geometry<f64>>>
where
R: Read + 'static,
{
match format {
GeometryFormat::Wkt => Box::new(read_wkt_geometries(reader)),
GeometryFormat::WkbHex => Box::new(read_wkbhex_geometries(reader)),
GeometryFormat::WkbRaw => Box::new(read_wkbraw_geometries(reader)),
}
Box::new(read_wkt_geometries(reader))
}

pub fn write_geometries<W, G>(writer: W, geometries: G, format: GeometryFormat) -> eyre::Result<()>
pub fn write_geometries<W, G>(writer: W, geometries: G) -> eyre::Result<()>
where
W: Write,
G: IntoIterator<Item = Geometry<f64>>,
{
match format {
GeometryFormat::Wkt => write_wkt_geometries(writer, geometries),
GeometryFormat::WkbHex => write_wkbhex_geometries(writer, geometries),
GeometryFormat::WkbRaw => write_wkbraw_geometries(writer, geometries),
}
write_wkt_geometries(writer, geometries)
}

pub struct WktGeometries<R>
Expand All @@ -192,20 +155,6 @@ where
lines: Lines<BufReader<R>>,
}

pub struct WkbHexGeometries<R>
where
R: Read,
{
lines: Lines<BufReader<R>>,
}

pub struct WkbRawGeometries<R>
where
R: Read,
{
reader: BufReader<R>,
}

impl<R> Iterator for WktGeometries<R>
where
R: Read,
Expand Down Expand Up @@ -271,69 +220,6 @@ where
}
}

impl<R> Iterator for WkbRawGeometries<R>
where
R: Read,
{
type Item = Geometry<f64>;

fn next(&mut self) -> Option<Self::Item> {
// This is the only way to tell if a BufRead is exhausted without using the nightly only
// unstable has_data_left() API.
match self.reader.fill_buf() {
Ok(buf) => {
if buf.is_empty() {
return None;
}
}
Err(e) => {
tracing::warn!("Failed to read WKB: {e:?}");
return None;
}
}

match wkb_to_geom(&mut self.reader) {
Ok(geom) => Some(geom),
Err(e) => {
tracing::warn!("Failed to parse WKB: {e:?}");
None
}
}
}
}

impl<R> Iterator for WkbHexGeometries<R>
where
R: Read,
{
type Item = Geometry<f64>;

fn next(&mut self) -> Option<Self::Item> {
match self.lines.next() {
Some(line) => match line {
Ok(line) => match decode(line) {
Ok(buf) => match wkb_to_geom(&mut &buf[..]) {
Ok(geom) => Some(geom),
Err(e) => {
tracing::warn!("Failed to parse WKB(hex): {e:?}");
None
}
},
Err(e) => {
tracing::warn!("Failed to decode WKB(hex): {e:?}");
None
}
},
Err(e) => {
tracing::warn!("Failed to read WKB(hex) from line: {e:?}");
None
}
},
None => None,
}
}
}

/// Return an iterator to the WKT geometries passed in through the given BufReader
///
/// Expects one geometry per line (LF or CRLF). Parsing any given line ends after either the first
Expand All @@ -349,24 +235,6 @@ where
}
}

fn read_wkbhex_geometries<R>(reader: R) -> WkbHexGeometries<R>
where
R: Read,
{
WkbHexGeometries {
lines: BufReader::new(reader).lines(),
}
}

fn read_wkbraw_geometries<R>(reader: R) -> WkbRawGeometries<R>
where
R: Read,
{
WkbRawGeometries {
reader: BufReader::new(reader),
}
}

/// Write the given geometries with the given Writer in WKT format
///
/// Each geometry will be written on its own line.
Expand All @@ -382,37 +250,6 @@ where
Ok(())
}

fn write_wkbhex_geometries<W, G>(mut writer: W, geometries: G) -> eyre::Result<()>
where
W: Write,
G: IntoIterator<Item = Geometry<f64>>,
{
for geom in geometries {
match geom_to_wkb(&geom) {
Ok(buffer) => {
writeln!(writer, "{}", encode_upper(buffer))?;
}
Err(e) => {
tracing::warn!("Failed to serialize geometry to WKB: {e:?}");
}
}
}
Ok(())
}

fn write_wkbraw_geometries<W, G>(mut writer: W, geometries: G) -> eyre::Result<()>
where
W: Write,
G: IntoIterator<Item = Geometry<f64>>,
{
for geom in geometries {
// TODO: What's this about the endianity byte?
write_geom_to_wkb(&geom, &mut writer)
.map_err(|e| eyre::eyre!("Failed to write WKB geometry: {e:?}"))?;
}
Ok(())
}

pub fn read_wkt_geometries_and_styles<R>(reader: R) -> WktGeometriesAndStyles<R>
where
R: Read,
Expand Down Expand Up @@ -474,75 +311,6 @@ mod tests {
assert_eq!(actual, expected);
}

#[test]
fn test_wkb_single_input() {
let input_wkt = b"POINT(2 3.5)";
let input_wkbhex = b"010100000000000000000000400000000000000C40";
let input_wkbraw = decode(input_wkbhex).unwrap();

let expected = Geometry::Point(Point::new(2.0, 3.5));

let mut wkt_geometries = read_wkt_geometries(&input_wkt[..]);
assert_eq!(wkt_geometries.next().unwrap(), expected);

let mut wkbraw_geometries = read_wkbraw_geometries(input_wkbraw.as_slice());
assert_eq!(wkbraw_geometries.next().unwrap(), expected);

let mut wkbhex_geometries = read_wkbhex_geometries(&input_wkbhex[..]);
assert_eq!(wkbhex_geometries.next().unwrap(), expected);
}

#[test]
fn test_wkb_multi_input() {
let input_wkt = b"POINT(1 1)\nPOINT(2 3.5)";
let input_wkbhex = b"0101000000000000000000F03F000000000000F03F\n010100000000000000000000400000000000000C40";
let buffer: Vec<u8> = input_wkbhex[..]
.lines()
.flat_map(|l| decode(l.unwrap()).unwrap())
.collect();

let expected1 = Geometry::Point(Point::new(1.0, 1.0));
let expected2 = Geometry::Point(Point::new(2.0, 3.5));

let mut wkt_geometries = read_wkt_geometries(&input_wkt[..]);
assert_eq!(wkt_geometries.next().unwrap(), expected1);
assert_eq!(wkt_geometries.next().unwrap(), expected2);

let mut wkbhex_geometries = read_wkbhex_geometries(&input_wkbhex[..]);
assert_eq!(wkbhex_geometries.next().unwrap(), expected1);
assert_eq!(wkbhex_geometries.next().unwrap(), expected2);

let mut wkbraw_geometries = read_wkbraw_geometries(buffer.as_slice());
assert_eq!(wkbraw_geometries.next().unwrap(), expected1);
assert_eq!(wkbraw_geometries.next().unwrap(), expected2);
}

#[test]
fn test_wkbhex_output() {
let input_wkbhex = b"0101000000000000000000F03F000000000000F03F\n010100000000000000000000400000000000000C40\n";
let geometries = read_wkbhex_geometries(&input_wkbhex[..]);

let mut output_buffer = Vec::<u8>::new();
write_wkbhex_geometries(&mut output_buffer, geometries).unwrap();

assert_eq!(output_buffer, input_wkbhex);
}

#[test]
fn test_wkbraw_output() {
let input_wkbhex = b"0101000000000000000000F03F000000000000F03F\n010100000000000000000000400000000000000C40\n";
let buffer: Vec<u8> = input_wkbhex[..]
.lines()
.flat_map(|l| decode(l.unwrap()).unwrap())
.collect();
let geometries = read_wkbraw_geometries(buffer.as_slice());

let mut output_buffer = Vec::<u8>::new();
write_wkbraw_geometries(&mut output_buffer, geometries).unwrap();

assert_eq!(output_buffer, buffer);
}

#[test]
fn test_can_parse_3d() {
let wkt = b"POINT Z(1 2 3)";
Expand Down
Loading
Loading