vector_salad 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.yardopts +1 -0
  4. data/README.md +6 -5
  5. data/examples/birthday.png +0 -0
  6. data/examples/birthday.svg +604 -604
  7. data/examples/bunny_card.png +0 -0
  8. data/examples/bunny_card.svg +110 -110
  9. data/examples/circles.png +0 -0
  10. data/examples/circles.svg +2 -2
  11. data/examples/rects.rb +4 -4
  12. data/examples/squares.png +0 -0
  13. data/examples/squares.rb +4 -4
  14. data/examples/squares.svg +6 -6
  15. data/lib/contracts_contracts.rb +4 -4
  16. data/lib/vector_salad/canvas.rb +17 -0
  17. data/lib/vector_salad/dsl.rb +29 -28
  18. data/lib/vector_salad/export_with_magic.rb +1 -0
  19. data/lib/vector_salad/exporters/base_exporter.rb +1 -0
  20. data/lib/vector_salad/exporters/svg_exporter.rb +30 -22
  21. data/lib/vector_salad/interpolate.rb +13 -12
  22. data/lib/vector_salad/magic.rb +4 -3
  23. data/lib/vector_salad/mixins/at.rb +1 -0
  24. data/lib/vector_salad/mixins/mixins.rb +5 -0
  25. data/lib/vector_salad/monkeypatches.rb +4 -0
  26. data/lib/vector_salad/shape_proxy.rb +1 -0
  27. data/lib/vector_salad/standard_shapes/basic_shape.rb +25 -17
  28. data/lib/vector_salad/standard_shapes/circle.rb +9 -3
  29. data/lib/vector_salad/standard_shapes/clip.rb +10 -9
  30. data/lib/vector_salad/standard_shapes/custom.rb +23 -4
  31. data/lib/vector_salad/standard_shapes/difference.rb +5 -6
  32. data/lib/vector_salad/standard_shapes/exclusion.rb +5 -6
  33. data/lib/vector_salad/standard_shapes/flip.rb +8 -2
  34. data/lib/vector_salad/standard_shapes/heart.rb +3 -1
  35. data/lib/vector_salad/standard_shapes/hexagon.rb +3 -2
  36. data/lib/vector_salad/standard_shapes/intersection.rb +5 -6
  37. data/lib/vector_salad/standard_shapes/iso_tri.rb +3 -2
  38. data/lib/vector_salad/standard_shapes/jitter.rb +10 -7
  39. data/lib/vector_salad/standard_shapes/move.rb +7 -1
  40. data/lib/vector_salad/standard_shapes/multi_path.rb +17 -6
  41. data/lib/vector_salad/standard_shapes/n.rb +6 -3
  42. data/lib/vector_salad/standard_shapes/oval.rb +3 -1
  43. data/lib/vector_salad/standard_shapes/path.rb +28 -14
  44. data/lib/vector_salad/standard_shapes/pentagon.rb +2 -1
  45. data/lib/vector_salad/standard_shapes/polygon.rb +3 -1
  46. data/lib/vector_salad/standard_shapes/rect.rb +3 -1
  47. data/lib/vector_salad/standard_shapes/rotate.rb +3 -5
  48. data/lib/vector_salad/standard_shapes/scale.rb +8 -7
  49. data/lib/vector_salad/standard_shapes/square.rb +6 -21
  50. data/lib/vector_salad/standard_shapes/standard_shapes.rb +6 -0
  51. data/lib/vector_salad/standard_shapes/transform.rb +6 -5
  52. data/lib/vector_salad/standard_shapes/triangle.rb +3 -2
  53. data/lib/vector_salad/standard_shapes/union.rb +5 -6
  54. data/lib/vector_salad/version.rb +1 -1
  55. data/lib/vector_salad.rb +1 -1
  56. metadata +4 -2
@@ -1,6 +1,7 @@
1
1
  require "stringio"
2
2
 
3
3
  module VectorSalad
4
+ # @api private
4
5
  module Exporters
5
6
  class BaseExporter
6
7
  attr_reader :canvas
@@ -1,13 +1,14 @@
1
- require_relative 'base_exporter.rb'
2
- Dir.glob(File.expand_path('../../standard_shapes/*.rb', __FILE__)).each do |file|
3
- require file
1
+ require_relative "base_exporter.rb"
2
+ Dir.glob(File.expand_path("../../standard_shapes/*.rb", __FILE__)).each do |f|
3
+ require f
4
4
  end
