hexflex 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2833a0751fc3f43a005521396169e153cd228633
4
+ data.tar.gz: 51f73e9b9b794272f846b9517b471c9051bdf0eb
5
+ SHA512:
6
+ metadata.gz: c24254a00503b7a81bd6dea748e34bbca9671cfbf2d6f613a94d7b7af2bf066c955ffe4094ebc4663b6bf09abcb565223d19963e3d51725cc68c279bf10defe3
7
+ data.tar.gz: 13c1663cfafb6937c5dbfc1a69432ab301015c326629925c7463f9bcdc8bde5319c965400be9d10ff0e1d6b37cdc7326ba13a81e35e89af8e03838b7870d3948
@@ -0,0 +1,42 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+ /.rake_tasks~
12
+
13
+ ## Specific to RubyMotion:
14
+ .dat*
15
+ .repl_history
16
+ build/
17
+
18
+ ## Documentation cache and generated files:
19
+ /.yardoc/
20
+ /_yardoc/
21
+ /doc/
22
+ /rdoc/
23
+
24
+ ## Environment normalisation:
25
+ /.bundle/
26
+ /lib/bundler/man/
27
+
28
+ # for a library or gem, you might want to ignore these files since the code is
29
+ # intended to run in multiple environments; otherwise, check them in:
30
+ Gemfile.lock
31
+ .ruby-version
32
+ .ruby-gemset
33
+
34
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35
+ .rvmrc
36
+
37
+ # development related files
38
+ tags
39
+ feh*
40
+
41
+ # don't commit these unless you make a design change
42
+ /*.png
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1 @@
1
+ hexflex
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hexaflexa.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Tom Dunlap
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Tom Dunlap
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/NOTES ADDED
@@ -0,0 +1,7 @@
1
+ [-1,-1, 1,1 -1,1
2
+
3
+ [0,1, sqrt(3)/2,-1/2, -sqrt(3)/2,-r/2]
4
+
5
+ [1,0,
6
+
7
+ http://www.vitutor.com/geometry/plane/equilateral_triangle.html
@@ -0,0 +1,38 @@
1
+ # Hexflex
2
+ [![Build Status](https://travis-ci.org/aauthor/hexflex.svg?branch=master)](https://travis-ci.org/aauthor/hexflex)
3
+
4
+ Hexflex is a Ruby gem and command-line tool for automatically generating
5
+ [hexaflexagon] templates.
6
+
7
+ ## Installation
8
+
9
+ gem install 'hexaflexa'
10
+
11
+ ...or you can put it in your Gemfile.
12
+
13
+ ## Usage
14
+
15
+ ### as a gem in your ruby project
16
+
17
+ To create an [RVG] object containing a vector of the hexaflexagon template:
18
+
19
+ Hexflex.make_template_vector(side_fills: ARRAY_OF_SIDE_FILLS, template: TEMPLATE)
20
+
21
+ To save the hexaflexagon template as a file to the disk:
22
+
23
+ Hexflex.create_template_image!(side_fills: ARRAY_OF_SIDE_FILLS, template: TEMPLATE, output_file_name: OUTPUT)
24
+
25
+ Where:
26
+ - a `SIDE_FILL` is a [standard X color] or path to file for a side of the hexaflexagon. Either three or zero sides should be specified. The default are cyan, magenta, and yellow.
27
+ - `TEMPLATE` is template the form for the hexaflexagon. It can either be "tape" or "glue". The default is "tape".
28
+ - `OUTPUT` is a path to save the hexaflexagon template image. The default is "out.png".
29
+
30
+ ### as a command-line tool
31
+
32
+ hexflex [-s SIDE_FILL -s SIDE_FILL -s SIDE_FILL] [-t TEMPLATE] [-o OUTPUT]
33
+
34
+ See above for definitions of `SIDE_FILL`, `TEMPLATE`, AND `OUTPUT`.
35
+
36
+ [hexaflexagon]: https://en.wikipedia.org/wiki/Flexagon#Trihexaflexagon
37
+ [standard X color]: https://en.wikipedia.org/wiki/X11_color_names
38
+ [RVG]: https://rmagick.github.io/rvg.html
@@ -0,0 +1,66 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'hexflex'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default =>
8
+ [:spec, :default_test, :color_test, :image_test, :photo_test, :glue_test]
9
+
10
+ task :generate_fixtures =>
11
+ [:default_test, :color_test, :image_test, :photo_test, :glue_test]
12
+
13
+ TEST_FIXTURES= 'spec/fixtures'
14
+
15
+ task :default_test do
16
+ Hexflex.create_template_image!
17
+ puts 'Default test output to out.png.'
18
+ end
19
+
20
+ task :color_test do
21
+ Hexflex.create_template_image!(
22
+ output_file_name: 'color_test.png',
23
+ side_fills: [:blue, :green, :yellow]
24
+ )
25
+ puts 'Color test output to color_test.png.'
26
+ end
27
+
28
+ task :image_test do
29
+ fixture_path = "#{TEST_FIXTURES}/images"
30
+ Hexflex.create_template_image!(
31
+ output_file_name: 'image_test.png',
32
+ side_fills: [
33
+ "#{fixture_path}/1.png",
34
+ "#{fixture_path}/2.png",
35
+ "#{fixture_path}/3.png"
36
+ ]
37
+ )
38
+ puts 'Image test output to image_test.png.'
39
+ end
40
+
41
+ task :photo_test do
42
+ fixture_path = "#{TEST_FIXTURES}/photos"
43
+ Hexflex.create_template_image!(
44
+ output_file_name: 'photo_test.png',
45
+ side_fills: [
46
+ "#{fixture_path}/1.jpg",
47
+ "#{fixture_path}/2.jpg",
48
+ "#{fixture_path}/3.jpg"
49
+ ]
50
+ )
51
+ puts 'Photo test output to photo_test.png.'
52
+ end
53
+
54
+ task :glue_test do
55
+ fixture_path = "#{TEST_FIXTURES}/photos"
56
+ Hexflex.create_template_image!(
57
+ output_file_name: 'glue_test.png',
58
+ side_fills: [
59
+ "#{fixture_path}/1.jpg",
60
+ "#{fixture_path}/2.jpg",
61
+ "#{fixture_path}/3.jpg"
62
+ ],
63
+ template_style: :glue
64
+ )
65
+ puts 'Photo test with glue template style output to glue_test.png.'
66
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "hexflex"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
12
+
13
+ # require "irb"
14
+ # IRB.start
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'hexflex'
5
+ require 'optparse'
6
+
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.banner = 'Usage: hexflex -s SIDE -s SIDE -s SIDE [-t TEMPLATE] [-o OUTPUT]'
10
+
11
+ opts.on('-s', '--side SIDE', 'A standard X color or path to image for a side of the hexaflexagon. The defaults are cyan, magenta, and yellow.') do |side|
12
+ options[:side_fills] ||= []
13
+ options[:side_fills] << side
14
+ end
15
+
16
+ opts.on('-t', '--template TEMPLATE', 'template the form for the hexaflexagon. It can either be "tape" or "glue". The default is "tape".') do |template|
17
+ options[:template_style] = template
18
+ end
19
+
20
+ opts.on('-o', '--output OUTPUT', 'A path to save the hexaflexagon template image. The default is "out.png".' ) do |file_name|
21
+ options[:output_file_name] = file_name
22
+ end
23
+ end.parse!
24
+
25
+ Hexflex.create_template_image!(options)
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hexflex/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hexflex"
8
+ spec.version = Hexflex::VERSION
9
+ spec.authors = ["Tom Dunlap"]
10
+ spec.email = ["apocryphalauthor@gmail.com"]
11
+
12
+ spec.summary = "Generate hexaflexagons to print and fold!"
13
+ spec.description = `cat README.md`
14
+ spec.homepage = "http://github.com/aauthor/hexflex"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "rmagick", "~> 2.13"
23
+ spec.add_dependency "activesupport", "~>4.2"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.4"
27
+ spec.add_development_dependency "pry-byebug", "~> 3.1"
28
+ spec.add_development_dependency "rspec", "~> 3.3"
29
+ end
@@ -0,0 +1,36 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext'
3
+ require 'yaml'
4
+
5
+ require 'hexflex/hexaflexagon'
6
+ require 'hexflex/tape_template'
7
+ require 'hexflex/glue_template'
8
+
9
+ module Hexflex
10
+
11
+ class << self
12
+
13
+ DEFAULT_SIDE_FILLS = [:cyan, :magenta, :yellow]
14
+ DEFAULT_TEMPLATE_STYLE = :tape
15
+
16
+ def make_template_vector(side_fills: DEFAULT_SIDE_FILLS, template_style: DEFAULT_TEMPLATE_STYLE)
17
+ hexaflexagon = Hexaflexagon.new(side_fills)
18
+ make_template(hexaflexagon, template_style).make_vector
19
+ end
20
+
21
+ def create_template_image!(output_file_name: 'out.png', **options)
22
+ vector = make_template_vector(options)
23
+ vector.draw.write(output_file_name)
24
+ end
25
+
26
+ def make_template(hexaflexagon, template_style)
27
+ case template_style.to_sym
28
+ when :glue
29
+ GlueTemplate.new(hexaflexagon)
30
+ when :tape
31
+ TapeTemplate.new(hexaflexagon)
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ require "rvg/rvg"
2
+
3
+ module Hexflex
4
+ class BaseTemplate
5
+
6
+ class InterfaceViolation < StandardError
7
+ def message
8
+ "##{method} must be defined by classes implementing BaseTemplate."
9
+ end
10
+
11
+ def method
12
+ backtrace.first.split.last.delete("`'")
13
+ end
14
+ end
15
+
16
+ attr_reader :hexaflexagon, :sides
17
+
18
+ def initialize(hexaflexagon)
19
+ @hexaflexagon = hexaflexagon
20
+ @sides = hexaflexagon.sides
21
+ end
22
+
23
+ def make_vector
24
+ fail InterfaceViolation
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,48 @@
1
+ require 'hexflex/base_template'
2
+ require 'hexflex/triangle_grid'
3
+ require 'hexflex/rvg_template_vectorizer'
4
+ require 'hexflex/triangle'
5
+
6
+ module Hexflex
7
+ class GlueTemplate < BaseTemplate
8
+
9
+ def make_vector
10
+ RvgTemplateVectorizer.new(triangle_grid).vectorize
11
+ end
12
+
13
+ private
14
+
15
+ def triangle_grid
16
+ TriangleGrid.new.tap do |grid|
17
+ grid.place(blank_triangle, 0, 0)
18
+ grid.place(blank_triangle, 0, 9)
19
+
20
+ grid.place(sides[0].triangles[0], 0, 1)
21
+ grid.place(sides[0].triangles[1], 1, 3)
22
+ grid.place(sides[0].triangles[2], 1, 4)
23
+ grid.place(sides[0].triangles[3], 0, 6)
24
+ grid.place(sides[0].triangles[4], 0, 7)
25
+ grid.place(sides[0].triangles[5], 1, 9)
26
+
27
+ grid.place(sides[2].triangles[0], 0, 3)
28
+ grid.place(sides[2].triangles[1], 1, 5)
29
+ grid.place(sides[2].triangles[2], 1, 6)
30
+ grid.place(sides[2].triangles[3], 0, 8)
31
+ grid.place(sides[2].triangles[4], 1, 0)
32
+ grid.place(sides[2].triangles[5], 0, 2)
33
+
34
+ grid.place(sides[1].triangles[0], 0, 5)
35
+ grid.place(sides[1].triangles[1], 1, 7)
36
+ grid.place(sides[1].triangles[2], 1, 8)
37
+ grid.place(sides[1].triangles[3], 1, 1)
38
+ grid.place(sides[1].triangles[4], 1, 2)
39
+ grid.place(sides[1].triangles[5], 0, 4)
40
+ end
41
+ end
42
+
43
+ def blank_triangle
44
+ Triangle.place_holder
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,26 @@
1
+ require 'forwardable'
2
+
3
+ module Hexflex
4
+ class GridTriangle
5
+ extend Forwardable
6
+
7
+ attr_reader :triangle, :row, :position
8
+
9
+ def_delegators :@triangle, :fill, :image_fill?, :color_fill?, :index
10
+
11
+ def initialize(triangle:, row:, position:)
12
+ @triangle = triangle
13
+ @row = row
14
+ @position = position
15
+ end
16
+
17
+ def first_row?
18
+ row == 0
19
+ end
20
+
21
+ def second_row?
22
+ row == 1
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ require "hexflex/side"
2
+
3
+ module Hexflex
4
+ class Hexaflexagon
5
+
6
+ attr_accessor :sides
7
+
8
+ def initialize(side_fills)
9
+ @raw_fills = side_fills
10
+ @sides = Array.new(3) do |index|
11
+ Side.new(fill: side_fills[index])
12
+ end
13
+ end
14
+
15
+ def triangles
16
+ self.sides.map(&:triangles).flatten
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ require 'hexflex/triangle_rvg_group'
2
+
3
+ module Hexflex
4
+ class RvgColorTriangleVectorizer
5
+
6
+ SIMPLE_TRIANGLE_BASE = Math::sqrt(3)
7
+ SIMPLE_TRIANGLE_HEIGHT = 1.5
8
+
9
+ def initialize(triangle)
10
+ @triangle = triangle
11
+ end
12
+
13
+ def vectorize
14
+ @vector ||= make_vector
15
+ end
16
+
17
+ private
18
+
19
+ def make_vector
20
+ TriangleRvgGroup.new(
21
+ make_simple_triangle,
22
+ base: SIMPLE_TRIANGLE_BASE,
23
+ height: SIMPLE_TRIANGLE_HEIGHT
24
+ )
25
+ end
26
+
27
+ def make_simple_triangle
28
+ base = SIMPLE_TRIANGLE_BASE
29
+ height = SIMPLE_TRIANGLE_HEIGHT
30
+ Magick::RVG::Group.new.tap do |group|
31
+ group.polygon(
32
+ 0, height,
33
+ base, height,
34
+ base/2, 0
35
+ ).styles(fill: @triangle.fill.to_s)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,99 @@
1
+ require 'hexflex/triangle_rvg_group'
2
+
3
+ module Hexflex
4
+ class RvgImageTriangleVectorizer
5
+
6
+ def initialize(triangle)
7
+ @triangle = triangle
8
+ @image = Magick::Image.read(triangle.fill).first
9
+ @index = triangle.index
10
+ end
11
+
12
+ def vectorize
13
+ @vector ||= make_vector
14
+ end
15
+
16
+ private
17
+
18
+ attr_accessor :image, :index
19
+
20
+ def make_vector
21
+ TriangleRvgGroup.new(
22
+ make_rvg_group,
23
+ base: triangle_base,
24
+ height: triangle_height
25
+ )
26
+ end
27
+
28
+ def make_rvg_group
29
+ Magick::RVG::Group.new.tap do |group|
30
+ group.use(centered_background)
31
+ .rotate(-60 * index, triangle_base, triangle_height)
32
+ group.styles(clip_path: clip_path)
33
+ end
34
+ end
35
+
36
+ def centered_background
37
+ Magick::RVG::Group.new.tap do |group|
38
+ group.image(image).tap do |img|
39
+ if image_tall?
40
+ img.translate(0, (hexagon_height - image_height) / 2.0)
41
+ else
42
+ img.translate((hexagon_width - image_width) / 2.0, 0)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def triangle_height
49
+ if image_tall?
50
+ (triangle_base / 2.0) * Math.sqrt(3)
51
+ else
52
+ image_height / 2.0
53
+ end
54
+ end
55
+
56
+ def triangle_base
57
+ if image_wide?
58
+ (triangle_height / Math.sqrt(3)) * 2.0
59
+ else
60
+ image_width / 2.0
61
+ end
62
+ end
63
+
64
+ def image_height
65
+ image.rows.to_f
66
+ end
67
+
68
+ def image_width
69
+ image.columns.to_f
70
+ end
71
+
72
+ def hexagon_height
73
+ triangle_height * 2
74
+ end
75
+
76
+ def hexagon_width
77
+ triangle_base * 2
78
+ end
79
+
80
+ def clip_path
81
+ Magick::RVG::ClipPath.new.tap do |path|
82
+ path.polygon(
83
+ 0, triangle_height,
84
+ triangle_base, triangle_height,
85
+ triangle_base/2, 0
86
+ )
87
+ end
88
+ end
89
+
90
+ def image_wide?
91
+ (image.columns / image.rows.to_f) > (2.0 / Math.sqrt(3))
92
+ end
93
+
94
+ def image_tall?
95
+ !image_wide?
96
+ end
97
+ end
98
+
99
+ end
@@ -0,0 +1,94 @@
1
+ require 'rvg/rvg'
2
+ require 'hexflex/rvg_image_triangle_vectorizer'
3
+ require 'hexflex/rvg_color_triangle_vectorizer'
4
+
5
+ module Hexflex
6
+ class RvgTemplateVectorizer
7
+
8
+ Magick::RVG::dpi = 72
9
+ attr_reader :triangle_grid
10
+
11
+ def initialize(triangle_grid)
12
+ @triangle_grid = triangle_grid
13
+ end
14
+
15
+ def vectorize
16
+ @vector ||= make_vector
17
+ end
18
+
19
+ private
20
+
21
+ def vector_width
22
+ 10.in
23
+ end
24
+
25
+ def vector_height
26
+ vector_width * Math::sqrt(3) / triangle_base_lengths_per_row
27
+ end
28
+
29
+ def triangle_base
30
+ @triangle_base ||=
31
+ vector_width / triangle_base_lengths_per_row
32
+ end
33
+
34
+ def triangle_height
35
+ @triangle_height ||=
36
+ vector_height / 2.0
37
+ end
38
+
39
+ def triangle_base_lengths_per_row
40
+ (triangle_grid.triangles_per_row + 1) / 2.0
41
+ end
42
+
43
+ def make_vector
44
+ Magick::RVG.new(vector_width, vector_height).tap do |vector|
45
+ vector.background_fill = "white"
46
+
47
+ triangle_grid.grid_triangles.each do |triangle|
48
+ triangle_vector = triangle_vector(triangle)
49
+ vector.use(triangle_vector)
50
+ .translate(lateral_placement(triangle), vertical_placement(triangle))
51
+ .rotate(rotation(triangle), triangle_base, triangle_height)
52
+ .scale(horizonal_normalization(triangle_vector), vertical_normalization(triangle_vector))
53
+ end
54
+ end
55
+ end
56
+
57
+ def triangle_vector(triangle)
58
+ if triangle.image_fill?
59
+ RvgImageTriangleVectorizer.new(triangle).vectorize
60
+ else
61
+ RvgColorTriangleVectorizer.new(triangle).vectorize
62
+ end
63
+ end
64
+
65
+ def lateral_placement(triangle)
66
+ if triangle.first_row?
67
+ lateral_placement = triangle_base * (triangle.position/2).floor
68
+ else
69
+ lateral_placement = (triangle_base * ((triangle.position+1)/2).floor) - triangle_base/2.0
70
+ end
71
+ end
72
+
73
+ def vertical_placement(triangle)
74
+ triangle.first_row? ? 0 : triangle_height
75
+ end
76
+
77
+ def rotation(triangle)
78
+ if triangle.first_row? && triangle.position.odd? || triangle.second_row? && triangle.position.even?
79
+ rotation = 60
80
+ else
81
+ rotation = 0
82
+ end
83
+ end
84
+
85
+ def horizonal_normalization(triangle_vector)
86
+ triangle_base / triangle_vector.base
87
+ end
88
+
89
+ def vertical_normalization(triangle_vector)
90
+ triangle_height / triangle_vector.height
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,19 @@
1
+ require "hexflex/triangle"
2
+
3
+ module Hexflex
4
+ class Side
5
+ attr_accessor :fill, :triangles
6
+
7
+ # make a new side of a hexaflexagon
8
+ # @param [Hash] opts options hash with properties for new side
9
+ # @option opts [Mixed] :face the fill of the triangle, can be a
10
+ # symbol, string, or hex number representing a color
11
+ def initialize(opts = {})
12
+ self.fill = opts[:fill]
13
+ self.triangles = Array.new(6) do |index|
14
+ Triangle.new(fill: fill, index: index)
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,78 @@
1
+ module Hexflex
2
+ class TapePlacer
3
+ attr_reader :canvas, :triangle_vector, :index
4
+
5
+ def initialize(canvas, triangle_vector, index)
6
+ @canvas = canvas
7
+ @triangle_vector = triangle_vector
8
+ @index = index.to_i
9
+
10
+ @triangle_base = @canvas.width / 5.0
11
+ @triangle_half_base = @triangle_base / 2.0
12
+ @triangle_height = @canvas.height / 2.0
13
+ @triangle_height_after_radius = @triangle_height / 3.0
14
+ end
15
+
16
+ def place!
17
+ @canvas.use(triangle_vector)
18
+ .translate(*lateral_placement)
19
+ .translate(*vertical_placement)
20
+ .rotate(*rotation)
21
+ .scale(*normalize_triangle_scale)
22
+ end
23
+
24
+ private
25
+
26
+ def lateral_placement
27
+ if first_row?
28
+ [@triangle_base * (index_on_row/2).floor, 0]
29
+ else
30
+ [(@triangle_base * ((index_on_row+1)/2).floor) - @triangle_base/2.0, 0]
31
+ end
32
+ end
33
+
34
+ def vertical_placement
35
+ [0, (@index/9) * @triangle_height]
36
+ end
37
+
38
+ def rotation
39
+ if rotate?
40
+ [60, @triangle_base, @triangle_height]
41
+ else
42
+ [0]
43
+ end
44
+ end
45
+
46
+ def pre_rotation_vertical_adjustment
47
+ if rotate?
48
+ [0, @triangle_height_after_radius]
49
+ else
50
+ [0, 0]
51
+ end
52
+ end
53
+
54
+ def normalize_triangle_scale
55
+ [
56
+ @triangle_base / triangle_vector.triangle_base,
57
+ @triangle_height / triangle_vector.triangle_height
58
+ ]
59
+ end
60
+
61
+ def rotate?
62
+ index.odd?
63
+ end
64
+
65
+ def first_row?
66
+ index < 9
67
+ end
68
+
69
+ def second_row?
70
+ !second_row?
71
+ end
72
+
73
+ def index_on_row
74
+ index % 9
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,40 @@
1
+ require 'hexflex/base_template'
2
+ require 'hexflex/triangle_grid'
3
+ require 'hexflex/rvg_template_vectorizer'
4
+
5
+ module Hexflex
6
+ class TapeTemplate < BaseTemplate
7
+
8
+ def make_vector
9
+ RvgTemplateVectorizer.new(triangle_grid).vectorize
10
+ end
11
+
12
+ private
13
+
14
+ def triangle_grid
15
+ TriangleGrid.new.tap do |grid|
16
+ grid.place(sides[0].triangles[0], 0, 0)
17
+ grid.place(sides[0].triangles[1], 0, 1)
18
+ grid.place(sides[0].triangles[2], 1, 3)
19
+ grid.place(sides[0].triangles[3], 1, 4)
20
+ grid.place(sides[0].triangles[4], 0, 6)
21
+ grid.place(sides[0].triangles[5], 0, 7)
22
+
23
+ grid.place(sides[2].triangles[0], 0, 8)
24
+ grid.place(sides[2].triangles[1], 1, 0)
25
+ grid.place(sides[2].triangles[2], 0, 2)
26
+ grid.place(sides[2].triangles[3], 0, 3)
27
+ grid.place(sides[2].triangles[4], 1, 5)
28
+ grid.place(sides[2].triangles[5], 1, 6)
29
+
30
+ grid.place(sides[1].triangles[0], 1, 1)
31
+ grid.place(sides[1].triangles[1], 1, 2)
32
+ grid.place(sides[1].triangles[2], 0, 4)
33
+ grid.place(sides[1].triangles[3], 0, 5)
34
+ grid.place(sides[1].triangles[4], 1, 7)
35
+ grid.place(sides[1].triangles[5], 1, 8)
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ module Hexflex
2
+ class Triangle
3
+
4
+ attr_accessor :fill, :index
5
+
6
+ def initialize(opts = {})
7
+ @fill = opts[:fill]
8
+ @index = opts[:index]
9
+ end
10
+
11
+ def image_fill?
12
+ @fill.to_s.include? '.'
13
+ end
14
+
15
+ def color_fill?
16
+ !image_fill?
17
+ end
18
+
19
+ class << self
20
+ def place_holder
21
+ new(fill: "grey")
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ require 'hexflex/grid_triangle'
2
+
3
+ module Hexflex
4
+ class TriangleGrid
5
+
6
+ attr_reader :grid_triangles
7
+
8
+ def initialize
9
+ @grid_triangles = []
10
+ end
11
+
12
+ def place(triangle, row, position)
13
+ @grid_triangles << GridTriangle.new(
14
+ triangle: triangle,
15
+ row: row,
16
+ position: position
17
+ )
18
+ end
19
+
20
+ def triangles_per_row
21
+ grid_triangles.group_by(&:row).values.map(&:count).max
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ require "rvg/rvg"
2
+
3
+ module Hexflex
4
+ class TriangleRvgGroup < SimpleDelegator
5
+
6
+ SIMPLE_TRIANGLE_BASE = Math::sqrt(3)
7
+ SIMPLE_TRIANGLE_HEIGHT = 1.5
8
+
9
+ attr_reader :base, :height
10
+
11
+ def initialize(rvg_group, base:, height:)
12
+ @base = base
13
+ @height = height
14
+ super(rvg_group)
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Hexflex
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,200 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hexflex
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Tom Dunlap
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rmagick
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.13'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.3'
97
+ description: |
98
+ # Hexflex
99
+ [![Build Status](https://travis-ci.org/aauthor/hexflex.svg?branch=master)](https://travis-ci.org/aauthor/hexflex)
100
+
101
+ Hexflex is a Ruby gem and command-line tool for automatically generating
102
+ [hexaflexagon] templates.
103
+
104
+ ## Installation
105
+
106
+ gem install 'hexaflexa'
107
+
108
+ ...or you can put it in your Gemfile.
109
+
110
+ ## Usage
111
+
112
+ ### as a gem in your ruby project
113
+
114
+ To create an [RVG] object containing a vector of the hexaflexagon template:
115
+
116
+ Hexflex.make_template_vector(side_fills: ARRAY_OF_SIDE_FILLS, template: TEMPLATE)
117
+
118
+ To save the hexaflexagon template as a file to the disk:
119
+
120
+ Hexflex.create_template_image!(side_fills: ARRAY_OF_SIDE_FILLS, template: TEMPLATE, output_file_name: OUTPUT)
121
+
122
+ Where:
123
+ - a `SIDE_FILL` is a [standard X color] or path to file for a side of the hexaflexagon. Either three or zero sides should be specified. The default are cyan, magenta, and yellow.
124
+ - `TEMPLATE` is template the form for the hexaflexagon. It can either be "tape" or "glue". The default is "tape".
125
+ - `OUTPUT` is a path to save the hexaflexagon template image. The default is "out.png".
126
+
127
+ ### as a command-line tool
128
+
129
+ hexflex [-s SIDE_FILL -s SIDE_FILL -s SIDE_FILL] [-t TEMPLATE] [-o OUTPUT]
130
+
131
+ See above for definitions of `SIDE_FILL`, `TEMPLATE`, AND `OUTPUT`.
132
+
133
+ [hexaflexagon]: https://en.wikipedia.org/wiki/Flexagon#Trihexaflexagon
134
+ [standard X color]: https://en.wikipedia.org/wiki/X11_color_names
135
+ [RVG]: https://rmagick.github.io/rvg.html
136
+ email:
137
+ - apocryphalauthor@gmail.com
138
+ executables:
139
+ - console
140
+ - hexflex
141
+ - setup
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - ".gitignore"
146
+ - ".rspec"
147
+ - ".ruby_gemset"
148
+ - ".travis.yml"
149
+ - CODE_OF_CONDUCT.md
150
+ - Gemfile
151
+ - LICENSE
152
+ - LICENSE.txt
153
+ - NOTES
154
+ - README.md
155
+ - Rakefile
156
+ - bin/console
157
+ - bin/hexflex
158
+ - bin/setup
159
+ - hexflex.gemspec
160
+ - lib/hexflex.rb
161
+ - lib/hexflex/base_template.rb
162
+ - lib/hexflex/glue_template.rb
163
+ - lib/hexflex/grid_triangle.rb
164
+ - lib/hexflex/hexaflexagon.rb
165
+ - lib/hexflex/rvg_color_triangle_vectorizer.rb
166
+ - lib/hexflex/rvg_image_triangle_vectorizer.rb
167
+ - lib/hexflex/rvg_template_vectorizer.rb
168
+ - lib/hexflex/side.rb
169
+ - lib/hexflex/tape_placer.rb
170
+ - lib/hexflex/tape_template.rb
171
+ - lib/hexflex/triangle.rb
172
+ - lib/hexflex/triangle_grid.rb
173
+ - lib/hexflex/triangle_rvg_group.rb
174
+ - lib/hexflex/version.rb
175
+ - tmp/.gitkeep
176
+ homepage: http://github.com/aauthor/hexflex
177
+ licenses:
178
+ - MIT
179
+ metadata: {}
180
+ post_install_message:
181
+ rdoc_options: []
182
+ require_paths:
183
+ - lib
184
+ required_ruby_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ requirements: []
195
+ rubyforge_project:
196
+ rubygems_version: 2.5.1
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: Generate hexaflexagons to print and fold!
200
+ test_files: []