vector_be_winding 0.9.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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +37 -0
- data/Rakefile +6 -0
- data/exe/vbw +46 -0
- data/lib/vector_be_winding/drawable.rb +61 -0
- data/lib/vector_be_winding/path.rb +52 -0
- data/lib/vector_be_winding/rect.rb +77 -0
- data/lib/vector_be_winding/segment.rb +101 -0
- data/lib/vector_be_winding/shape.rb +28 -0
- data/lib/vector_be_winding/spatial_tree.rb +39 -0
- data/lib/vector_be_winding/sub_path.rb +93 -0
- data/lib/vector_be_winding/vector.rb +47 -0
- data/lib/vector_be_winding/version.rb +3 -0
- data/lib/vector_be_winding.rb +10 -0
- data/vector_be_winding.gemspec +29 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8d63331bdde70ece16ad946c86b40d16651ff6e3
|
4
|
+
data.tar.gz: 757ea390d934c45d3dfb2461d05182955bfafaf3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 349d180848c2a3a0cda2fdab934c4cf6bacd599c629fcafaaa66e74073b2f8e9cce6553cca95df1fc78d3e15abad77ada9647450724ceab32dffae7c56a8194a
|
7
|
+
data.tar.gz: 02deb594f43de821830d565aa5c5db4d4ac9bae726b6d8b9875624f0f5febaab80f0eaa2c0a5b77708276693f932e90882985bc4553ce566774f3f1382c4780f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# VectorBeWinding 
|
2
|
+
|
3
|
+
Android Vector Drawable is very combinent, but sometimes it missing holes in the shape generated by some tools. `vector_be_winding` will massage such Vector Drawalbes so that the shape will be rendered as expectd!
|
4
|
+
|
5
|
+
## Example
|
6
|
+
|
7
|
+
TODO: Describe some pipeline which might generate "wrong" Vector Drawable.
|
8
|
+
|
9
|
+
1. Skecth + vdtool
|
10
|
+
|
11
|
+
TODO: Describe why it happens and how this tool resolve it.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
just with Gem.
|
16
|
+
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
TODO: Write usage instructions here
|
21
|
+
|
22
|
+
## Development
|
23
|
+
|
24
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
25
|
+
|
26
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
27
|
+
|
28
|
+
## Contributing
|
29
|
+
|
30
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/dagezi/vector_be_winding.
|
31
|
+
|
32
|
+
## Lisense
|
33
|
+
|
34
|
+
MIT License.
|
35
|
+
|
36
|
+
|
37
|
+
|
data/Rakefile
ADDED
data/exe/vbw
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'vector_be_winding'
|
4
|
+
|
5
|
+
opt = OptionParser.new
|
6
|
+
|
7
|
+
@dry_run = false
|
8
|
+
@verbose = false
|
9
|
+
@debug = false
|
10
|
+
@backup_template = '%{p}.bak'
|
11
|
+
|
12
|
+
opt.on('-n', 'Dry run') { @dry_run = true }
|
13
|
+
opt.on('-v', 'Verbose') { @verbose = true }
|
14
|
+
opt.on('-d', 'Debug') { @debug = true }
|
15
|
+
opt.on('-i TEMP', 'Template of back-up filename. Default; ".bak"') { |temp|
|
16
|
+
@backup_template = if temp.index('%')
|
17
|
+
temp
|
18
|
+
else
|
19
|
+
'%{p}' + temp
|
20
|
+
end
|
21
|
+
}
|
22
|
+
|
23
|
+
opt.parse!(ARGV)
|
24
|
+
|
25
|
+
ARGV.each { |file|
|
26
|
+
drawable = VectorBeWinding::Drawable.new
|
27
|
+
drawable.read_from_file(file)
|
28
|
+
if !drawable.is_winding
|
29
|
+
print "Not winding: #{file}\n" if @verbose
|
30
|
+
drawable.dump if @debug
|
31
|
+
|
32
|
+
if !@dry_run
|
33
|
+
args = {
|
34
|
+
d: File.dirname(file),
|
35
|
+
p: file,
|
36
|
+
r: File.basename(file, File.extname(file)),
|
37
|
+
e: File.extname(file)
|
38
|
+
}
|
39
|
+
|
40
|
+
File.rename(file, @backup_template % args)
|
41
|
+
drawable.be_winding
|
42
|
+
drawable.dump if @debug
|
43
|
+
drawable.write_to_file(file)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "rexml/document"
|
2
|
+
|
3
|
+
module VectorBeWinding
|
4
|
+
class Drawable
|
5
|
+
# A class to handle Android's vector drawable
|
6
|
+
|
7
|
+
def read_from_file(filename)
|
8
|
+
open(filename) { |inio|
|
9
|
+
read(inio)
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def read(inio)
|
14
|
+
@document = REXML::Document.new inio
|
15
|
+
end
|
16
|
+
|
17
|
+
def write_to_file(filename)
|
18
|
+
open(filename, 'w') {|outio|
|
19
|
+
write(outio)
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def write(outio)
|
24
|
+
@document.write outio
|
25
|
+
end
|
26
|
+
|
27
|
+
def be_winding
|
28
|
+
@document.each_recursive {|node|
|
29
|
+
if node.name == 'path'
|
30
|
+
pathString = node.attributes['android:pathData']
|
31
|
+
path = Path.with_string(pathString)
|
32
|
+
newPath = path.be_winding
|
33
|
+
|
34
|
+
node.add_attribute('android:pathData', newPath.to_command)
|
35
|
+
end
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_winding
|
40
|
+
@document.each_recursive {|node|
|
41
|
+
if node.name == 'path'
|
42
|
+
pathString = node.attributes['android:pathData']
|
43
|
+
path = Path.with_string(pathString)
|
44
|
+
return false if !path.is_winding
|
45
|
+
end
|
46
|
+
}
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def dump
|
51
|
+
@document.each_recursive {|node|
|
52
|
+
if node.name == 'path'
|
53
|
+
pathString = node.attributes['android:pathData']
|
54
|
+
path = Path.with_string(pathString)
|
55
|
+
puts "Path"
|
56
|
+
path.dump(1)
|
57
|
+
end
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module VectorBeWinding
|
2
|
+
class Path < Shape
|
3
|
+
include SpatialTree
|
4
|
+
|
5
|
+
attr_reader :sub_paths
|
6
|
+
|
7
|
+
def self.with_string(path_string)
|
8
|
+
begin
|
9
|
+
svg_path = Savage::Parser.parse(path_string)
|
10
|
+
rescue => e
|
11
|
+
raise ArgumentError, "Possibly wrong path string \"#{path_string}\""
|
12
|
+
end
|
13
|
+
Path.new(svg_path.subpaths.map { |svg_subpath| SubPath.with_svg(svg_subpath) })
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(sub_paths, is_tree = false)
|
17
|
+
if is_tree
|
18
|
+
@children = sub_paths
|
19
|
+
@sub_paths = []
|
20
|
+
children.each {|root| root.each { |subpath| @sub_paths << subpath }}
|
21
|
+
else
|
22
|
+
@sub_paths = sub_paths
|
23
|
+
sub_paths.each {|p| insert_to_tree(p)}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def bounding_rect
|
28
|
+
@bounding_rect ||= @sub_paths.map(&:bounding_rect).reduce(:|)
|
29
|
+
end
|
30
|
+
|
31
|
+
def area
|
32
|
+
sub_paths.map(&:area).reduce(:+)
|
33
|
+
end
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
"#<Path>"
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_winding(sign = 1)
|
40
|
+
children.all? { |c| c.is_winding }
|
41
|
+
end
|
42
|
+
|
43
|
+
def be_winding()
|
44
|
+
wounds = children.map(&:be_winding)
|
45
|
+
Path.new(wounds, true)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_command
|
49
|
+
sub_paths.map(&:to_command).join
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module VectorBeWinding
|
2
|
+
class Rect < Shape
|
3
|
+
attr_reader :left, :top, :right, :bottom
|
4
|
+
|
5
|
+
def initialize(left, top, right, bottom)
|
6
|
+
@left, @right = if left <= right then [left, right] else [right, left] end
|
7
|
+
@top, @bottom = if top <= bottom then [top, bottom] else [bottom, top] end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.with_vectors(v0, v1)
|
11
|
+
self.new(v0.x, v0.y, v1.x, v1.y)
|
12
|
+
end
|
13
|
+
|
14
|
+
def bounding_rect
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(rect1)
|
19
|
+
left == rect1.left && top == rect1.top &&
|
20
|
+
right == rect1.right && bottom == rect1.bottom
|
21
|
+
end
|
22
|
+
|
23
|
+
def empty?
|
24
|
+
left == right || top == bottom
|
25
|
+
end
|
26
|
+
|
27
|
+
def |(rect1)
|
28
|
+
Rect.new([left, rect1.left].min, [top, rect1.top].min,
|
29
|
+
[right, rect1.right].max, [bottom, rect1.bottom].max)
|
30
|
+
end
|
31
|
+
|
32
|
+
def &(rect1)
|
33
|
+
newLeft = [left, rect1.left].max
|
34
|
+
newTop = [top, rect1.top].max
|
35
|
+
newRight = [right, rect1.right].min
|
36
|
+
newBottom = [bottom, rect1.bottom].min
|
37
|
+
|
38
|
+
if (newLeft <= newRight && newTop <= newBottom)
|
39
|
+
Rect.new(newLeft, newTop, newRight, newBottom)
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def intersectedness(shape1)
|
46
|
+
if shape1.class == Rect
|
47
|
+
[Rect.range_intersectedness(left, right, shape1.left, shape1.right),
|
48
|
+
Rect.range_intersectedness(top, bottom, shape1.top, shape1.bottom)].min
|
49
|
+
else
|
50
|
+
shape1.intersectedness(self)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.range_intersectedness(a0, a1, b0, b1)
|
55
|
+
if a0 < b0
|
56
|
+
a1 - b0
|
57
|
+
else
|
58
|
+
b1 - a0
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def containingness(shape1)
|
63
|
+
b = shape1.bounding_rect
|
64
|
+
[Rect.range_containingness(left, right, b.left, b.right),
|
65
|
+
Rect.range_containingness(top, bottom, b.top, b.bottom)].min
|
66
|
+
end
|
67
|
+
|
68
|
+
# check [a0, a0] contains [b0, b1]
|
69
|
+
def self.range_containingness(a0, a1, b0, b1)
|
70
|
+
if a0 <= b0 && b1 <= a1
|
71
|
+
(b0 - a0) * (a1 - b1)
|
72
|
+
else
|
73
|
+
-1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module VectorBeWinding
|
2
|
+
class Segment < Shape
|
3
|
+
attr_reader :start_point, :direction, :end_point,
|
4
|
+
:control, :control_1
|
5
|
+
|
6
|
+
def initialize(direction, start_point, end_point_hint, prev_segment = nil)
|
7
|
+
@direction = direction
|
8
|
+
@start_point = start_point
|
9
|
+
@end_point =
|
10
|
+
if @direction.kind_of?(::Savage::Directions::PointTarget)
|
11
|
+
create_vector(@direction.target.x, @direction.target.y, @direction.absolute?)
|
12
|
+
elsif @direction.kind_of?(::Savage::Directions::HorizontalTo)
|
13
|
+
create_vector(@direction.target, nil, @direction.absolute?)
|
14
|
+
elsif @direction.kind_of?(::Savage::Directions::VerticalTo)
|
15
|
+
create_vector(nil, @direction.target, @direction.absolute?)
|
16
|
+
elsif @direction.kind_of?(::Savage::Directions::ClosePath)
|
17
|
+
end_point_hint
|
18
|
+
else
|
19
|
+
raise "Unknown direction: #{@direction}"
|
20
|
+
end
|
21
|
+
|
22
|
+
if @direction.instance_of?(::Savage::Directions::QuadraticCurveTo)
|
23
|
+
control = @direction.control ||
|
24
|
+
prev_segment.control&.reflect(start_point) ||
|
25
|
+
start_point
|
26
|
+
@control = create_vector(control.x, control.y, @direction.absolute?)
|
27
|
+
elsif @direction.instance_of?(::Savage::Directions::CubicCurveTo)
|
28
|
+
control = @direction.control
|
29
|
+
control_1 = @direction.control_1 ||
|
30
|
+
prev_segment.control&.reflect(start_point) ||
|
31
|
+
start_point
|
32
|
+
@control = create_vector(control.x, control.y, @direction.absolute?)
|
33
|
+
@control_1 = create_vector(control_1.x, control_1.y, @direction.absolute?)
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO: Support 'A'
|
37
|
+
end
|
38
|
+
|
39
|
+
def control_2
|
40
|
+
control
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_vector(x, y, absolute)
|
44
|
+
if absolute
|
45
|
+
Vector.new(x || start_point.x, y || start_point.y)
|
46
|
+
else
|
47
|
+
start_point + Vector.new(x || 0, y || 0)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def bounding_rect
|
52
|
+
@bounding_rect ||=
|
53
|
+
Rect.new(start_point.x, start_point.y, end_point.x, end_point.y)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Calculate direction area of the triangle (p, start_point, end_point)
|
57
|
+
# It must be positive iff the three points forms clockwise order.
|
58
|
+
def area(p)
|
59
|
+
if @direction.kind_of?(::Savage::Directions::QuadraticCurveTo)
|
60
|
+
# Approximate with triangle
|
61
|
+
(start_point - p).cross(control - start_point) +
|
62
|
+
(control - p).cross(end_point - control)
|
63
|
+
elsif @direction.kind_of?(::Savage::Directions::CubicCurveTo)
|
64
|
+
# Approximate with quadrangle
|
65
|
+
(start_point - p).cross(control_1 - start_point) +
|
66
|
+
(control_1 - p).cross(control_2 - control_1) +
|
67
|
+
(control_2 - p).cross(end_point - control_2)
|
68
|
+
else
|
69
|
+
# TODO: Support arc
|
70
|
+
(start_point - p).cross(end_point - start_point) / 2.0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def reverse
|
75
|
+
Segment.new(reverse_dir, end_point, start_point)
|
76
|
+
end
|
77
|
+
|
78
|
+
def reverse_dir
|
79
|
+
case @direction.command_code.upcase
|
80
|
+
when 'Z', 'L'
|
81
|
+
::Savage::Directions::LineTo.new(start_point.x, start_point.y, true)
|
82
|
+
when 'M'
|
83
|
+
nil # Unable to reverse
|
84
|
+
when 'H'
|
85
|
+
::Savage::Directions::HorizontalTo.new(start_point.x, true)
|
86
|
+
when 'V'
|
87
|
+
::Savage::Directions::VerticalTo.new(start_point.y, true)
|
88
|
+
when 'Q', 'T'
|
89
|
+
::Savage::Directions::QuadraticCurveTo.new(
|
90
|
+
control.x, control.y, start_point.x, start_point.y, true)
|
91
|
+
when 'C', 'S'
|
92
|
+
::Savage::Directions::CubicCurveTo.new(
|
93
|
+
control.x, control.y, control_1.x, control_1.y,
|
94
|
+
start_point.x, start_point.y, true)
|
95
|
+
else
|
96
|
+
# TODO: Support 'A'
|
97
|
+
raise "Unknown direction: #{@direction}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module VectorBeWinding
|
2
|
+
class Shape
|
3
|
+
# Each subclass must override it
|
4
|
+
def bounding_rect
|
5
|
+
raise 'implment this'
|
6
|
+
end
|
7
|
+
|
8
|
+
def intersects?(shape1)
|
9
|
+
intersectedness(shape1) >= 0
|
10
|
+
end
|
11
|
+
|
12
|
+
# Very naive definition. Each subclass is expected to override it
|
13
|
+
# negative: no intersected, 0: touched, positive: intersected
|
14
|
+
def intersectedness(shape1)
|
15
|
+
bounding_rect.intersectedness(shape1.bounding_rect)
|
16
|
+
end
|
17
|
+
|
18
|
+
def contains?(shape1)
|
19
|
+
containingness(shape1) >= 0
|
20
|
+
end
|
21
|
+
|
22
|
+
# Very naive definition. Each subclass is expected to override it
|
23
|
+
# negative: no containing, 0: containing with border, positive: inside
|
24
|
+
def containingness(shape1)
|
25
|
+
bounding_rect.containingness(shape1.bounding_rect)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module VectorBeWinding
|
2
|
+
module SpatialTree
|
3
|
+
def children
|
4
|
+
@children ||= []
|
5
|
+
end
|
6
|
+
|
7
|
+
def insert_to_tree(node)
|
8
|
+
raise "#{node} is same node: " if equal?(node)
|
9
|
+
|
10
|
+
containers = children.select {|n1| n1.contains?(node)}
|
11
|
+
if containers.empty?
|
12
|
+
components = children.select {|n1| node.contains?(n1)}
|
13
|
+
components.each{|n1| children.delete(n1)}
|
14
|
+
node.children.concat(components)
|
15
|
+
children << node
|
16
|
+
else
|
17
|
+
containers.first.insert_to_tree(node)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def depth
|
22
|
+
if children.empty?
|
23
|
+
1
|
24
|
+
else
|
25
|
+
children.map(&:depth).max + 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def each &pr
|
30
|
+
pr.call(self)
|
31
|
+
children.each {|node| node.each(&pr) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def dump(indent = 0)
|
35
|
+
puts ' ' * (indent * 2) + (area < 0 ? '-' : '+') + inspect
|
36
|
+
children.each {|node| node.dump(indent + 1)}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module VectorBeWinding
|
2
|
+
class SubPath < Shape
|
3
|
+
include SpatialTree
|
4
|
+
|
5
|
+
attr_reader :start_point, :svg_subpath, :segments
|
6
|
+
|
7
|
+
def self.with_segments(segments)
|
8
|
+
SubPath.new.init_with_segment(segments)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Suppose all segments are connected
|
12
|
+
def init_with_segment(segments)
|
13
|
+
raise "No segments" if segments.empty?
|
14
|
+
@segments = segments
|
15
|
+
@start_point = segments.first.start_point
|
16
|
+
@svg_subpath = Savage::SubPath.new(start_point.x, start_point.y)
|
17
|
+
@svg_subpath.directions.concat(segments.map(&:direction))
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.with_string(path_string)
|
22
|
+
path = Savage::Parser.parse(path_string)
|
23
|
+
raise "No subpaths: ${path_string}" if path.subpaths.empty?
|
24
|
+
SubPath.with_svg(path.subpaths.last)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.with_svg(svg_subpath, start_point = nil)
|
28
|
+
SubPath.new.init_with_svg(svg_subpath, start_point)
|
29
|
+
end
|
30
|
+
|
31
|
+
def init_with_svg(svg_subpath, start_point = nil)
|
32
|
+
start_point ||= Vector.new(0,0)
|
33
|
+
@svg_subpath = svg_subpath
|
34
|
+
@segments = []
|
35
|
+
|
36
|
+
point = start_point
|
37
|
+
@svg_subpath.directions.each { |dir|
|
38
|
+
segment = Segment.new(dir, point, start_point, segments.last)
|
39
|
+
point = segment.end_point
|
40
|
+
if segment.direction.kind_of?(Savage::Directions::MoveTo)
|
41
|
+
start_point = point
|
42
|
+
else
|
43
|
+
@segments << segment
|
44
|
+
end
|
45
|
+
}
|
46
|
+
@start_point = start_point
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def bounding_rect
|
51
|
+
unless @bounding_rect
|
52
|
+
rect = Rect.with_vectors(start_point, start_point)
|
53
|
+
segments.each { |segment|
|
54
|
+
rect |= segment.bounding_rect
|
55
|
+
}
|
56
|
+
@bounding_rect = rect
|
57
|
+
end
|
58
|
+
@bounding_rect
|
59
|
+
end
|
60
|
+
|
61
|
+
# Calculate direction area of this path.
|
62
|
+
# It's positive iff the path forms clockwise.
|
63
|
+
def area()
|
64
|
+
segments.map { |seg| seg.area(start_point) }.reduce(:+)
|
65
|
+
end
|
66
|
+
|
67
|
+
def reverse
|
68
|
+
SubPath.with_segments(segments.map(&:reverse).reverse)
|
69
|
+
end
|
70
|
+
|
71
|
+
def be_winding(sign = 1)
|
72
|
+
wound = if area * sign >= 0
|
73
|
+
SubPath.with_svg(svg_subpath)
|
74
|
+
else
|
75
|
+
reverse
|
76
|
+
end
|
77
|
+
wound.children.concat(children.map { |c| c.be_winding(-sign) })
|
78
|
+
wound
|
79
|
+
end
|
80
|
+
|
81
|
+
def is_winding
|
82
|
+
children.all? { |c| c.is_winding && c.area * area < 0}
|
83
|
+
end
|
84
|
+
|
85
|
+
def inspect
|
86
|
+
"#<SubPath \"#{svg_subpath.to_command}\">"
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_command
|
90
|
+
@svg_subpath.to_command
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module VectorBeWinding
|
2
|
+
class Vector
|
3
|
+
attr_accessor :x, :y
|
4
|
+
|
5
|
+
def initialize(x, y)
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(v)
|
11
|
+
x == v.x && y == v.y
|
12
|
+
end
|
13
|
+
|
14
|
+
def -@()
|
15
|
+
Vector.new(-x, -y)
|
16
|
+
end
|
17
|
+
|
18
|
+
def +(v)
|
19
|
+
Vector.new(x + v.x, y + v.y)
|
20
|
+
end
|
21
|
+
|
22
|
+
def -(v)
|
23
|
+
Vector.new(x - v.x, y - v.y)
|
24
|
+
end
|
25
|
+
|
26
|
+
def *(s)
|
27
|
+
Vector.new(x * s, y * s)
|
28
|
+
end
|
29
|
+
|
30
|
+
def dot(v)
|
31
|
+
x * v.x + y * v.y
|
32
|
+
end
|
33
|
+
|
34
|
+
def norm
|
35
|
+
dot(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def cross(v)
|
39
|
+
x * v.y - y * v.x
|
40
|
+
end
|
41
|
+
|
42
|
+
# Create reflection point of self
|
43
|
+
def reflect(v)
|
44
|
+
v + (v - self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "savage"
|
2
|
+
require "vector_be_winding/version"
|
3
|
+
require "vector_be_winding/shape"
|
4
|
+
require "vector_be_winding/vector"
|
5
|
+
require "vector_be_winding/rect"
|
6
|
+
require "vector_be_winding/segment"
|
7
|
+
require "vector_be_winding/spatial_tree"
|
8
|
+
require "vector_be_winding/sub_path"
|
9
|
+
require "vector_be_winding/path"
|
10
|
+
require "vector_be_winding/drawable"
|
@@ -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 'vector_be_winding/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "vector_be_winding"
|
8
|
+
spec.version = VectorBeWinding::VERSION
|
9
|
+
spec.authors = ["Takeshi SASAKI"]
|
10
|
+
spec.email = ["dagezi@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Let Android vector drawable follow winding-rule.}
|
13
|
+
spec.description = %q{Let Android vector drawable follow winding-rule.}
|
14
|
+
spec.homepage = "http://github.com/dagezi/vector-be-winding"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features|bin|sampleApp)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "savage"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vector_be_winding
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Takeshi SASAKI
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: savage
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.13'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.13'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
description: Let Android vector drawable follow winding-rule.
|
70
|
+
email:
|
71
|
+
- dagezi@gmail.com
|
72
|
+
executables:
|
73
|
+
- vbw
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- ".travis.yml"
|
80
|
+
- Gemfile
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- exe/vbw
|
84
|
+
- lib/vector_be_winding.rb
|
85
|
+
- lib/vector_be_winding/drawable.rb
|
86
|
+
- lib/vector_be_winding/path.rb
|
87
|
+
- lib/vector_be_winding/rect.rb
|
88
|
+
- lib/vector_be_winding/segment.rb
|
89
|
+
- lib/vector_be_winding/shape.rb
|
90
|
+
- lib/vector_be_winding/spatial_tree.rb
|
91
|
+
- lib/vector_be_winding/sub_path.rb
|
92
|
+
- lib/vector_be_winding/vector.rb
|
93
|
+
- lib/vector_be_winding/version.rb
|
94
|
+
- vector_be_winding.gemspec
|
95
|
+
homepage: http://github.com/dagezi/vector-be-winding
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata: {}
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 2.6.10
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: Let Android vector drawable follow winding-rule.
|
119
|
+
test_files: []
|
120
|
+
has_rdoc:
|