5
5
 
6
6
  module VectorSalad
7
7
  module Exporters
8
+ # @api private
8
9
  class SvgExporter < BaseExporter
9
10
  def header
10
- puts <<-END.gsub(/^ {10}/, '')
11
+ puts <<-END.gsub(/^ {10}/, "")
11
12
  <svg version="1.1"
12
13
  xmlns="http://www.w3.org/2000/svg"
13
14
  width="#{canvas_width}"
@@ -16,13 +17,13 @@ module VectorSalad
16
17
  end
17
18
 
18
19
  def footer
19
- puts '</svg>'
20
+ puts "</svg>"
20
21
  end
21
22
 
22
23
  def self.options(options)
23
- out = ''
24
+ out = ""
24
25
  options.each do |k, v|
25
- out << " #{k.to_s.gsub(/_/, ?-)}=\"#{v.to_s.gsub(/_/, ?-)}\""
26
+ out << " #{k.to_s.gsub(/_/, '-')}=\"#{v.to_s.gsub(/_/, '-')}\""
26
27
  end
27
28
  out
28
29
  end
@@ -30,59 +31,62 @@ module VectorSalad
30
31
  end
31
32
  end
32
33
 
33
-
34
34
  module VectorSalad
35
35
  module StandardShapes
36
36
  class Path
37
+ # Export the shape to an svg string
37
38
  def to_svg
38
39
  svg = '<path d="'
39
40
  svg << to_svg_d_attribute
40
41
  svg << '"'
41
42
  svg << VectorSalad::Exporters::SvgExporter.options(@options)
42
- svg << '/>'
43
+ svg << "/>"
43
44
  end
44
45
 
46
+ # @api private
45
47
  def to_svg_d_attribute
46
48
  nodes = to_bezier_path.nodes
47
- svg = ''
49
+ svg = ""
48
50
  svg << "M #{nodes[0].x} #{nodes[0].y}"
49
51
 
50
52
  nodes[1..-1].each_index do |j|
51
- i = j+1
53
+ i = j + 1
52
54
 
53
55
  n = nodes[i]
54
56
  case n.type
55
57
  when :cubic
56
- if nodes[i-1].type == :node
58
+ if nodes[i - 1].type == :node
57
59
  svg << " C #{n.x} #{n.y}"
58
- elsif nodes[i-2].type == :node && nodes[i-1].type == :cubic
60
+ elsif nodes[i - 2].type == :node && nodes[i - 1].type == :cubic
59
61
  svg << ", #{n.x} #{n.y}"
60
62
  end
61
63
  when :quadratic
62
64
  svg << " Q #{n.x} #{n.y}"
63
65
  when :node
64
- if nodes[i-1].type == :cubic || nodes[i-1].type == :quadratic
66
+ if nodes[i - 1].type == :cubic || nodes[i - 1].type == :quadratic
65
67
  svg << ", #{n.x} #{n.y}"
66
- elsif nodes[i-1].type == :node
68
+ elsif nodes[i - 1].type == :node
67
69
  svg << " L #{n.x} #{n.y}"
68
70
  end
69
71
  else
70
- raise "The SVG exporter doesn't support #{n.type} node type."
72
+ fail "The SVG exporter doesn't support #{n.type} node type."
71
73
  end
72
74
  end
73
75
 
74
- svg << ' Z' if @closed
76
+ svg << " Z" if @closed
75
77
  svg
76
78
  end
77
79
  end
78
80
 
79
81
  class BasicShape
82
+ # Export the shape to an svg string
80
83
  def to_svg
81
84
  to_path.to_svg
82
85
  end
83
86
  end
84
87
 
85
88
  class MultiPath
89
+ # Export the shape to an svg string
86
90
  def to_svg
87
91
  svg = '<path d="'
88
92
  paths.each do |path|
@@ -90,42 +94,46 @@ module VectorSalad
90
94
  end
91
95
  svg << '"'
92
96
  svg << VectorSalad::Exporters::SvgExporter.options(@options)
93
- svg << '/>'
97
+ svg << "/>"
94
98
  end
95
99
  end
96
100
 
97
101
  class Circle
102
+ # Export the shape to an svg string
98
103
  def to_svg
99
104
  svg = "<circle cx=\"#{at[0]}\" cy=\"#{at[1]}\" r=\"#{radius}\""
100
105
  svg << VectorSalad::Exporters::SvgExporter.options(@options)
