wicket 0.0.2 → 0.1.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -5
- data/Gemfile +2 -0
- data/README.md +132 -12
- data/changelog.md +12 -0
- data/lib/wicket.rb +22 -0
- data/lib/wicket/cartesian.rb +23 -0
- data/lib/wicket/command.rb +47 -14
- data/lib/wicket/commands/bezier_curve.rb +75 -0
- data/lib/wicket/commands/c.rb +24 -0
- data/lib/wicket/commands/cubic_bezier.rb +20 -0
- data/lib/wicket/commands/h.rb +10 -14
- data/lib/wicket/commands/l.rb +6 -18
- data/lib/wicket/commands/m.rb +6 -18
- data/lib/wicket/commands/q.rb +21 -0
- data/lib/wicket/commands/quadratic_bezier.rb +15 -0
- data/lib/wicket/commands/s.rb +37 -0
- data/lib/wicket/commands/t.rb +34 -0
- data/lib/wicket/commands/v.rb +10 -13
- data/lib/wicket/commands/z.rb +9 -9
- data/lib/wicket/configuration.rb +26 -0
- data/lib/wicket/coordinate.rb +21 -0
- data/lib/wicket/subpath.rb +7 -3
- data/lib/wicket/subpoint.rb +39 -0
- data/lib/wicket/svg_path.rb +25 -10
- data/lib/wicket/utilities.rb +60 -0
- data/lib/wicket/version.rb +1 -1
- data/spec/coordinate_spec.rb +28 -0
- data/spec/svg_path_spec.rb +26 -107
- data/spec/test_cases.yml +91 -0
- data/spec/utilities_spec.rb +91 -0
- metadata +21 -2
@@ -0,0 +1,24 @@
|
|
1
|
+
module Wicket
|
2
|
+
module Commands
|
3
|
+
class C < Command
|
4
|
+
ARGS = 6
|
5
|
+
ARG_LIST = [:c1x,:c1y,:c2x,:c2y,:x,:y]
|
6
|
+
include CubicBezier
|
7
|
+
|
8
|
+
def initialize(absolute,cursor_start,subpath,opts,c1x,c1y,c2x,c2y,x,y)
|
9
|
+
@absolute = absolute
|
10
|
+
@cursor_start = cursor_start
|
11
|
+
@subpath = subpath
|
12
|
+
@opts = opts
|
13
|
+
@c1x = c1x
|
14
|
+
@c1y = c1y
|
15
|
+
@c2x = c2x
|
16
|
+
@c2y = c2y
|
17
|
+
@x = x
|
18
|
+
@y = y
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Wicket
|
2
|
+
module Commands
|
3
|
+
module CubicBezier
|
4
|
+
include BezierCurve
|
5
|
+
|
6
|
+
def control_points
|
7
|
+
[cursor_start,c1,c2,cursor_end]
|
8
|
+
end
|
9
|
+
|
10
|
+
def c1
|
11
|
+
@absolute ? Coordinate.new(@c1x,@c1y) : @cursor_start.absolutize(@c1x,@c1y)
|
12
|
+
end
|
13
|
+
|
14
|
+
def c2
|
15
|
+
@absolute ? Coordinate.new(@c2x,@c2y) : @cursor_start.absolutize(@c2x,@c2y)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/wicket/commands/h.rb
CHANGED
@@ -1,29 +1,25 @@
|
|
1
1
|
module Wicket
|
2
2
|
module Commands
|
3
3
|
class H < Command
|
4
|
+
ARGS = 1
|
5
|
+
ARG_LIST = [:x]
|
4
6
|
|
5
|
-
def
|
6
|
-
1
|
7
|
-
end
|
8
|
-
|
9
|
-
def initialize(absolute,cursor_start,x)
|
7
|
+
def initialize(absolute,cursor_start,subpath,opts,x)
|
10
8
|
@absolute = absolute
|
11
9
|
@cursor_start = cursor_start
|
10
|
+
@subpath = subpath
|
11
|
+
@opts = opts
|
12
12
|
@x = x
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def absolute_y
|
16
|
+
@cursor_start.y
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
else
|
23
|
-
{:x => (@cursor_start[:x] + @x), :y => @cursor_start[:y]}
|
24
|
-
end
|
19
|
+
def to_svg(opts={})
|
20
|
+
o = @opts.merge(opts)
|
21
|
+
"H #{Utilities.format(absolute_x,o)}"
|
25
22
|
end
|
26
|
-
|
27
23
|
end
|
28
24
|
end
|
29
25
|
end
|
data/lib/wicket/commands/l.rb
CHANGED
@@ -1,28 +1,16 @@
|
|
1
1
|
module Wicket
|
2
2
|
module Commands
|
3
3
|
class L < Command
|
4
|
+
ARGS = 2
|
5
|
+
ARG_LIST = [:x,:y]
|
4
6
|
|
5
|
-
def
|
6
|
-
2
|
7
|
-
end
|
8
|
-
|
9
|
-
def initialize(absolute,cursor_start,x,y)
|
7
|
+
def initialize(absolute,cursor_start,subpath,opts,x,y)
|
10
8
|
@absolute = absolute
|
9
|
+
@cursor_start = cursor_start
|
10
|
+
@subpath = subpath
|
11
|
+
@opts = opts
|
11
12
|
@x = x
|
12
13
|
@y = y
|
13
|
-
@cursor_start = cursor_start
|
14
|
-
end
|
15
|
-
|
16
|
-
def coordinates
|
17
|
-
{:x => @x, :y => @y}
|
18
|
-
end
|
19
|
-
|
20
|
-
def cursor_end
|
21
|
-
if @absolute
|
22
|
-
coordinates
|
23
|
-
else
|
24
|
-
{:x => (@cursor_start[:x] + @x), :y => (@cursor_start[:y] + @y)}
|
25
|
-
end
|
26
14
|
end
|
27
15
|
|
28
16
|
end
|
data/lib/wicket/commands/m.rb
CHANGED
@@ -1,28 +1,16 @@
|
|
1
1
|
module Wicket
|
2
2
|
module Commands
|
3
3
|
class M < Command
|
4
|
+
ARGS = 2
|
5
|
+
ARG_LIST = [:x,:y]
|
4
6
|
|
5
|
-
def
|
6
|
-
2
|
7
|
-
end
|
8
|
-
|
9
|
-
def initialize(absolute,cursor_start,x,y)
|
7
|
+
def initialize(absolute,cursor_start,subpath,opts,x,y)
|
10
8
|
@absolute = absolute
|
9
|
+
@cursor_start = cursor_start
|
10
|
+
@subpath = subpath
|
11
|
+
@opts = opts
|
11
12
|
@x = x
|
12
13
|
@y = y
|
13
|
-
@cursor_start = cursor_start
|
14
|
-
end
|
15
|
-
|
16
|
-
def coordinates
|
17
|
-
{:x => @x, :y => @y}
|
18
|
-
end
|
19
|
-
|
20
|
-
def cursor_end
|
21
|
-
if @absolute
|
22
|
-
coordinates
|
23
|
-
else
|
24
|
-
{:x => (@cursor_start[:x] + @x), :y => (@cursor_start[:y] + @y)}
|
25
|
-
end
|
26
14
|
end
|
27
15
|
|
28
16
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Wicket
|
2
|
+
module Commands
|
3
|
+
class Q < Command
|
4
|
+
ARGS = 4
|
5
|
+
ARG_LIST = [:c1x,:c1y,:x,:y]
|
6
|
+
include QuadraticBezier
|
7
|
+
|
8
|
+
def initialize(absolute,cursor_start,subpath,opts,c1x,c1y,x,y)
|
9
|
+
@absolute = absolute
|
10
|
+
@cursor_start = cursor_start
|
11
|
+
@subpath = subpath
|
12
|
+
@opts = opts
|
13
|
+
@c1x = c1x
|
14
|
+
@c1y = c1y
|
15
|
+
@x = x
|
16
|
+
@y = y
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Wicket
|
2
|
+
module Commands
|
3
|
+
module QuadraticBezier
|
4
|
+
include BezierCurve
|
5
|
+
|
6
|
+
def control_points
|
7
|
+
[cursor_start,c1,cursor_end]
|
8
|
+
end
|
9
|
+
|
10
|
+
def c1
|
11
|
+
@absolute ? Coordinate.new(@c1x,@c1y) : @cursor_start.absolutize(@c1x,@c1y)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Wicket
|
2
|
+
module Commands
|
3
|
+
class S < Command
|
4
|
+
ARGS = 4
|
5
|
+
include CubicBezier
|
6
|
+
ARG_LIST = [:c2x,:c2y,:x,:y]
|
7
|
+
|
8
|
+
def initialize(absolute,cursor_start,subpath,opts,c2x,c2y,x,y)
|
9
|
+
@absolute = absolute
|
10
|
+
@cursor_start = cursor_start
|
11
|
+
@subpath = subpath
|
12
|
+
@opts = opts
|
13
|
+
set_implicit_control_point!
|
14
|
+
@c2x = c2x
|
15
|
+
@c2y = c2y
|
16
|
+
@x = x
|
17
|
+
@y = y
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# > The first control point is assumed to be the reflection of the second
|
23
|
+
# > control point on the previous command relative to the current point.
|
24
|
+
# > (If there is no previous command or if the previous command was not an
|
25
|
+
# > C, c, S or s, assume the first control point is coincident with the
|
26
|
+
# > current point.)
|
27
|
+
# > http://www.w3.org/TR/SVG11/paths.html#PathDataCubicBezierCommands
|
28
|
+
def implied_c1
|
29
|
+
last = @subpath.last_command
|
30
|
+
case last
|
31
|
+
when C,S then @cursor_start.reflect(last.c2)
|
32
|
+
else @cursor_start
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Wicket
|
2
|
+
module Commands
|
3
|
+
class T < Command
|
4
|
+
ARGS = 2
|
5
|
+
include QuadraticBezier
|
6
|
+
ARG_LIST = [:x,:y]
|
7
|
+
|
8
|
+
def initialize(absolute,cursor_start,subpath,opts,x,y)
|
9
|
+
@absolute = absolute
|
10
|
+
@cursor_start = cursor_start
|
11
|
+
@subpath = subpath
|
12
|
+
@opts = opts
|
13
|
+
set_implicit_control_point!
|
14
|
+
@x = x
|
15
|
+
@y = y
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# > The control point is assumed to be the reflection of the control point on
|
21
|
+
# > the previous command relative to the current point. (If there is no previous
|
22
|
+
# > command or if the previous command was not a Q, q, T or t, assume the control
|
23
|
+
# > point is coincident with the current point.)
|
24
|
+
# > http://www.w3.org/TR/SVG11/paths.html#PathDataQuadraticBezierCommands
|
25
|
+
def implied_c1
|
26
|
+
last = @subpath.last_command
|
27
|
+
case last
|
28
|
+
when Q,T then @cursor_start.reflect(last.c1)
|
29
|
+
else @cursor_start
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/wicket/commands/v.rb
CHANGED
@@ -1,27 +1,24 @@
|
|
1
1
|
module Wicket
|
2
2
|
module Commands
|
3
3
|
class V < Command
|
4
|
+
ARGS = 1
|
5
|
+
ARG_LIST = [:y]
|
4
6
|
|
5
|
-
def
|
6
|
-
1
|
7
|
-
end
|
8
|
-
|
9
|
-
def initialize(absolute,cursor_start,y)
|
7
|
+
def initialize(absolute,cursor_start,subpath,opts,y)
|
10
8
|
@absolute = absolute
|
11
9
|
@cursor_start = cursor_start
|
10
|
+
@subpath = subpath
|
11
|
+
@opts = opts
|
12
12
|
@y = y
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def absolute_x
|
16
|
+
cursor_start.x
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
else
|
23
|
-
{:x => @cursor_start[:x], :y => (@cursor_start[:y] + @y)}
|
24
|
-
end
|
19
|
+
def to_svg(opts={})
|
20
|
+
o = @opts.merge(opts)
|
21
|
+
"V #{Utilities.format(absolute_y,o)}"
|
25
22
|
end
|
26
23
|
|
27
24
|
end
|
data/lib/wicket/commands/z.rb
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
module Wicket
|
2
2
|
module Commands
|
3
3
|
class Z < Command
|
4
|
+
ARGS = 1
|
5
|
+
ARG_LIST = []
|
4
6
|
|
5
|
-
def
|
6
|
-
1
|
7
|
-
end
|
8
|
-
|
9
|
-
def initialize(absolute,cursor_start)
|
7
|
+
def initialize(absolute,cursor_start,subpath,opts)
|
10
8
|
@cursor_start = cursor_start
|
11
|
-
|
12
|
-
|
13
|
-
def coordinates
|
14
|
-
{}
|
9
|
+
@subpath = subpath
|
10
|
+
@opts = opts
|
15
11
|
end
|
16
12
|
|
17
13
|
def cursor_end
|
18
14
|
@subpath.first_command.cursor_end
|
19
15
|
end
|
16
|
+
|
17
|
+
def to_svg(opts={})
|
18
|
+
"Z"
|
19
|
+
end
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Wicket
|
2
|
+
class Configuration
|
3
|
+
SETTINGS = [
|
4
|
+
:min_angle,
|
5
|
+
:min_angle_unit
|
6
|
+
]
|
7
|
+
attr_accessor *SETTINGS
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@min_angle = 165
|
11
|
+
end
|
12
|
+
|
13
|
+
def merge(*opts)
|
14
|
+
opts.each_with_object(to_h) do |opts,aggregate|
|
15
|
+
aggregate.merge!(opts)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def to_h
|
22
|
+
SETTINGS.each_with_object({}){|s,h| h.merge!(s => send(s)) }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Wicket
|
2
|
+
class Coordinate < Struct.new(:x,:y)
|
3
|
+
include Cartesian
|
4
|
+
|
5
|
+
# method for a reference point to create absolute coords from
|
6
|
+
# relative measures.
|
7
|
+
def absolutize(delta_x,delta_y)
|
8
|
+
self.class.new(x + delta_x , y + delta_y)
|
9
|
+
end
|
10
|
+
|
11
|
+
def relativize(remote)
|
12
|
+
[remote.x - x, remote.y - y]
|
13
|
+
end
|
14
|
+
|
15
|
+
def reflect(remote)
|
16
|
+
x,y = relativize(remote)
|
17
|
+
absolutize(-x,-y)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/wicket/subpath.rb
CHANGED
@@ -7,9 +7,9 @@ module Wicket
|
|
7
7
|
|
8
8
|
def add_command(*new_commands)
|
9
9
|
new_commands.each do |c|
|
10
|
-
c.subpath = self
|
11
10
|
@commands << c
|
12
11
|
end
|
12
|
+
self
|
13
13
|
end
|
14
14
|
|
15
15
|
def cursor_end
|
@@ -22,8 +22,8 @@ module Wicket
|
|
22
22
|
@commands.last.class == Commands::Z
|
23
23
|
end
|
24
24
|
|
25
|
-
def to_polygon
|
26
|
-
vertices = commands.map(
|
25
|
+
def to_polygon(opts)
|
26
|
+
vertices = commands.map{|c| c.to_wkt(opts) }.join(",")
|
27
27
|
"((#{vertices}))"
|
28
28
|
end
|
29
29
|
|
@@ -34,5 +34,9 @@ module Wicket
|
|
34
34
|
def first_command
|
35
35
|
@commands.first
|
36
36
|
end
|
37
|
+
|
38
|
+
def to_svg(opts={})
|
39
|
+
@commands.map{|c| c.to_svg(opts) }.join(" ")
|
40
|
+
end
|
37
41
|
end
|
38
42
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Wicket
|
2
|
+
class Subpoint < Struct.new(:x,:y,:t,:siblings)
|
3
|
+
include Cartesian
|
4
|
+
|
5
|
+
def self.from_coordinate(coordinate,t,siblings)
|
6
|
+
new(coordinate.x,coordinate.y,t,siblings)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(x,y,t,siblings)
|
10
|
+
super(x,y,t,siblings)
|
11
|
+
siblings << self
|
12
|
+
end
|
13
|
+
|
14
|
+
def previous_neighbor
|
15
|
+
siblings.select{|s| s.t < t }.max_by(&:t)
|
16
|
+
end
|
17
|
+
|
18
|
+
def next_neighbor
|
19
|
+
siblings.select{|s| s.t > t }.min_by(&:t)
|
20
|
+
end
|
21
|
+
|
22
|
+
def angle
|
23
|
+
evaluate_angle(previous_neighbor,self,next_neighbor) unless endpoint?
|
24
|
+
end
|
25
|
+
|
26
|
+
def endpoint?
|
27
|
+
t == 0 || t == 1
|
28
|
+
end
|
29
|
+
|
30
|
+
# This method evaluates the angle at p2 in radians (180 deg = pi)
|
31
|
+
def evaluate_angle(p1,p2,p3)
|
32
|
+
a = p2.distance_to(p1)
|
33
|
+
b = p2.distance_to(p3)
|
34
|
+
c = p1.distance_to(p3)
|
35
|
+
Math.acos((a**2 + b**2 - c**2)/(2 * a * b))
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|