Merge branch 'release/1.0.1'

This commit is contained in:
Ingolf Wagner 2021-09-28 22:51:27 +02:00
commit a83f3c782e
No known key found for this signature in database
GPG key ID: 76BF5F1928B9618B
13 changed files with 285 additions and 27 deletions

View file

@ -1,10 +1,9 @@
[package] [package]
name = "polygon-art" name = "polygon-art"
version = "1.0.0" version = "1.0.1"
authors = ["Ingolf Wagner <contact@ingolf-wagner.de>"] authors = ["Ingolf Wagner <contact@ingolf-wagner.de>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
description = "Convenience library to use geo to create art." description = "Convenience library to use geo to create art."
documentation = "https://github.com/mrVanDalo/polygon-art" documentation = "https://github.com/mrVanDalo/polygon-art"

View file

@ -6,7 +6,15 @@ Convenience library to create art using [geo](https://georust.org/).
* load SVGs * load SVGs
* scaling * scaling
# Asteroids # Binaries
I deliver some binaries to give you an impression and ideas
for your own images.
All binaries created by polygon art have the same
command line interface.
## Asteroids
asteroids is an example binary which renders an asteroids is an example binary which renders an
image inspired by the image inspired by the
@ -14,12 +22,55 @@ image inspired by the
![image](./images/asteroids.png) ![image](./images/asteroids.png)
run `asteroids --help` to get information. ## Rings
All binaries created by polygon art have the same
command line interface.
# How to run examples ![image](./images/rings.png)
# How to run /examples
``` ```
cargo run --example clipping -- --help # run the examples/clipping cargo run --example clipping -- --help # run the examples/clipping
``` ```
# How to Build (with flakes)
```shell
nix build
```
# How to us it (with flakes)
``` nix
{
description = "example usage";
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-21.05";
inputs.polygon-art.url = "git+https://git.ingolf-wagner.de/palo/polygon-art.git";
inputs.polygon-art.inputs.nixpkgs.follows = "nixpkgs";
outputs = { self, nixpkgs, polygon-art, ... }: {
nixosConfigurations.example = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
({ pkgs, ... }: {
nixpkgs.overlays = [
(_self: _super: {
polygon-art = polygon-art.packages.${pkgs.system};
})
];
environment.systemPackages = [ pkgs.polygon-art.polygon-art ];
})
];
};
};
}
```
# How to update
``` shell
cargo update # to update Cargo.lock
```
and
``` shell
nix flake update # to update flake.lock
nix build # to verify if everything is ok
```

36
default.nix Normal file
View file

@ -0,0 +1,36 @@
{ rustPlatform,
lib,
cairo,
geos,
clipper,
clang,
pkg-config,
cmake,
openssl,
llvmPackages,
... }:
rustPlatform.buildRustPackage {
pname = "polygon-art";
version = "1.0.1";
src = ./.;
cargoSha256 = "0sgk4hw77cxqbqzd258fz67r7fpjblkm7cqh14n5f1c43y8vgxa0";
verifyCargoDeps = true;
# Needed so bindgen can find libclang.so
LIBCLANG_PATH = "${llvmPackages.libclang.lib}/lib";
buildInputs = [ cairo geos clipper openssl ];
nativeBuildInputs =
[ cmake llvmPackages.clang llvmPackages.libclang pkg-config ];
meta = with lib; {
description = "Framework with examples to generate plotter friendly SVGs";
homepage = "https://git.ingolf-wagner.de/palo/polygon-art.git";
license = licenses.gpl3Plus;
maintainers = [ maintainers.mrVanDalo ];
};
}

41
flake.lock Normal file
View file

@ -0,0 +1,41 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1631561581,
"narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1632855891,
"narHash": "sha256-crW76mt9/kbUBiKy/KiSnsQ9JEYgD3StDuYAMVkTbM0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "73086069ebd402e85eaa39c06aef33c2b917f532",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

15
flake.nix Normal file
View file

@ -0,0 +1,15 @@
{
description = "Framework with examples to generate plotter friendly SVGs";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils , ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
polygon-art = (pkgs.callPackage ./default.nix {});
in {
packages.polygon-art = polygon-art;
defaultPackage = polygon-art;
devShell = import ./shell.nix { inherit pkgs; };
}
);
}

BIN
images/rings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

55
src/bin/rings.rs Normal file
View file

@ -0,0 +1,55 @@
use geo::algorithm::rotate::{Rotate, RotatePoint};
use geo::algorithm::translate::Translate;
use geo::{polygon, Coordinate, MultiPolygon, Point};
use geo_clipper::Clipper;
use polygon_art::{Context, MergeExt, TranslateExt};
use rand::prelude::StdRng;
use rand::Rng;
fn main() {
let mut context = Context::create();
context.translate_center();
let min = f64::min(context.width, context.height);
let number_of_rings = (min / 150.) as i32;
let mut radius = min / 2. - 5.;
for _ in 0..number_of_rings {
let ring_size = context.gen_rng().gen_range(10..(radius as i32 / 5)) as f64;
radius = radius - ring_size;
if radius < 10. {
break;
}
let polygons = generate_ring(&mut context.gen_rng(), radius, ring_size);
radius = radius - ring_size;
context.draw_multipolygon(&polygons);
}
context.render();
}
fn generate_ring(rng: &mut StdRng, radius: f64, ring_size: f64) -> MultiPolygon<f64> {
let main = polygon![
(x: 0.0, y:0.0),
(x: ring_size , y:0.0),
(x: ring_size , y:ring_size ),
(x: 0.0, y:ring_size ),
]
.translate_center()
.rotate(30.)
.translate(0., radius);
let center_point = Point(Coordinate { x: 0., y: 0. });
let parts = rng.gen_range(20..120);
let rotation = 360. / parts as f64;
let direction = if rng.gen_bool(0.5) { -1. } else { 1. };
let fragment = main.difference(
&main.rotate_around_point(direction * rotation, center_point),
100.,
);
let mut polygons: MultiPolygon<f64> = MultiPolygon(Vec::new());
for index in 0..parts {
polygons.merge(&fragment.rotate_around_point(rotation * index as f64, center_point));
}
polygons
}

View file

@ -4,12 +4,12 @@ use geo::{polygon, LineString, MultiLineString, MultiPolygon, Polygon};
use rand::rngs::StdRng; use rand::rngs::StdRng;
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use crate::context::commandline::Opt; use crate::context::command_line_options::Opt;
use crate::context::palette::Palette; use crate::context::palette::Palette;
use crate::context::renderer::{PngRenderContainer, RenderContainer, SvgRenderContainer}; use crate::context::renderer::{PngRenderContainer, RenderContainer, SvgRenderContainer};
use iso8601::Date::*; use iso8601::Date::*;
pub mod commandline; pub mod command_line_options;
pub mod palette; pub mod palette;
pub mod renderer; pub mod renderer;
@ -116,7 +116,8 @@ impl Context {
let opt = Opt::get_command_line_arguments(); let opt = Opt::get_command_line_arguments();
let line_size = opt.line_size.clone(); let line_size = opt.line_size.clone();
let mm = 3.779527547619048; // calculated with inkscape 1587.40157 (px) / 420 (mm) (from a3) // todo : this is not correct
let mm = 2.8346456693; // 1mm = 2.8346456693pt
let (mut height, mut width) = match opt.format { let (mut height, mut width) = match opt.format {
None => (f64::from(opt.height.clone()), f64::from(opt.width.clone())), None => (f64::from(opt.height.clone()), f64::from(opt.width.clone())),
Some(format) => match &format[..] { Some(format) => match &format[..] {
@ -128,6 +129,9 @@ impl Context {
}, },
}; };
print!("height: {}\n", height);
print!("width: {}\n", width);
if opt.orientation == "portrait" { if opt.orientation == "portrait" {
let cache = height; let cache = height;
height = width; height = width;
@ -150,11 +154,12 @@ impl Context {
let mut rng = StdRng::seed_from_u64(random_seed); let mut rng = StdRng::seed_from_u64(random_seed);
// color // color
let y: f32 = rng.gen(); // generates a float between 0 and 1 let main_color = Palette::random_color(StdRng::seed_from_u64(rng.gen()));
let hue: f32 = y * 360.0; let palette = match &opt.color_type[..] {
let saturation: f32 = f32::max(rng.gen(), 0.6); "bright_on_dark" => Palette::bright_on_dark(main_color),
let value: f32 = f32::max(rng.gen(), 0.6); "dark_on_bright" => Palette::dark_on_bright(main_color),
let palette = Palette::dark_on_bright(Palette::color(hue, saturation, value)); _ => Palette::bright_on_dark(main_color),
};
let render_container: Box<dyn RenderContainer> = if opt.output_type == "svg" { let render_container: Box<dyn RenderContainer> = if opt.output_type == "svg" {
let svg_surface = SvgSurface::new( let svg_surface = SvgSurface::new(

View file

@ -53,6 +53,14 @@ pub struct Opt {
/// (must be ISO8601) /// (must be ISO8601)
#[structopt(long = "random-date", conflicts_with = "random-seed", parse(try_from_str = iso8601::date))] #[structopt(long = "random-date", conflicts_with = "random-seed", parse(try_from_str = iso8601::date))]
pub random_seed_date: Option<iso8601::Date>, pub random_seed_date: Option<iso8601::Date>,
/// how to use color palette to draw image
#[structopt(
long,
possible_values=&["dark_on_bright", "bright_on_dark"],
default_value="dark_on_bright"
)]
pub color_type: String,
} }
impl Opt { impl Opt {

View file

@ -1,5 +1,6 @@
//! Color palate generator. //! Color palate generator.
use crate::RandomExt;
use palette::rgb::Rgb; use palette::rgb::Rgb;
use palette::FromColor; use palette::FromColor;
use palette::Hsl; use palette::Hsl;
@ -19,21 +20,18 @@ impl Palette {
/// and to color less. /// and to color less.
#[allow(dead_code)] #[allow(dead_code)]
pub fn random_color(mut rng: StdRng) -> Rgb { pub fn random_color(mut rng: StdRng) -> Rgb {
//let mut rng = rand::thread_rng(); let hue: f32 = rng.gen_interval(0., 360.);
let y: f32 = rng.gen(); // generates a float between 0 and 1 let saturation: f32 = rng.gen_interval(0.9, 1.0);
let hue: f32 = y * 360.0; let value: f32 = rng.gen_interval(0.8, 1.0);
let saturation: f32 = f32::max(rng.gen(), 0.6);
let value: f32 = f32::max(rng.gen(), 0.6);
//Rgb::from_linear(Hsv::new(hue, saturation, value).into_rgb())
Palette::color(hue, saturation, value) Palette::color(hue, saturation, value)
} }
pub fn color(hue: f32, saturation: f32, value: f32) -> Rgb { pub fn color(hue: f32, saturation: f32, value: f32) -> Rgb {
Rgb::from_linear(Hsv::new(hue, saturation, value).into_rgb()) Rgb::from_linear(Hsv::new(hue, saturation, value).into_rgb())
} }
/// generate a palette from the tint and shade palette /// generate a palette from the tint and shade palette
/// algorithm. choose a dark background and a bright filling color /// algorithm. choose a dark background, and a bright filling color
#[allow(dead_code)]
pub fn bright_on_dark(input: Rgb) -> Palette { pub fn bright_on_dark(input: Rgb) -> Palette {
let tint_and_shade_palette = TintAndShadePalette::create(input); let tint_and_shade_palette = TintAndShadePalette::create(input);
Palette { Palette {
@ -43,7 +41,7 @@ impl Palette {
} }
/// generate a palette from the tint and shade palette /// generate a palette from the tint and shade palette
/// algorithm. choose a bright background and a dark filling color /// algorithm. choose a bright background, and a dark filling color
pub fn dark_on_bright(input: Rgb) -> Palette { pub fn dark_on_bright(input: Rgb) -> Palette {
let tint_and_shade_palette = TintAndShadePalette::create(input); let tint_and_shade_palette = TintAndShadePalette::create(input);
Palette { Palette {
@ -54,8 +52,7 @@ impl Palette {
} }
/// The Tint and Shade Palette from https://gitlab.com/cameralibre/tint-and-shade /// The Tint and Shade Palette from https://gitlab.com/cameralibre/tint-and-shade
/// Thank you Sam /// Thank you, Sam
#[allow(dead_code)]
struct TintAndShadePalette { struct TintAndShadePalette {
base_color: Rgb, base_color: Rgb,
base_tint_30: Rgb, base_tint_30: Rgb,

View file

@ -1,3 +1,4 @@
pub(crate) mod merge_ext; pub(crate) mod merge_ext;
pub(crate) mod random_ext;
pub(crate) mod scale_ext; pub(crate) mod scale_ext;
pub(crate) mod translate_ext; pub(crate) mod translate_ext;

View file

@ -0,0 +1,49 @@
use rand::rngs::StdRng;
use rand::Rng;
pub trait RandomExt<T> {
fn gen_interval(&mut self, min: T, max: T) -> T;
}
impl RandomExt<f32> for StdRng {
fn gen_interval(&mut self, min: f32, max: f32) -> f32 {
if min == max {
return min;
}
let minimum = f32::min(min, max);
let maximum = f32::max(min, max);
let diff = maximum - minimum;
let x: f32 = self.gen();
x * diff + minimum
}
}
impl RandomExt<f64> for StdRng {
fn gen_interval(&mut self, min: f64, max: f64) -> f64 {
if min == max {
return min;
}
let minimum = f64::min(min, max);
let maximum = f64::max(min, max);
let diff = maximum - minimum;
let x: f64 = self.gen();
x * diff + minimum
}
}
#[cfg(test)]
mod test {
use rand::prelude::StdRng;
use rand::{Rng, SeedableRng};
#[test]
fn test_gen() {
let random_seed = rand::random();
let mut rng = StdRng::seed_from_u64(random_seed);
for _ in 0..1000 {
let random_number: f64 = rng.gen();
assert!(random_number < 1.);
assert!(random_number > 0.);
}
}
}

View file

@ -3,6 +3,7 @@ pub use crate::context::Context;
mod extensions; mod extensions;
pub use crate::extensions::merge_ext::MergeExt; pub use crate::extensions::merge_ext::MergeExt;
pub use crate::extensions::random_ext::RandomExt;
pub use crate::extensions::scale_ext::ScaleExt; pub use crate::extensions::scale_ext::ScaleExt;
pub use crate::extensions::translate_ext::TranslateExt; pub use crate::extensions::translate_ext::TranslateExt;