Recreational Ruby tools for geometry. This ranges from simple functions like area calculation and line intersection, to Delaunay triangulation and Voronoi partitions. This is companion code to my educational video series about code and sound, and this code was featured in a video about Voronoi diagrams and Delaunay triangulations. I've also written more about this code on my blog.
XRES=960 YRES=540 bin/voronoi_transitions.rb /tmp/polygon.gif \
test_data/3gon.yml 30 60 \
test_data/square.yml 30 60 \
test_data/pentagon.json 30 60 \
test_data/zero.csv 30 0
You might also be interested in mb-sound, mb-math, and mb-util.
This code is reasonably well-tested, but I recommend using it for non-critical tasks like fun and offline graphics, and not for making important decisions or mission-critical data modeling.
Check out all of the scripts in bin/
; they usually have a header comment
describing what they do.
The bin/voronoi_transitions.rb
script will turn a sequence of Voronoi
diagrams into an animation with smooth transitions.
See the documentation for MB::Geometry::Generators.generate
in
lib/mb/geometry/generators.rb
for the syntax of the Voronoi diagram file
format (.json, .yml, or .csv), with examples in test_data/
. Also check out
the MB::Geometry::VoronoiAnimator
class.
XRES=640 RANDOM_SEED=10 bin/voronoi_transitions.rb /tmp/shuffle.gif \
test_data/lines.json 0 \
__shuffle 60
XRES=960 YRES=540 bin/voronoi_transitions.rb /tmp/polygon.mp4 \
test_data/3gon.yml 30 15 \
test_data/square.yml 30 15 \
test_data/pentagon.json 30 15 \
test_data/zero.csv 30 0
polygon.mp4
From the shell:
bin/voronoi_to_svg.rb test_data/pentagon.json /tmp/pentagon.svg
From code:
require 'mb-geometry'
# The Hash must be inside an Array to prevent it being interpreted as keyword args
# Rotation is in degrees
v = MB::Geometry::Voronoi.new([{ generator: :polygon, sides: 5, rotate: 30 }]) ; nil
v.save_svg('/tmp/pentagon_from_code.svg')
# You can save the Delaunay triangulation instead:
v.save_delaunay_svg('/tmp/pentagon_delaunay.svg')
You can generate a JSON points file for a Voronoi diagram from a PNG (or other) image, then generate a pixel-art SVG from that (16x16 or smaller images recommended):
bin/png_to_voronoi.rb image.png image.json
bin/voronoi_to_svg.rb image.json image.svg
Input | Output |
---|---|
The bin/triangulate.rb
, bin/voronoi_to_svg.rb
, and
bin/voronoi_transitions.rb
tools all use a common file format to describe a
Voronoi partition. Any JSON, YAML, or CSV file that parses to an Array of X
and Y coordinates is supported. There is also an abbreviated syntax for
generating polygons or random points. The file format is documented below in
the Voronoi points file format section. See
MB::Geometry::Generators#generate_from_file for more info.
JSON:
[
[-1, 1],
[1.5, -0.5],
[-1, 0],
]
YML:
- [-1, 1]
- - 1.5
- -0.5
- [-1, 0]
CSV:
-1,1
1.5,-0.5
-1,0
Point hashes may include a name and a color for a point.
JSON:
[
{ "x": -1, "y": 1, "name": "A", "color": [0.1, 0.2, 0.5, 0.9] },
{ "x": 1.5, "y": -0.5, "name": "B", "color": [0.6, 0.2, 0.5, 0.9] },
{ "x": -1, "y": 0, "name": "C", "color": [0.3, 0.8, 0.4, 0.9] }
]
bin/voronoi_to_svg.rb /tmp/hashes.json /tmp/hashes.svg
See the source code and files under test_data/
for details. Generators
include :random
, :segment
, :polygon
, :grid
, :points
, and :multi
.
Colors and names may be specified in separate Arrays. Colors will be reused in a loop if there are more points than colors.
The anneal
option controls how many times points are moved toward their
cell's center. The bounding_box
option controls the maximum space in which
the points may expand. See MB::Geometry::Voronoi#anneal
.
YML:
generator: :random
count: 10
seed: 3
anneal: 1
bounding_box: [-3, -2, 3, 2]
colors:
- [0.1, 0.2, 0.5, 0.9]
- [0.5, 0.3, 0.2, 0.9]
names:
- "P0"
- "P1"
- "P2"
- "P3"
- "P4"
- "P5"
- "P6"
- "P7"
- "P8"
- "P9"
LABELS=1 bin/voronoi_to_svg.rb /tmp/random_points.yml /tmp/random_points.svg
YML:
generator: :multi
generators:
# Line segment generator
- generator: :segment
count: 5
from: [-3, -0.1]
to: [-2, -1]
# Polygon generator
- generator: :polygon
sides: 7
radius: 0.5
rotate: 45
clockwise: true
# Random points generator
- generator: :random
count: 10
seed: 3
xmin: 1.5
xmax: 3.25
ymin: -1.5
ymax: 1.5
anneal: 1
# Another segment
- generator: :segment
count: 5
from: [-3, 0.1]
to: [-2, 1]
The Voronoi diagrams generated by this code are all derived from Delaunay
triangulation. There are three backends for Delaunay triangulation that can be switched by
setting the DELAUNAY_ENGINE
and DELAUNAY_DEBUG
environment variables.
DELAUNAY_ENGINE=rubyvor
-- Uses the RubyVor Gem's C extension for Delaunay triangulation. This is very fast, and the default.DELAUNAY_ENGINE=delaunay
-- My pure Ruby implementation of Lee and Schacter's 1980 divide and conquer algorithm. SeeREADME-Delaunay.md
andlib/mb/geometry/delaunay.rb
for more info. This is reasonably fast.DELAUNAY_ENGINE=delaunay_debug DELAUNAY_DEBUG=1
-- The same pure Ruby implementation, but with lots of debugging output and each step of the algorithm dumped to a .json file in /tmp (or the directory specified by theJSON_DIR
environment variable, if set). This is slow.
The triangulate.rb
command prints each input point's neighbors and the final
list of triangles in Ruby Hash syntax.
DELAUNAY_ENGINE=delaunay bin/triangulate.rb test_data/square.yml
DELAUNAY_ENGINE=rubyvor bin/triangulate.rb test_data/square.yml
See MB::Geometry
.
MB::Geometry.polygon_area([[0, 0], [1, 0], [1, 1], [0, 1]])
# => 1.0
This project contains some useful programs of its own, or you can use it as a Gem (with Git source) in your own projects.
First, install a Ruby version manager like RVM. Using the system's Ruby is not recommended -- that is only for applications that come with the system. You should follow the instructions from https://rvm.io, but here are the basics:
gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
\curl -sSL https://get.rvm.io | bash -s stable
Next, install Ruby. RVM binary rubies are still broken on Ubuntu 20.04.x, so
use the --disable-binary
option if you are running Ubuntu 20.04.x.
rvm install --disable-binary 2.7.3
You can tell RVM to isolate all your projects and switch Ruby versions
automatically by creating .ruby-version
and .ruby-gemset
files (already
present in this project):
cd mb-geometry
cat .ruby-gemset
cat .ruby-version
Now install dependencies:
bundle install
You will also want ffmpeg if you want to make GIFs or videos:
# Debian/Ubuntu
sudo apt-get install ffmpeg
# macOS (might not be exactly right, but this is the gist)
brew install ffmpeg
To use mb-geometry in your own Ruby projects, add this Git repo to your
Gemfile
(plus the Git repos of other pre-release gems it depends on):
# your-project/Gemfile
gem 'mb-geometry', git: 'https://github.com/mike-bourgeous/mb-geometry.git'
gem 'mb-math', git: 'https://github.com/mike-bourgeous/mb-math.git'
gem 'mb-util', git: 'https://github.com/mike-bourgeous/mb-util.git'
Run rspec
, or play with the included scripts under bin/
.
Pull requests welcome, though development is focused specifically on the needs of my video series.
This project is released under a 2-clause BSD license. See the LICENSE file.
- RubyVor; unfortunately the homepage link is no longer valid, but the RubyVor source code is still available.
See README-Delaunay.md
for Delaunay tringulation references.