fusuma 2.0.0.pre → 2.0.3

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +50 -11
  3. data/fusuma.gemspec +5 -6
  4. data/lib/fusuma.rb +87 -33
  5. data/lib/fusuma/config.rb +33 -40
  6. data/lib/fusuma/config/index.rb +34 -8
  7. data/lib/fusuma/config/searcher.rb +80 -4
  8. data/lib/fusuma/custom_process.rb +13 -0
  9. data/lib/fusuma/device.rb +19 -6
  10. data/lib/fusuma/environment.rb +4 -3
  11. data/lib/fusuma/hash_support.rb +40 -0
  12. data/lib/fusuma/libinput_command.rb +10 -15
  13. data/lib/fusuma/multi_logger.rb +2 -6
  14. data/lib/fusuma/plugin/base.rb +18 -15
  15. data/lib/fusuma/plugin/buffers/buffer.rb +3 -2
  16. data/lib/fusuma/plugin/buffers/gesture_buffer.rb +34 -25
  17. data/lib/fusuma/plugin/buffers/timer_buffer.rb +3 -3
  18. data/lib/fusuma/plugin/detectors/detector.rb +26 -5
  19. data/lib/fusuma/plugin/detectors/pinch_detector.rb +109 -58
  20. data/lib/fusuma/plugin/detectors/rotate_detector.rb +91 -50
  21. data/lib/fusuma/plugin/detectors/swipe_detector.rb +93 -56
  22. data/lib/fusuma/plugin/events/event.rb +5 -4
  23. data/lib/fusuma/plugin/events/records/context_record.rb +27 -0
  24. data/lib/fusuma/plugin/events/records/gesture_record.rb +9 -6
  25. data/lib/fusuma/plugin/events/records/index_record.rb +46 -14
  26. data/lib/fusuma/plugin/events/records/record.rb +1 -1
  27. data/lib/fusuma/plugin/events/records/text_record.rb +2 -1
  28. data/lib/fusuma/plugin/executors/command_executor.rb +21 -4
  29. data/lib/fusuma/plugin/executors/executor.rb +45 -3
  30. data/lib/fusuma/plugin/filters/filter.rb +1 -1
  31. data/lib/fusuma/plugin/filters/libinput_device_filter.rb +6 -7
  32. data/lib/fusuma/plugin/filters/libinput_timeout_filter.rb +2 -2
  33. data/lib/fusuma/plugin/inputs/input.rb +20 -7
  34. data/lib/fusuma/plugin/inputs/libinput_command_input.rb +17 -5
  35. data/lib/fusuma/plugin/inputs/timer_input.rb +7 -7
  36. data/lib/fusuma/plugin/manager.rb +22 -29
  37. data/lib/fusuma/plugin/parsers/libinput_gesture_parser.rb +10 -8
  38. data/lib/fusuma/plugin/parsers/parser.rb +8 -9
  39. data/lib/fusuma/string_support.rb +16 -0
  40. data/lib/fusuma/version.rb +1 -1
  41. data/spec/helpers/config_helper.rb +20 -0
  42. data/spec/lib/config/searcher_spec.rb +97 -0
  43. data/spec/lib/config_spec.rb +112 -0
  44. data/spec/lib/custom_process_spec.rb +28 -0
  45. data/spec/lib/device_spec.rb +96 -0
  46. data/spec/lib/dummy_config.yml +31 -0
  47. data/spec/lib/fusuma_spec.rb +103 -0
  48. data/spec/lib/libinput-list-devices_iberianpig-XPS-9360.txt +181 -0
  49. data/spec/lib/libinput-list-devices_magic_trackpad.txt +51 -0
  50. data/spec/lib/libinput-list-devices_razer_razer_blade.txt +252 -0
  51. data/spec/lib/libinput-list-devices_thejinx0r.txt +361 -0
  52. data/spec/lib/libinput-list-devices_unavailable.txt +36 -0
  53. data/spec/lib/libinput_command_spec.rb +160 -0
  54. data/spec/lib/plugin/base_spec.rb +74 -0
  55. data/spec/lib/plugin/buffers/buffer_spec.rb +80 -0
  56. data/spec/lib/plugin/buffers/dummy_buffer.rb +20 -0
  57. data/spec/lib/plugin/buffers/gesture_buffer_spec.rb +172 -0
  58. data/spec/lib/plugin/detectors/detector_spec.rb +43 -0
  59. data/spec/lib/plugin/detectors/dummy_detector.rb +24 -0
  60. data/spec/lib/plugin/detectors/pinch_detector_spec.rb +119 -0
  61. data/spec/lib/plugin/detectors/rotate_detector_spec.rb +125 -0
  62. data/spec/lib/plugin/detectors/swipe_detector_spec.rb +118 -0
  63. data/spec/lib/plugin/events/event_spec.rb +30 -0
  64. data/spec/lib/plugin/events/records/gesture_record_spec.rb +22 -0
  65. data/spec/lib/plugin/events/records/record_spec.rb +31 -0
  66. data/spec/lib/plugin/events/records/text_record_spec.rb +26 -0
  67. data/spec/lib/plugin/executors/command_executor_spec.rb +57 -0
  68. data/spec/lib/plugin/executors/executor_spec.rb +160 -0
  69. data/spec/lib/plugin/filters/filter_spec.rb +92 -0
  70. data/spec/lib/plugin/filters/libinput_filter_spec.rb +120 -0
  71. data/spec/lib/plugin/inputs/input_spec.rb +70 -0
  72. data/spec/lib/plugin/inputs/libinput_command_input_spec.rb +121 -0
  73. data/spec/lib/plugin/inputs/timer_input_spec.rb +40 -0
  74. data/spec/lib/plugin/manager_spec.rb +27 -0
  75. data/spec/lib/plugin/parsers/parser_spec.rb +45 -0
  76. data/spec/spec_helper.rb +20 -0
  77. metadata +84 -38
  78. data/.github/FUNDING.yml +0 -8
  79. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -32
  80. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -17
  81. data/.github/pull_request_template.md +0 -9
  82. data/.github/stale.yml +0 -18
  83. data/.gitignore +0 -17
  84. data/.reek.yml +0 -96
  85. data/.rspec +0 -2
  86. data/.rubocop.yml +0 -40
  87. data/.rubocop_todo.yml +0 -40
  88. data/.travis.yml +0 -11
  89. data/CHANGELOG.md +0 -456
  90. data/CODE_OF_CONDUCT.md +0 -74
  91. data/CONTRIBUTING.md +0 -72
  92. data/Gemfile +0 -18
  93. data/Rakefile +0 -15
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './buffer.rb'
3
+ require_relative './buffer'
4
4
 