101
- svg << '/>'
106
+ svg << "/>"
102
107
  end
103
108
  end
104
109
 
105
110
  class Oval
111
+ # Export the shape to an svg string
106
112
  def to_svg
107
113
  svg = "<ellipse cx=\"#{at[0]}\" cy=\"#{at[1]}\""
108
114
  svg << " rx=\"#{width}\" ry=\"#{height}\""
109
115
  svg << VectorSalad::Exporters::SvgExporter.options(@options)
110
- svg << '/>'
116
+ svg << "/>"
111
117
  end
112
118
  end
113
119
 
114
120
  class Square
121
+ # Export the shape to an svg string
115
122
  def to_svg
116
123
  svg = "<rect x=\"#{at[0]}\" y=\"#{at[1]}\""
117
124
  svg << " width=\"#{size}\" height=\"#{size}\""
118
125
  svg << VectorSalad::Exporters::SvgExporter.options(@options)
119
- svg << '/>'
126
+ svg << "/>"
120
127
  end
121
128
  end
122
129
 
123
130
  class Rect
131
+ # Export the shape to an svg string
124
132
  def to_svg
125
133
  svg = "<rect x=\"#{at[0]}\" y=\"#{at[1]}\""
126
134
  svg << " width=\"#{width}\" height=\"#{height}\""
127
135
  svg << VectorSalad::Exporters::SvgExporter.options(@options)
128
- svg << '/>'
136
+ svg << "/>"
129
137
  end
130
138
  end
131
139
  end
@@ -1,11 +1,12 @@
1
- # Code adapted from:
2
- # http://jeremykun.com/2013/05/11/bezier-curves-and-picasso/
3
- #
4
- # Interpolate a cubic bezier spline into straight line segments,
5
- # using the De Casteljau algorithm.
6
1
  module VectorSalad
2
+ # Code adapted from:
3
+ # http://jeremykun.com/2013/05/11/bezier-curves-and-picasso/
4
+ #
5
+ # Interpolate a cubic bezier spline into straight line segments,
6
+ # using the De Casteljau algorithm.
7
+ #
8
+ # @api private
7
9
  class Interpolate
8
- #TOLERANCE = 10 # anything below 50 is roughly good-looking
9
10
  TOLERANCE = 0.2 # anything below 50 is roughly good-looking
10
11
 
11
12
  def initialize
@@ -25,10 +26,10 @@ module VectorSalad
25
26
  # Early stopping function for the Casteljau algorithm.
26
27
  # Is the curve flat enough for visual purposes?
27
28
  def flat_enough?(curve)
28
- ax = (3.0*curve[1][0] - 2.0*curve[0][0] - curve[3][0])**2
29
- ay = (3.0*curve[1][1] - 2.0*curve[0][1] - curve[3][1])**2
30
- bx = (3.0*curve[2][0] - curve[0][0] - 2.0*curve[3][0])**2
31
- by = (3.0*curve[2][1] - curve[0][1] - 2.0*curve[3][1])**2
29
+ ax = (3.0 * curve[1][0] - 2.0 * curve[0][0] - curve[3][0])**2
30
+ ay = (3.0 * curve[1][1] - 2.0 * curve[0][1] - curve[3][1])**2
31
+ bx = (3.0 * curve[2][0] - curve[0][0] - 2.0 * curve[3][0])**2
32
+ by = (3.0 * curve[2][1] - curve[0][1] - 2.0 * curve[3][1])**2
32
33
 
33
34
  [ax, bx].max + [ay, by].max <= TOLERANCE
34
35
  end
@@ -40,7 +41,7 @@ module VectorSalad
40
41
  def midpoints(points)
41
42
  midpoints = Array.new(points.length - 1)
42
43
  midpoints.each_index do |i|
43
- midpoints[i] = midpoint(points[i], points[i+1])
44
+ midpoints[i] = midpoint(points[i], points[i + 1])
44
45
  end
45
46
  midpoints
46
47
  end
@@ -51,7 +52,7 @@ module VectorSalad
51
52
  third = midpoints(second)
52
53
 
53
54
  [[curve[0], first[0], second[0], third[0]],
54
- [third[0], second[1], first[2], curve[3]]]
55
+ [third[0], second[1], first[2], curve[3]]]
55
56
  end
56
57
  end
57
58
  end
