vector_salad 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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