5
5
  module Fusuma
6
6
  module Plugin
@@ -12,8 +12,8 @@ module Fusuma
12
12
 
13
13
  def config_param_types
14
14
  {
15
- 'source': [String],
16
- 'seconds_to_keep': [Float, Integer]
15
+ source: [String],
16
+ seconds_to_keep: [Float, Integer]
17
17
  }
18
18
  end
19
19
 
@@ -1,13 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../base.rb'
4
- require_relative '../events/event.rb'
3
+ require_relative '../base'
4
+ require_relative '../events/event'
5
5
 
6
6
  module Fusuma
7
7
  module Plugin
8
8
  module Detectors
9
9
  # Inherite this base
10
10
  class Detector < Base
11
+ # @return [Array<String>]
12
+ def sources
13
+ @source ||= self.class.const_get('SOURCES')
14
+ end
15
+
16
+ # Always watch buffers and detect them or not
17
+ # @return [TrueClass,FalseClass]
18
+ def watch?
19
+ false
20
+ end
21
+
11
22
  # @param _buffers [Array<Buffer>]
12
23
  # @return [Event] if event is detected
13
24
  # @return [NilClass] if event is NOT detected
@@ -21,7 +32,7 @@ module Fusuma
21
32
  # @return [Events::Event]
22
33
  def create_event(record:)
23
34
  @last_time = Time.now
24
- Events::Event.new(time: Time.now, tag: tag, record: record)
35
+ Events::Event.new(time: @last_time, tag: tag, record: record)
25
36
  end
26
37
 
27
38
  def last_time
@@ -33,11 +44,21 @@ module Fusuma
33
44
  end
34
45
 