@@ -1,10 +1,11 @@
1
- require 'vector_salad/canvas'
2
- require 'vector_salad/dsl'
3
- Dir.glob(File.expand_path('../standard_shapes/*.rb', __FILE__)).each do |file|
1
+ require "vector_salad/canvas"
2
+ require "vector_salad/dsl"
3
+ Dir.glob(File.expand_path("../standard_shapes/*.rb", __FILE__)).each do |file|
4
4
  require file
5
5
  end
6
6
 
7
7
  module VectorSalad
8
+ # @api private
8
9
  module Magic
9
10
  def canvas
10
11
  @vs_canvas ||= VectorSalad::Canvas.new
@@ -1,5 +1,6 @@
1
1
  module VectorSalad
2
2
  module Mixins
3
+ # Mixin for positioning shapes.
3
4
  module At
4
5
  include Contracts
5
6
 
@@ -0,0 +1,5 @@
1
+ module VectorSalad
2
+ # Common methods for mixing in to shapes.
3
+ module Mixins
4
+ end
5
+ end
@@ -1,10 +1,14 @@
1
+ # Monkeypatches to core Ruby Fixnum class
1
2
  class Fixnum
3
+ # Multiply number self by UNIT constant
2
4
  def ~
3
5
  self * UNIT
4
6
  end
5
7
  end
6
8
 
9
+ # Monkeypatches to core Ruby Float class
7
10
  class Float
11
+ # Multiply number self by UNIT constant
8
12
  def ~
9
13
  self * UNIT
10
14
  end
@@ -1,4 +1,5 @@
1
1
  module VectorSalad
2
+ # @api private
2
3
  class ShapeProxy
3
4
  attr_reader :shape
4
5
 
@@ -1,29 +1,37 @@
1
- require 'forwardable'
2
- require 'contracts'
3
- require 'contracts_contracts'
1
+ require "forwardable"
2
+ require "contracts"
3
+ require "contracts_contracts"
4
4
 
5
5
  module VectorSalad
6
6
  module StandardShapes
7
+ # All shapes must inherit from BasicShape,
8
+ # with the exception of N node.
9
+ # It alows many usefulmethods in {Path} to be called on the shape
10
+ # by delegating them, so see {Path} for more information on all the
11
+ # methods that shapes can use.
12
+ #
13
+ # You can't use BasicShape directly.
7
14
  class BasicShape
8
15
  extend Forwardable
9
16
  include Contracts
10
17
 
11
18
  attr_accessor :options
12
19
 
13
- delegate [
14
- :flip,
15
- :flip_x,
16
- :flip_y,
17
- :rotate,
18
- :move,
19
- :jitter,
20
- :scale,
21
- :to_simple_path,
22
- :to_bezier_path,
23
- :to_cubic_path,
24
- :to_multi_path,
25
- :to_a
26
- ] => :to_path
20
+ # @!macro [attach] property
21
+ # @!method $2
22
+ # @see Path#$2
23
+ def_instance_delegator :to_path, :flip
24
+ def_instance_delegator :to_path, :flip_x
25
+ def_instance_delegator :to_path, :flip_y
26
+ def_instance_delegator :to_path, :rotate
27
+ def_instance_delegator :to_path, :move
28
+ def_instance_delegator :to_path, :jitter
29
+ def_instance_delegator :to_path, :scale
30
+ def_instance_delegator :to_path, :to_simple_path
31
+ def_instance_delegator :to_path, :to_bezier_path
32
+ def_instance_delegator :to_path, :to_cubic_path
33
+ def_instance_delegator :to_path, :to_multi_path
34
+ def_instance_delegator :to_path, :to_a
27
35
  end
28
36
  end
29
37
  end
@@ -4,13 +4,14 @@ require "vector_salad/mixins/at"
4
4
 
5
5
  module VectorSalad
6
6
  module StandardShapes
7
- class Circle < Path
7
+ # Perfect circle shape.
8
+ class Circle < BasicShape # Used to inherit from Path, check still works
8
9
  include VectorSalad::Mixins::At
9
10
  attr_reader :radius
10
11
 
11
12
  # Create a perfectly round circle.
12
13
  #
13
- # Examples do
14
+ # @example
14
15
  # new(100)
15
16
  Contract Pos, {} => Circle
16
17
  def initialize(radius, **options)
@@ -20,6 +21,7 @@ module VectorSalad
20
21
  self
21
22
  end
22
23
 
24
+ # Convert the shape to a path
23
25
  def to_path
