vector_salad 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +89 -0
- data/Rakefile +2 -0
- data/bin/vector_salad +72 -0
- data/examples/birthday.png +0 -0
- data/examples/birthday.rb +45 -0
- data/examples/birthday.svg +630 -0
- data/examples/boolean_operations.png +0 -0
- data/examples/boolean_operations.rb +23 -0
- data/examples/boolean_operations.svg +151 -0
- data/examples/bp_logo.png +0 -0
- data/examples/bp_logo.rb +18 -0
- data/examples/bp_logo.svg +25 -0
- data/examples/bunny_card.png +0 -0
- data/examples/bunny_card.rb +219 -0
- data/examples/bunny_card.svg +134 -0
- data/examples/chill.png +0 -0
- data/examples/chill.rb +16 -0
- data/examples/chill.svg +86 -0
- data/examples/circle_line_segments.png +0 -0
- data/examples/circle_line_segments.rb +11 -0
- data/examples/circle_line_segments.svg +28 -0
- data/examples/circles.png +0 -0
- data/examples/circles.rb +14 -0
- data/examples/circles.svg +11 -0
- data/examples/clip_operations.png +0 -0
- data/examples/clip_operations.rb +14 -0
- data/examples/clip_operations.svg +8 -0
- data/examples/cog_menu.png +0 -0
- data/examples/cog_menu.rb +32 -0
- data/examples/cog_menu.svg +37 -0
- data/examples/cubic_bezier_handles.png +0 -0
- data/examples/cubic_bezier_handles.rb +21 -0
- data/examples/cubic_bezier_handles.svg +14 -0
- data/examples/cubic_circle.png +0 -0
- data/examples/cubic_circle.rb +26 -0
- data/examples/cubic_circle.svg +29 -0
- data/examples/face.png +0 -0
- data/examples/face.rb +4 -0
- data/examples/face.svg +10 -0
- data/examples/flower.png +0 -0
- data/examples/flower.rb +23 -0
- data/examples/flower.svg +207 -0
- data/examples/fox.png +0 -0
- data/examples/fox.rb +110 -0
- data/examples/fox.svg +31 -0
- data/examples/fresh_vector_salad_gui.png +0 -0
- data/examples/galaxies.png +0 -0
- data/examples/galaxies.rb +60 -0
- data/examples/galaxies.svg +5806 -0
- data/examples/gold_stars.png +0 -0
- data/examples/gold_stars.rb +9 -0
- data/examples/gold_stars.svg +12 -0
- data/examples/paths.png +0 -0
- data/examples/paths.rb +87 -0
- data/examples/paths.svg +13 -0
- data/examples/pepsi_logo.png +0 -0
- data/examples/pepsi_logo.rb +21 -0
- data/examples/pepsi_logo.svg +10 -0
- data/examples/polygons.png +0 -0
- data/examples/polygons.rb +9 -0
- data/examples/polygons.svg +13 -0
- data/examples/quadratic_bezier_handle.png +0 -0
- data/examples/quadratic_bezier_handle.rb +18 -0
- data/examples/quadratic_bezier_handle.svg +13 -0
- data/examples/rects.png +0 -0
- data/examples/rects.rb +10 -0
- data/examples/rects.svg +11 -0
- data/examples/simple_path.png +0 -0
- data/examples/simple_path.rb +29 -0
- data/examples/simple_path.svg +8 -0
- data/examples/space.png +0 -0
- data/examples/space.rb +171 -0
- data/examples/space.svg +46453 -0
- data/examples/spiro_nodes.png +0 -0
- data/examples/spiro_nodes.rb +20 -0
- data/examples/spiro_nodes.svg +13 -0
- data/examples/squares.png +0 -0
- data/examples/squares.rb +14 -0
- data/examples/squares.svg +11 -0
- data/examples/stars.png +0 -0
- data/examples/stars.rb +3 -0
- data/examples/stars.svg +30006 -0
- data/examples/transforms.png +0 -0
- data/examples/transforms.rb +58 -0
- data/examples/transforms.svg +121 -0
- data/examples/triangles.png +0 -0
- data/examples/triangles.rb +8 -0
- data/examples/triangles.svg +9 -0
- data/lib/contracts_contracts.rb +32 -0
- data/lib/vector_salad.rb +5 -0
- data/lib/vector_salad/canvas.rb +27 -0
- data/lib/vector_salad/dsl.rb +41 -0
- data/lib/vector_salad/export_with_magic.rb +29 -0
- data/lib/vector_salad/exporters/base_exporter.rb +92 -0
- data/lib/vector_salad/exporters/svg_exporter.rb +174 -0
- data/lib/vector_salad/interpolate.rb +57 -0
- data/lib/vector_salad/magic.rb +17 -0
- data/lib/vector_salad/mixins/at.rb +28 -0
- data/lib/vector_salad/shape_proxy.rb +14 -0
- data/lib/vector_salad/standard_shapes/basic_shape.rb +29 -0
- data/lib/vector_salad/standard_shapes/circle.rb +64 -0
- data/lib/vector_salad/standard_shapes/clip.rb +51 -0
- data/lib/vector_salad/standard_shapes/custom.rb +28 -0
- data/lib/vector_salad/standard_shapes/difference.rb +28 -0
- data/lib/vector_salad/standard_shapes/exclusion.rb +28 -0
- data/lib/vector_salad/standard_shapes/flip.rb +24 -0
- data/lib/vector_salad/standard_shapes/hexagon.rb +15 -0
- data/lib/vector_salad/standard_shapes/intersection.rb +28 -0
- data/lib/vector_salad/standard_shapes/iso_tri.rb +39 -0
- data/lib/vector_salad/standard_shapes/jitter.rb +33 -0
- data/lib/vector_salad/standard_shapes/move.rb +24 -0
- data/lib/vector_salad/standard_shapes/multi_path.rb +82 -0
- data/lib/vector_salad/standard_shapes/n.rb +112 -0
- data/lib/vector_salad/standard_shapes/oval.rb +51 -0
- data/lib/vector_salad/standard_shapes/path.rb +249 -0
- data/lib/vector_salad/standard_shapes/pentagon.rb +15 -0
- data/lib/vector_salad/standard_shapes/polygon.rb +37 -0
- data/lib/vector_salad/standard_shapes/rect.rb +34 -0
- data/lib/vector_salad/standard_shapes/rotate.rb +24 -0
- data/lib/vector_salad/standard_shapes/scale.rb +34 -0
- data/lib/vector_salad/standard_shapes/square.rb +34 -0
- data/lib/vector_salad/standard_shapes/transform.rb +20 -0
- data/lib/vector_salad/standard_shapes/triangle.rb +15 -0
- data/lib/vector_salad/standard_shapes/union.rb +28 -0
- data/lib/vector_salad/version.rb +3 -0
- data/vector_salad.gemspec +34 -0
- metadata +262 -0
@@ -0,0 +1,57 @@
|
|
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
|
+
module VectorSalad
|
7
|
+
class Interpolate
|
8
|
+
#TOLERANCE = 10 # anything below 50 is roughly good-looking
|
9
|
+
TOLERANCE = 0.2 # anything below 50 is roughly good-looking
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@nodes = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def casteljau(curve)
|
16
|
+
if flat_enough? curve
|
17
|
+
@nodes << curve[3]
|
18
|
+
else
|
19
|
+
halves = subdivide(curve)
|
20
|
+
casteljau(halves[0])
|
21
|
+
casteljau(halves[1])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Early stopping function for the Casteljau algorithm.
|
26
|
+
# Is the curve flat enough for visual purposes?
|
27
|
+
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
|
32
|
+
|
33
|
+
[ax, bx].max + [ay, by].max <= TOLERANCE
|
34
|
+
end
|
35
|
+
|
36
|
+
def midpoint(p, q)
|
37
|
+
[(p[0] + q[0]) / 2.0, (p[1] + q[1]) / 2.0]
|
38
|
+
end
|
39
|
+
|
40
|
+
def midpoints(points)
|
41
|
+
midpoints = Array.new(points.length - 1)
|
42
|
+
midpoints.each_index do |i|
|
43
|
+
midpoints[i] = midpoint(points[i], points[i+1])
|
44
|
+
end
|
45
|
+
midpoints
|
46
|
+
end
|
47
|
+
|
48
|
+
def subdivide(curve)
|
49
|
+
first = midpoints(curve)
|
50
|
+
second = midpoints(first)
|
51
|
+
third = midpoints(second)
|
52
|
+
|
53
|
+
[[curve[0], first[0], second[0], third[0]],
|
54
|
+
[third[0], second[1], first[2], curve[3]]]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'vector_salad/canvas'
|
2
|
+
require 'vector_salad/dsl'
|
3
|
+
Dir.glob(File.expand_path('../standard_shapes/*.rb', __FILE__)).each do |file|
|
4
|
+
require file
|
5
|
+
end
|
6
|
+
|
7
|
+
module VectorSalad
|
8
|
+
module Magic
|
9
|
+
def canvas
|
10
|
+
@vs_canvas ||= VectorSalad::Canvas.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
extend VectorSalad::DSL
|
16
|
+
extend VectorSalad::Magic
|
17
|
+
include VectorSalad::StandardShapes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module VectorSalad
|
2
|
+
module Mixins
|
3
|
+
module At
|
4
|
+
include Contracts
|
5
|
+
|
6
|
+
# Change the absolute x, y coordinates of the shape.
|
7
|
+
Contract Num, Num => Any
|
8
|
+
def [](x, y)
|
9
|
+
@x, @y = x, y
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
# Get the x, y coordinates of the shape.
|
14
|
+
Contract None => Coords
|
15
|
+
def at
|
16
|
+
[@x, @y]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Move the shape relatively.
|
20
|
+
Contract Num, Num => Any
|
21
|
+
def move(x, y)
|
22
|
+
shape = clone
|
23
|
+
shape[shape.at[0] + x, shape.at[1] + y]
|
24
|
+
shape
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'contracts'
|
3
|
+
require 'contracts_contracts'
|
4
|
+
|
5
|
+
module VectorSalad
|
6
|
+
module StandardShapes
|
7
|
+
class BasicShape
|
8
|
+
extend Forwardable
|
9
|
+
include Contracts
|
10
|
+
|
11
|
+
attr_accessor :options
|
12
|
+
|
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
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "vector_salad/standard_shapes/path"
|
2
|
+
require "vector_salad/standard_shapes/n"
|
3
|
+
require "vector_salad/mixins/at"
|
4
|
+
|
5
|
+
module VectorSalad
|
6
|
+
module StandardShapes
|
7
|
+
class Circle < Path
|
8
|
+
include VectorSalad::Mixins::At
|
9
|
+
attr_reader :radius
|
10
|
+
|
11
|
+
# Create a perfectly round circle.
|
12
|
+
#
|
13
|
+
# Examples do
|
14
|
+
# new(100)
|
15
|
+
Contract Pos, {} => Circle
|
16
|
+
def initialize(radius, **options)
|
17
|
+
@options = options
|
18
|
+
@radius = radius
|
19
|
+
@x, @y = 0, 0
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_path
|
24
|
+
# http://stackoverflow.com/a/13338311
|
25
|
+
# c = 4 * (Math.sqrt(2) - 1) / 3
|
26
|
+
# c = 0.5522847498307936
|
27
|
+
#
|
28
|
+
# http://spencermortensen.com/articles/bezier-circle/
|
29
|
+
c = 0.551915024494
|
30
|
+
d = c * @radius
|
31
|
+
Path.new(
|
32
|
+
N.n(@x + @radius, @y),
|
33
|
+
N.c(@x + @radius, @y + d),
|
34
|
+
N.c(@x + d, @y + @radius),
|
35
|
+
N.n(@x, @y + @radius),
|
36
|
+
N.c(@x - d, @y + @radius),
|
37
|
+
N.c(@x - @radius, @y + d),
|
38
|
+
N.n(@x - @radius, @y),
|
39
|
+
N.c(@x - @radius, @y - d),
|
40
|
+
N.c(@x - d, @y - @radius),
|
41
|
+
N.n(@x, @y - @radius),
|
42
|
+
N.c(@x + d, @y - @radius),
|
43
|
+
N.c(@x + @radius, @y - d),
|
44
|
+
N.n(@x + @radius, @y),
|
45
|
+
**@options
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_simple_path(fn = nil)
|
50
|
+
fn ||= (@radius * 2).ceil
|
51
|
+
|
52
|
+
nodes = []
|
53
|
+
arc = (2.0 * Math::PI) / fn
|
54
|
+
fn.times do |t|
|
55
|
+
a = arc * t
|
56
|
+
x = @radius * Math.cos(a) + @x
|
57
|
+
y = @radius * Math.sin(a) + @y
|
58
|
+
nodes << N.n(x, y)
|
59
|
+
end
|
60
|
+
Path.new(*nodes, **@options)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'clipper'
|
2
|
+
|
3
|
+
require 'vector_salad/dsl'
|
4
|
+
require 'vector_salad/canvas'
|
5
|
+
require 'vector_salad/standard_shapes/multi_path'
|
6
|
+
|
7
|
+
module VectorSalad
|
8
|
+
module StandardShapes
|
9
|
+
class Clip < BasicShape
|
10
|
+
include VectorSalad::DSL
|
11
|
+
include VectorSalad::StandardShapes
|
12
|
+
|
13
|
+
# Perform a clipping operation on a set of paths or shapes.
|
14
|
+
# The first path is used as the subject, subsequent paths are applied to
|
15
|
+
# the first using the specified operation.
|
16
|
+
#
|
17
|
+
# It's easier to use one of the subclasses;
|
18
|
+
# (see {Difference}, {Intersection}, {Union}, {Exclusion}).
|
19
|
+
Contract Or[*%i(difference intersection union xor)], {}, Proc => MultiPath
|
20
|
+
def initialize(operation, **options, &block)
|
21
|
+
instance_eval(&block) # canvas is populated
|
22
|
+
|
23
|
+
clipper = Clipper::Clipper.new
|
24
|
+
|
25
|
+
i = 0
|
26
|
+
canvas.each do |shape|
|
27
|
+
method = i == 0 ? 'subject' : 'clip'
|
28
|
+
path = shape.to_simple_path.to_a
|
29
|
+
if path[0][0].instance_of? Array # MultiPath
|
30
|
+
clipper.send("add_#{method}_polygons".to_sym, path)
|
31
|
+
else # Path
|
32
|
+
clipper.send("add_#{method}_polygon".to_sym, path)
|
33
|
+
end
|
34
|
+
i += 1
|
35
|
+
end
|
36
|
+
|
37
|
+
@path = MultiPath.new(
|
38
|
+
*clipper.send(operation, :non_zero, :non_zero), **options
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def canvas
|
43
|
+
@canvas ||= VectorSalad::Canvas.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_path
|
47
|
+
@path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,28 @@
|
|
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
|
+
|
6
|
+
module VectorSalad
|
7
|
+
module StandardShapes
|
8
|
+
class Custom < BasicShape
|
9
|
+
def initialize(name, &block)
|
10
|
+
#instance_eval(&block)
|
11
|
+
::VectorSalad::StandardShapes.const_set(name.to_s.capitalize.to_sym, Class.new(BasicShape) do
|
12
|
+
include VectorSalad::DSL
|
13
|
+
include VectorSalad::StandardShapes
|
14
|
+
|
15
|
+
define_method(:initialize, &block)
|
16
|
+
|
17
|
+
def canvas
|
18
|
+
@canvas ||= VectorSalad::Canvas.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_path
|
22
|
+
canvas[0]
|
23
|
+
end
|
24
|
+
end)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'vector_salad/standard_shapes/clip'
|
2
|
+
require 'vector_salad/standard_shapes/multi_path'
|
3
|
+
|
4
|
+
module VectorSalad
|
5
|
+
module StandardShapes
|
6
|
+
class Difference < Clip
|
7
|
+
# Subtract paths.
|
8
|
+
# The first path is used as the subject, subsequent paths are subtracted
|
9
|
+
# from the first.
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
#
|
13
|
+
# Difference.new do
|
14
|
+
# canvas << Path.new([0,0], [90,90], [0,90])
|
15
|
+
# canvas << Path.new([50,0], [95,0], [50, 70])
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # Using DSL:
|
19
|
+
# difference do
|
20
|
+
# path([0,0], [90,90], [0,90])
|
21
|
+
# path([50,0], [95,0], [50, 70])
|
22
|
+
# end
|
23
|
+
def initialize(**options, &block)
|
24
|
+
super(:difference, **options, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'vector_salad/standard_shapes/clip'
|
2
|
+
require 'vector_salad/standard_shapes/multi_path'
|
3
|
+
|
4
|
+
module VectorSalad
|
5
|
+
module StandardShapes
|
6
|
+
class Exclusion < Clip
|
7
|
+
# Exclude paths.
|
8
|
+
# The first path is used as the subject, subsequent paths are excluded
|
9
|
+
# from the first.
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
#
|
13
|
+
# Exclusion.new do
|
14
|
+
# canvas << Path.new([0,0], [90,90], [0,90])
|
15
|
+
# canvas << Path.new([50,0], [95,0], [50, 70])
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # Using DSL:
|
19
|
+
# exclusion do
|
20
|
+
# path([0,0], [90,90], [0,90])
|
21
|
+
# path([50,0], [95,0], [50, 70])
|
22
|
+
# end
|
23
|
+
def initialize(**options, &block)
|
24
|
+
super(:xor, **options, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'vector_salad/standard_shapes/transform'
|
2
|
+
|
3
|
+
module VectorSalad
|
4
|
+
module StandardShapes
|
5
|
+
class Flip < Transform
|
6
|
+
# Flip the contained shapes on the specified axis.
|
7
|
+
#
|
8
|
+
# Examples:
|
9
|
+
#
|
10
|
+
# flip(:x) do
|
11
|
+
# triangle(30, at: [50, -50])
|
12
|
+
# pentagon(40, at: [50, -100])
|
13
|
+
# end
|
14
|
+
Contract Or[:x, :y], { canvas: VectorSalad::Canvas }, Proc => Any
|
15
|
+
def initialize(axis, canvas:, **_options, &block)
|
16
|
+
instance_eval(&block) # inner_canvas is populated
|
17
|
+
|
18
|
+
@canvas.each do |shape|
|
19
|
+
canvas << shape.flip(axis)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'vector_salad/standard_shapes/polygon'
|
2
|
+
|
3
|
+
module VectorSalad
|
4
|
+
module StandardShapes
|
5
|
+
class Hexagon < Polygon
|
6
|
+
# Create a regular hexagon.
|
7
|
+
#
|
8
|
+
# Examples:
|
9
|
+
# new(100)
|
10
|
+
def initialize(radius, **options)
|
11
|
+
super(6, radius, **options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'vector_salad/standard_shapes/clip'
|
2
|
+
require 'vector_salad/standard_shapes/multi_path'
|
3
|
+
|
4
|
+
module VectorSalad
|
5
|
+
module StandardShapes
|
6
|
+
class Intersection < Clip
|
7
|
+
# Intersect paths.
|
8
|
+
# The first path is used as the subject, subsequent paths are intersected
|
9
|
+
# with the first.
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
#
|
13
|
+
# Intersection.new do
|
14
|
+
# canvas << Path.new([0,0], [90,90], [0,90])
|
15
|
+
# canvas << Path.new([50,0], [95,0], [50, 70])
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # Using DSL:
|
19
|
+
# intersection do
|
20
|
+
# path([0,0], [90,90], [0,90])
|
21
|
+
# path([50,0], [95,0], [50, 70])
|
22
|
+
# end
|
23
|
+
def initialize(**options, &block)
|
24
|
+
super(:intersection, **options, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "vector_salad/standard_shapes/path"
|
2
|
+
require "vector_salad/standard_shapes/n"
|
3
|
+
require "vector_salad/mixins/at"
|
4
|
+
|
5
|
+
module VectorSalad
|
6
|
+
module StandardShapes
|
7
|
+
class IsoTri < BasicShape
|
8
|
+
include VectorSalad::Mixins::At
|
9
|
+
|
10
|
+
attr_reader :height
|
11
|
+
|
12
|
+
# Create an isosceles or right-angle triangle.
|
13
|
+
#
|
14
|
+
# Examples:
|
15
|
+
#
|
16
|
+
# new(100)
|
17
|
+
# new(100, 150)
|
18
|
+
#
|
19
|
+
# @param width (defaults to height*2).
|
20
|
+
Contract Args[Pos], {} => IsoTri
|
21
|
+
def initialize(width = nil, height, **options)
|
22
|
+
width = height * 2 if width.nil?
|
23
|
+
@width, @height = width, height
|
24
|
+
@options = options
|
25
|
+
@x, @y = 0, 0
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_path
|
30
|
+
Path.new(
|
31
|
+
N.n(@x, @y),
|
32
|
+
N.n(@x - @width / 2, @y + @height),
|
33
|
+
N.n(@x + @width / 2, @y + @height),
|
34
|
+
**@options
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|