35
46
  def tag
36
- self.class.name.split('Detectors::').last.underscore
47
+ self.class.tag
37
48
  end
38
49
 
39
50
  def type
40
- self.class.name.underscore.split('/').last.gsub('_detector', '')
51
+ self.class.type
52
+ end
53
+
54
+ class << self
55
+ def tag
56
+ name.split('Detectors::').last.underscore
57
+ end
58
+
59
+ def type(tag_name = tag)
60
+ tag_name.gsub('_detector', '')
61
+ end
41
62
  end
42
63
  end
43
64
  end
@@ -1,50 +1,111 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './detector.rb'
3
+ require_relative './detector'
4
4
 
5
5
  module Fusuma
6
6
  module Plugin
7
7
  module Detectors
8
8
  class PinchDetector < Detector
9
+ SOURCES = ['gesture'].freeze
9
10
  BUFFER_TYPE = 'gesture'
10
11
  GESTURE_RECORD_TYPE = 'pinch'
11
12
 
12
13
  FINGERS = [2, 3, 4].freeze
13
- BASE_THERESHOLD = 0.1
14
- BASE_INTERVAL = 0.1
14
+ BASE_THERESHOLD = 1.3
15
15
 
16
16
  # @param buffers [Array<Buffer>]
17
- # @return [Event] if event is detected
17
+ # @return [Events::Event] if event is detected
18
18
  # @return [NilClass] if event is NOT detected
19
19
  def detect(buffers)
20
- buffer = buffers.find { |b| b.type == BUFFER_TYPE }
21
- .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
22
-
23
- return if buffer.empty?
24
-
25
- finger = buffer.finger
26
-
27
- avg_zoom = buffer.avg_attrs(:zoom)
28
- first_zoom = buffer.events.first.record.direction.zoom
29
- diameter = avg_zoom / first_zoom
30
-
31
- direction = Direction.new(diameter: diameter).to_s
32
- quantity = Quantity.new(diameter: diameter).to_f
33
-
34
- index = create_index(gesture: type,
35
- finger: finger,
36
- direction: direction)
37
-
38
- return unless enough?(index: index, quantity: quantity)
20
+ gesture_buffer = buffers.find { |b| b.type == BUFFER_TYPE }
21
+ .select_from_last_begin
22
+ .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
23
+
24
+ updating_events = gesture_buffer.updating_events
25
+ return if updating_events.empty?
26
+
27
+ finger = gesture_buffer.finger
28
+
29
+ status = case gesture_buffer.events.last.record.status
30
+ when 'end'
31
+ 'end'
32
+ when 'update'
33
+ if updating_events.length == 1
34
+ 'begin'
35
+ else
36
+ 'update'
37
+ end
38
+ else
39
+ gesture_buffer.events.last.record.status
40
+ end
41
+
42
+ prev_event, event = if status == 'end'
43
+ [
44
+ gesture_buffer.events[-3],
45
+ gesture_buffer.events[-2]
46
+ ]
47
+ else
48
+ [
49
+ gesture_buffer.events[-2],
50
+ gesture_buffer.events[-1]
51
+ ]
52
+ end
53
+ delta = event.record.delta
54
+ prev_delta = prev_event.record.delta
55
+
56
+ repeat_direction = Direction.new(target: delta.zoom, base: (prev_delta&.zoom || 1.0)).to_s
57
+ # repeat_quantity = Quantity.new(target: delta.zoom, base: (prev_delta&.zoom || 1.0)).to_f
58
+
59
+ repeat_index = create_repeat_index(gesture: type, finger: finger,
60
+ direction: repeat_direction,
61
+ status: status)
62
+ if status == 'update'
63
+ return unless moved?(prev_event, event)
64
+
65
+ avg_zoom = gesture_buffer.avg_attrs(:zoom)
66
+ first_zoom = updating_events.first.record.delta.zoom
67
+
68
+ oneshot_quantity = Quantity.new(target: avg_zoom, base: first_zoom).to_f
69
+ oneshot_direction = Direction.new(target: avg_zoom, base: first_zoom).to_s
70
+ oneshot_index = create_oneshot_index(gesture: type, finger: finger,
71
+ direction: oneshot_direction)
72
+ if enough_oneshot_threshold?(index: oneshot_index, quantity: oneshot_quantity)
73
+ return [
74
+ create_event(record: Events::Records::IndexRecord.new(
75
+ index: oneshot_index, trigger: :oneshot, args: delta.to_h
76
+ )),
77
+ create_event(record: Events::Records::IndexRecord.new(
78
+ index: repeat_index, trigger: :repeat, args: delta.to_h
79
+ ))
80
+ ]
81
+ end
82
+ end
83
+ create_event(record: Events::Records::IndexRecord.new(
84
+ index: repeat_index, trigger: :repeat, args: delta.to_h
85
+ ))
86
+ end
39
87
 