24
26
  # http://stackoverflow.com/a/13338311
25
27
  # c = 4 * (Math.sqrt(2) - 1) / 3
@@ -46,8 +48,12 @@ module VectorSalad
46
48
  )
47
49
  end
48
50
 
51
+ # Flatten the circle into many small straight line segments.
52
+ #
53
+ # @param fn The number of segments
54
+ Contract Maybe[Num] => Path
49
55
  def to_simple_path(fn = nil)
50
- fn ||= (@radius * 2).ceil
56
+ fn ||= (@radius * 4).ceil
51
57
 
52
58
  nodes = []
53
59
  arc = (2.0 * Math::PI) / fn
@@ -1,21 +1,20 @@
1
- require 'clipper'
1
+ require "clipper"
2
2
 
3
- require 'vector_salad/dsl'
4
- require 'vector_salad/canvas'
5
- require 'vector_salad/standard_shapes/multi_path'
3
+ require "vector_salad/dsl"
4
+ require "vector_salad/canvas"
5
+ require "vector_salad/standard_shapes/multi_path"
6
6
 
7
7
  module VectorSalad
8
8
  module StandardShapes
9
+ # Perform a clipping operation on the contained paths or shapes.
10
+ # It's easier to use one of the subclasses;
11
+ # (see {Difference}, {Intersection}, {Union}, {Exclusion}).
9
12
  class Clip < BasicShape
10
13
  include VectorSalad::DSL
11
14
  include VectorSalad::StandardShapes
12
15
 
13
- # Perform a clipping operation on a set of paths or shapes.
14
16
  # The first path is used as the subject, subsequent paths are applied to
15
17
  # the first using the specified operation.
16
- #
17
- # It's easier to use one of the subclasses;
18
- # (see {Difference}, {Intersection}, {Union}, {Exclusion}).
19
18
  Contract Or[*%i(difference intersection union xor)], {}, Proc => MultiPath
20
19
  def initialize(operation, **options, &block)
21
20
  instance_eval(&block) # canvas is populated
@@ -24,7 +23,7 @@ module VectorSalad
24
23
 
25
24
  i = 0
26
25
  canvas.each do |shape|
27
- method = i == 0 ? 'subject' : 'clip'
26
+ method = i == 0 ? "subject" : "clip"
28
27
  path = shape.to_simple_path.to_a
29
28
  if path[0][0].instance_of? Array # MultiPath
30
29
  clipper.send("add_#{method}_polygons".to_sym, path)
@@ -39,10 +38,12 @@ module VectorSalad
39
38
  )
40
39
  end
41
40
 
41
+ # The canvas the clipping is done on.
42
42
  def canvas
43
43
  @canvas ||= VectorSalad::Canvas.new
44
44
  end
45
45
 
46
+ # Convert the shape to a path
46
47
  def to_path
47
48
  @path
48
49
  end
@@ -1,11 +1,30 @@
1
- require 'vector_salad/standard_shapes/path'
2
- require 'vector_salad/standard_shapes/n'
3
- require 'vector_salad/dsl'
4
- require 'vector_salad/canvas'
1
+ require "vector_salad/standard_shapes/path"
2
+ require "vector_salad/standard_shapes/n"
3
+ require "vector_salad/dsl"
4
+ require "vector_salad/canvas"
5
5
 
6
6
  module VectorSalad
7
7
  module StandardShapes
8
+ # Make your own custom shape.
8
9
  class Custom < BasicShape
10
+ # You must name your custom shape to be able to use it later.
11
+ # The block passed to custom shape is what will create your shape,
12
+ # it should create just a single shape or MultiPath.
13
+ # This means you should use {MultiPath} or one of the {Clip} operations
14
+ # which return MultiPaths to create complex shapes.
15
+ #
16
+ # @example Using DSL:
17
+ # custom(:donut) do |size| # name shape and specify parameters
18
+ # difference do # return a single shape
19
+ # circle(size / 2)
20
+ # circle(size / 3)
21
+ # end
22
+ # end
23
+ #
24
+ # donut(100)
25
+ #
26
+ # @param name The name of your shape in snake_case
27
+ Contract Symbol, {}, Proc => Custom
9
28
  def initialize(name, **options, &block)
10
29
  ::VectorSalad::StandardShapes.const_set(name.to_s.capitalize.to_sym, Class.new(BasicShape) do
11
30
  include VectorSalad::DSL