40
- create_event(record: Events::Records::IndexRecord.new(index: index))
88
+ # @param [String] gesture
89
+ # @param [Integer] finger
90
+ # @param [String] direction
91
+ # @param [String] status
92
+ # @return [Config::Index]
93
+ def create_repeat_index(gesture:, finger:, direction:, status:)
94
+ Config::Index.new(
95
+ [
96
+ Config::Index::Key.new(gesture),
97
+ Config::Index::Key.new(finger.to_i),
98
+ Config::Index::Key.new(direction, skippable: true),
99
+ Config::Index::Key.new(status)
100
+ ]
101
+ )
41
102
  end
42
103
 
43
104
  # @param [String] gesture
44
105
  # @param [Integer] finger
45
106
  # @param [String] direction
46
107
  # @return [Config::Index]
47
- def create_index(gesture:, finger:, direction:)
108
+ def create_oneshot_index(gesture:, finger:, direction:)
48
109
  Config::Index.new(
49
110
  [
50
111
  Config::Index::Key.new(gesture),
@@ -56,41 +117,25 @@ module Fusuma
56
117
 
57
118
  private
58
119
 
59
- def enough?(index:, quantity:)
60
- enough_interval?(index: index) && enough_diameter?(index: index, quantity: quantity)
120
+ def moved?(prev_event, event)
121
+ zoom_delta = (event.record.delta.zoom - prev_event.record.delta.zoom).abs
122
+ updating_time = (event.time - prev_event.time) * 100
123
+ zoom_delta / updating_time > 0.01
61
124
  end
62
125
 
63
- def enough_diameter?(index:, quantity:)
126
+ def enough_oneshot_threshold?(index:, quantity:)
64
127
  quantity >= threshold(index: index)
65
128
  end
66
129
 
67
- def enough_interval?(index:)
68
- return true if first_time?
69
- return true if (Time.now - last_time) > interval_time(index: index)
70
-
71
- false
72
- end
73
-
74
130
  def threshold(index:)
75
131
  @threshold ||= {}
76
132
  @threshold[index.cache_key] ||= begin
77
- keys_specific = Config::Index.new [*index.keys, 'threshold']
78
- keys_global = Config::Index.new ['threshold', type]
79
- config_value = Config.search(keys_specific) ||
80
- Config.search(keys_global) || 1
81
- BASE_THERESHOLD * config_value
82
- end
83
- end
84
-
85
- def interval_time(index:)
86
- @interval_time ||= {}
87
- @interval_time[index.cache_key] ||= begin
88
- keys_specific = Config::Index.new [*index.keys, 'interval']
89
- keys_global = Config::Index.new ['interval', type]
90
- config_value = Config.search(keys_specific) ||
91
- Config.search(keys_global) || 1
92
- BASE_INTERVAL * config_value
93
- end
133
+ keys_specific = Config::Index.new [*index.keys, 'threshold']
134
+ keys_global = Config::Index.new ['threshold', type]
135
+ config_value = Config.search(keys_specific) ||
136
+ Config.search(keys_global) || 1
137
+ BASE_THERESHOLD * config_value
138
+ end
94
139
  end
95
140
 
96
141
  # direction of gesture
@@ -98,8 +143,9 @@ module Fusuma
98
143
  IN = 'in'
99
144
  OUT = 'out'
100
145
 
101
- def initialize(diameter:)
102
- @diameter = diameter
146
+ def initialize(target:, base:)
147
+ @target = target.to_f
148
+ @base = base.to_f
103
149
  end
104
150
 
105
151
  def to_s
@@ -107,7 +153,7 @@ module Fusuma
107
153
  end
108
154
 
109
155
  def calc
110
- if @diameter > 1
156
+ if @target > @base
111
157
  IN
112
158
  else
113
159
  OUT
@@ -117,8 +163,9 @@ module Fusuma
117
163
 
118
164
  # quantity of gesture
119
165
  class Quantity
120
- def initialize(diameter:)
121
- @diameter = diameter
166
+ def initialize(target:, base:)
167
+ @target = target.to_f
168
+ @base = base.to_f
122
169
  end
123
170
 
124
171
  def to_f
@@ -126,7 +173,11 @@ module Fusuma
126
173
  end
127
174
 
128
175
  def calc
129
- (1.0 - @diameter).abs
176
+ if @target > @base
177
+ @target / @base
178
+ else
179
+ @base / @target
180
+ end
130
181
  end
131
182
  end
132
183
  end
@@ -1,47 +1,106 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './detector.rb'
3
+ require_relative './detector'
4
4
 
5
5
  module Fusuma
6
6
  module Plugin
7
7
  module Detectors
8
8
  class RotateDetector < Detector
9
+ SOURCES = ['gesture'].freeze
9
10
  BUFFER_TYPE = 'gesture'
10
11
  GESTURE_RECORD_TYPE = 'pinch'
11
12
 
12
13
  FINGERS = [2, 3, 4].freeze
13
14
  BASE_THERESHOLD = 0.5
14
- BASE_INTERVAL = 0.1
15
15
 
16
16
  # @param buffers [Array<Buffer>]
17
- # @return [Event] if event is detected
17
+ # @return [Events::Event] if event is detected
18
18
  # @return [NilClass] if event is NOT detected
19
19
  def detect(buffers)
20
- buffer = buffers.find { |b| b.type == BUFFER_TYPE }
21
- .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
22
-
23
- return if buffer.empty?
24
-
25
- angle = buffer.avg_attrs(:rotate)
26
-
27
- finger = buffer.finger
28
- direction = Direction.new(angle: angle).to_s
29
- quantity = Quantity.new(angle: angle).to_f
30
-
31
- index = create_index(gesture: type,
32
- finger: finger,
33
- direction: direction)
34
-
35
- return unless enough?(index: index, quantity: quantity)
20
+ gesture_buffer = buffers.find { |b| b.type == BUFFER_TYPE }
21
+ .select_from_last_begin
22
+ .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
23
+
24
+ updating_events = gesture_buffer.updating_events
25
+ return if updating_events.empty?
26
+
27
+ updating_time = 100 * (updating_events.last.time - updating_events.first.time)
28
+ oneshot_angle = gesture_buffer.sum_attrs(:rotate) / updating_time
29
+
30
+ return if updating_events.empty?
31
+
32
+ finger = gesture_buffer.finger
33
+
34
+ status = case gesture_buffer.events.last.record.status
35
+ when 'end'
36
+ 'end'
37
+ when 'update'
38
+ if updating_events.length == 1
39
+ 'begin'
40
+ else
41
+ 'update'
42
+ end
43
+ else
44
+ gesture_buffer.events.last.record.status
45
+ end
46
+
47
+ delta = if status == 'end'
48
+ gesture_buffer.events[-2].record.delta
49
+ else
50
+ gesture_buffer.events.last.record.delta
51
+ end
52
+
53
+ repeat_direction = Direction.new(angle: delta.rotate).to_s
54
+ repeat_quantity = Quantity.new(angle: delta.rotate).to_f
55
+
56
+ repeat_index = create_repeat_index(gesture: type, finger: finger,
57
+ direction: repeat_direction,
58
+ status: status)
59
+
60
+ if status == 'update'
61
+ return unless moved?(repeat_quantity)
62
+
63
+ oneshot_direction = Direction.new(angle: oneshot_angle).to_s
64
+ oneshot_quantity = Quantity.new(angle: oneshot_angle).to_f
65
+ oneshot_index = create_oneshot_index(gesture: type, finger: finger,
66
+ direction: oneshot_direction)
67
+ if enough_oneshot_threshold?(index: oneshot_index, quantity: oneshot_quantity)
68
+ return [
69
+ create_event(record: Events::Records::IndexRecord.new(
70
+ index: oneshot_index, trigger: :oneshot, args: delta.to_h
71
+ )),
72
+ create_event(record: Events::Records::IndexRecord.new(
73
+ index: repeat_index, trigger: :repeat, args: delta.to_h
74
+ ))
75
+ ]
76
+ end
77
+ end
78
+ create_event(record: Events::Records::IndexRecord.new(
79
+ index: repeat_index, trigger: :repeat, args: delta.to_h
80
+ ))
81
+ end
36
82
 
37
- create_event(record: Events::Records::IndexRecord.new(index: index))
83
+ # @param [String] gesture
84
+ # @param [Integer] finger
85
+ # @param [String] direction
86
+ # @param [String] status
87
+ # @return [Config::Index]
88
+ def create_repeat_index(gesture:, finger:, direction:, status:)
89
+ Config::Index.new(
90
+ [
91
+ Config::Index::Key.new(gesture),
92
+ Config::Index::Key.new(finger.to_i),
93
+ Config::Index::Key.new(direction),
94
+ Config::Index::Key.new(status)
95
+ ]
96
+ )
38
97
  end
39
98
 
40
99
  # @param [String] gesture
41
100
  # @param [Integer] finger
42
101
  # @param [String] direction
43
102
  # @return [Config::Index]
44
- def create_index(gesture:, finger:, direction:)
103
+ def create_oneshot_index(gesture:, finger:, direction:)
45
104
  Config::Index.new(
46
105
  [
47
106
  Config::Index::Key.new(gesture),
@@ -53,41 +112,23 @@ module Fusuma
53
112
 
54
113
  private
55
114
 
56
- def enough?(index:, quantity:)
57
- enough_interval?(index: index) && enough_angle?(index: index, quantity: quantity)
115
+ def moved?(repeat_quantity)
116
+ repeat_quantity > 0.2
58
117
  end
59
118
 
60
- def enough_angle?(index:, quantity:)
119
+ def enough_oneshot_threshold?(index:, quantity:)
61
120
  quantity > threshold(index: index)
62
121
  end
63
122
 
64
- def enough_interval?(index:)
65
- return true if first_time?
66
- return true if (Time.now - last_time) > interval_time(index: index)
67
-
68
- false
69
- end
70
-
71
123
  def threshold(index:)
72
124
  @threshold ||= {}
73
125
  @threshold[index.cache_key] ||= begin
74
- keys_specific = Config::Index.new [*index.keys, 'threshold']
75
- keys_global = Config::Index.new ['threshold', type]
76
- config_value = Config.search(keys_specific) ||
77
- Config.search(keys_global) || 1
78
- BASE_THERESHOLD * config_value
79
- end
80
- end
81
-
82
- def interval_time(index:)
83
- @interval_time ||= {}
84
- @interval_time[index.cache_key] ||= begin
85
- keys_specific = Config::Index.new [*index.keys, 'interval']
86
- keys_global = Config::Index.new ['interval', type]
87
- config_value = Config.search(keys_specific) ||
88
- Config.search(keys_global) || 1
89
- BASE_INTERVAL * config_value
90
- end
126
+ keys_specific = Config::Index.new [*index.keys, 'threshold']
127
+ keys_global = Config::Index.new ['threshold', type]
128
+ config_value = Config.search(keys_specific) ||
129
+ Config.search(keys_global) || 1
130
+ BASE_THERESHOLD * config_value
131
+ end
91
132
  end
92
133
 
93
134
  # direction of gesture
@@ -96,7 +137,7 @@ module Fusuma
96
137
  COUNTERCLOCKWISE = 'counterclockwise'
97
138
 
98
139
  def initialize(angle:)
99
- @angle = angle
140
+ @angle = angle.to_f
100
141
  end
101
142
 
102
143
  def to_s
@@ -104,7 +145,7 @@ module Fusuma
104
145
  end
105
146
 
106
147
  def calc
107
- if @angle > 0
148
+ if @angle.positive?
108
149
  CLOCKWISE
109
150
  else
110
151
  COUNTERCLOCKWISE