cyberarm_engine 0.12.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/Gemfile +1 -1
  4. data/README.md +33 -3
  5. data/Rakefile +1 -1
  6. data/assets/textures/default.png +0 -0
  7. data/cyberarm_engine.gemspec +11 -8
  8. data/lib/cyberarm_engine.rb +25 -7
  9. data/lib/cyberarm_engine/animator.rb +56 -0
  10. data/lib/cyberarm_engine/background.rb +19 -15
  11. data/lib/cyberarm_engine/background_nine_slice.rb +125 -0
  12. data/lib/cyberarm_engine/bounding_box.rb +18 -18
  13. data/lib/cyberarm_engine/cache.rb +4 -0
  14. data/lib/cyberarm_engine/cache/download_manager.rb +121 -0
  15. data/lib/cyberarm_engine/common.rb +16 -16
  16. data/lib/cyberarm_engine/config_file.rb +46 -0
  17. data/lib/cyberarm_engine/game_object.rb +63 -72
  18. data/lib/cyberarm_engine/game_state.rb +7 -4
  19. data/lib/cyberarm_engine/model.rb +207 -0
  20. data/lib/cyberarm_engine/model/material.rb +21 -0
  21. data/lib/cyberarm_engine/model/model_object.rb +131 -0
  22. data/lib/cyberarm_engine/model/parser.rb +74 -0
  23. data/lib/cyberarm_engine/model/parsers/collada_parser.rb +138 -0
  24. data/lib/cyberarm_engine/model/parsers/wavefront_parser.rb +154 -0
  25. data/lib/cyberarm_engine/model_cache.rb +31 -0
  26. data/lib/cyberarm_engine/opengl.rb +28 -0
  27. data/lib/cyberarm_engine/opengl/light.rb +50 -0
  28. data/lib/cyberarm_engine/opengl/orthographic_camera.rb +46 -0
  29. data/lib/cyberarm_engine/opengl/perspective_camera.rb +38 -0
  30. data/lib/cyberarm_engine/opengl/renderer/bounding_box_renderer.rb +249 -0
  31. data/lib/cyberarm_engine/opengl/renderer/g_buffer.rb +164 -0
  32. data/lib/cyberarm_engine/opengl/renderer/opengl_renderer.rb +289 -0
  33. data/lib/cyberarm_engine/opengl/renderer/renderer.rb +22 -0
  34. data/lib/cyberarm_engine/opengl/shader.rb +406 -0
  35. data/lib/cyberarm_engine/opengl/texture.rb +69 -0
  36. data/lib/cyberarm_engine/ray.rb +5 -5
  37. data/lib/cyberarm_engine/stats.rb +21 -0
  38. data/lib/cyberarm_engine/text.rb +45 -31
  39. data/lib/cyberarm_engine/timer.rb +1 -1
  40. data/lib/cyberarm_engine/transform.rb +221 -9
  41. data/lib/cyberarm_engine/ui/border_canvas.rb +4 -3
  42. data/lib/cyberarm_engine/ui/dsl.rb +86 -50
  43. data/lib/cyberarm_engine/ui/element.rb +60 -21
  44. data/lib/cyberarm_engine/ui/elements/button.rb +121 -28
  45. data/lib/cyberarm_engine/ui/elements/check_box.rb +25 -33
  46. data/lib/cyberarm_engine/ui/elements/container.rb +88 -24
  47. data/lib/cyberarm_engine/ui/elements/edit_box.rb +179 -0
  48. data/lib/cyberarm_engine/ui/elements/edit_line.rb +180 -27
  49. data/lib/cyberarm_engine/ui/elements/flow.rb +1 -3
  50. data/lib/cyberarm_engine/ui/elements/image.rb +32 -12
  51. data/lib/cyberarm_engine/ui/elements/label.rb +122 -16
  52. data/lib/cyberarm_engine/ui/elements/list_box.rb +82 -0
  53. data/lib/cyberarm_engine/ui/elements/progress.rb +6 -5
  54. data/lib/cyberarm_engine/ui/elements/radio.rb +6 -0
  55. data/lib/cyberarm_engine/ui/elements/slider.rb +104 -0
  56. data/lib/cyberarm_engine/ui/elements/stack.rb +1 -3
  57. data/lib/cyberarm_engine/ui/elements/text_block.rb +156 -0
  58. data/lib/cyberarm_engine/ui/elements/toggle_button.rb +40 -31
  59. data/lib/cyberarm_engine/ui/event.rb +8 -7
  60. data/lib/cyberarm_engine/ui/gui_state.rb +94 -6
  61. data/lib/cyberarm_engine/ui/style.rb +10 -9
  62. data/lib/cyberarm_engine/ui/theme.rb +85 -22
  63. data/lib/cyberarm_engine/vector.rb +132 -38
  64. data/lib/cyberarm_engine/version.rb +2 -2
  65. data/lib/cyberarm_engine/{engine.rb → window.rb} +32 -19
  66. metadata +88 -17
  67. data/lib/cyberarm_engine/gosu_ext/circle.rb +0 -9
  68. data/lib/cyberarm_engine/shader.rb +0 -205
@@ -15,19 +15,18 @@ module CyberarmEngine
15
15
 
16
16
  return unless enabled?
17
17
 
18
- if respond_to?(event)
19
- return :handled if send(event, self, *args) == :handled
20
- end
18
+ return :handled if respond_to?(event) && (send(event, self, *args) == :handled)
21
19
 
22
20
  @event_handler[event].reverse_each do |handler|
23
21
  return :handled if handler.call(self, *args) == :handled
24
22
  end
25
23
 
26
- return nil
24
+ parent.publish(event, *args) if parent
25
+ nil
27
26
  end
28
27
 
29
28
  def event(event)
30
- @event_handler ||= Hash.new
29
+ @event_handler ||= {}
31
30
  @event_handler[event] ||= []
32
31
  end
33
32
  end
@@ -36,11 +35,13 @@ module CyberarmEngine
36
35
  attr_reader :publisher, :event, :handler
37
36
 
38
37
  def initialize(publisher, event, handler)
39
- @publisher, @event, @handler = publisher, event, handler
38
+ @publisher = publisher
39
+ @event = event
40
+ @handler = handler
40
41
  end
41
42
 
42
43
  def unsubscribe
43
44
  @publisher.unsubscribe(self)
44
45
  end
45
46
  end
46
- end
47
+ end
@@ -12,22 +12,30 @@ module CyberarmEngine
12
12
 
13
13
  @root_container = Element::Stack.new(gui_state: self)
14
14
  @game_objects << @root_container
15
- @containers = [@root_container]
15
+ $__current_container__ = @root_container
16
16
 
17
17
  @active_width = window.width
18
18
  @active_height = window.height
19
19
 
20
+ @menu = nil
20
21
  @focus = nil
21
22
  @mouse_over = nil
22
23
  @mouse_down_on = {}
23
24
  @mouse_down_position = {}
25
+ @last_mouse_pos = nil
26
+ @dragging_element = nil
24
27
  @pending_recalculate_request = false
28
+
29
+ @tip = Element::ToolTip.new("", parent: @root_container, z: Float::INFINITY)
30
+ @menu = nil
31
+ @min_drag_distance = 0
32
+ @mouse_pos = Vector.new
25
33
  end
26
34
 
27
35
  # throws :blur event to focused element and sets GuiState focused element
28
36
  # Does NOT throw :focus event at element or set element as focused
29
37
  def focus=(element)
30
- @focus.publish(:blur) if @focus and element && @focus != element
38
+ @focus.publish(:blur) if @focus && element && @focus != element
31
39
  @focus = element
32
40
  end
33
41
 
@@ -35,15 +43,35 @@ module CyberarmEngine
35
43
  @focus
36
44
  end
37
45
 
46
+ def draw
47
+ super
48
+
49
+ if @menu
50
+ Gosu.flush
51
+ @menu.draw
52
+ end
53
+
54
+ if @tip.value.length.positive?
55
+ Gosu.flush
56
+ @tip.draw
57
+ end
58
+ end
59
+
38
60
  def update
39
61
  if @pending_recalculate_request
40
62
  @root_container.recalculate
63
+ @root_container.recalculate
64
+ @root_container.recalculate
65
+
41
66
  @pending_recalculate_request = false
42
67
  end
43
68
 
69
+ @menu&.update
44
70
  super
45
71
 
46
- new_mouse_over = @root_container.hit_element?(window.mouse_x, window.mouse_y)
72
+ new_mouse_over = @menu.hit_element?(window.mouse_x, window.mouse_y) if @menu
73
+ new_mouse_over ||= @root_container.hit_element?(window.mouse_x, window.mouse_y)
74
+
47
75
  if new_mouse_over
48
76
  new_mouse_over.publish(:enter) if new_mouse_over != @mouse_over
49
77
  new_mouse_over.publish(:hover)
@@ -56,12 +84,37 @@ module CyberarmEngine
56
84
  redirect_holding_mouse_button(:middle) if @mouse_over && Gosu.button_down?(Gosu::MsMiddle)
57
85
  redirect_holding_mouse_button(:right) if @mouse_over && Gosu.button_down?(Gosu::MsRight)
58
86
 
87
+ if Vector.new(window.mouse_x, window.mouse_y) == @last_mouse_pos
88
+ if @mouse_over && (Gosu.milliseconds - @mouse_moved_at) > tool_tip_delay
89
+ @tip.value = @mouse_over.tip if @mouse_over
90
+ @tip.x = window.mouse_x - @tip.width / 2
91
+ @tip.x = 0 if @tip.x < 0
92
+ @tip.x = window.width - @tip.width if @tip.x + @tip.width > window.width
93
+ @tip.y = window.mouse_y - @tip.height
94
+ @tip.y = 0 if @tip.y < 0
95
+ @tip.y = window.height - @tip.height if @tip.y + @tip.height > window.height
96
+ @tip.update
97
+ @tip.recalculate
98
+ else
99
+ @tip.value = ""
100
+ end
101
+ else
102
+ @mouse_moved_at = Gosu.milliseconds
103
+ end
104
+
105
+ @last_mouse_pos = Vector.new(window.mouse_x, window.mouse_y)
106
+ @mouse_pos = @last_mouse_pos.clone
107
+
59
108
  request_recalculate if @active_width != window.width || @active_height != window.height
60
109
 
61
110
  @active_width = window.width
62
111
  @active_height = window.height
63
112
  end
64
113
 
114
+ def tool_tip_delay
115
+ 250 # ms
116
+ end
117
+
65
118
  def button_down(id)
66
119
  super
67
120
 
@@ -72,7 +125,11 @@ module CyberarmEngine
72
125
  redirect_mouse_button(:middle)
73
126
  when Gosu::MsRight
74
127
  redirect_mouse_button(:right)
128
+ when Gosu::KbF5
129
+ request_recalculate
75
130
  end
131
+
132
+ @focus.button_down(id) if @focus.respond_to?(:button_down)
76
133
  end
77
134
 
78
135
  def button_up(id)
@@ -90,9 +147,13 @@ module CyberarmEngine
90
147
  when Gosu::MsWheelDown
91
148
  redirect_mouse_wheel(:down)
92
149
  end
150
+
151
+ @focus.button_up(id) if @focus.respond_to?(:button_up)
93
152
  end
94
153
 
95
154
  def redirect_mouse_button(button)
155
+ hide_menu unless @menu && (@menu == @mouse_over) || (@mouse_over&.parent == @menu)
156
+
96
157
  if @focus && @mouse_over != @focus
97
158
  @focus.publish(:blur)
98
159
  @focus = nil
@@ -110,9 +171,19 @@ module CyberarmEngine
110
171
  end
111
172
 
112
173
  def redirect_released_mouse_button(button)
174
+ hide_menu if @menu && (@menu == @mouse_over) || (@mouse_over&.parent == @menu)
175
+
113
176
  if @mouse_over
114
177
  @mouse_over.publish(:"released_#{button}_mouse_button", window.mouse_x, window.mouse_y)
115
- @mouse_over.publish(:"clicked_#{button}_mouse_button", window.mouse_x, window.mouse_y) if @mouse_over == @mouse_down_on[button]
178
+ if @mouse_over == @mouse_down_on[button]
179
+ @mouse_over.publish(:"clicked_#{button}_mouse_button", window.mouse_x,
180
+ window.mouse_y)
181
+ end
182
+ end
183
+
184
+ if @dragging_element
185
+ @dragging_element.publish(:end_drag, window.mouse_x, window.mouse_y, button)
186
+ @dragging_element = nil
116
187
  end
117
188
 
118
189
  @mouse_down_position[button] = nil
@@ -120,7 +191,16 @@ module CyberarmEngine
120
191
  end
121
192
 
122
193
  def redirect_holding_mouse_button(button)
123
- @mouse_over.publish(:"holding_#{button}_mouse_button", window.mouse_x, window.mouse_y) if @mouse_over
194
+ if !@dragging_element && @mouse_down_on[button] && @mouse_down_on[button].draggable?(button) && @mouse_pos.distance(@mouse_down_position[button]) > @min_drag_distance
195
+ @dragging_element = @mouse_down_on[button]
196
+ @dragging_element.publish(:begin_drag, window.mouse_x, window.mouse_y, button)
197
+ end
198
+
199
+ if @dragging_element
200
+ @dragging_element.publish(:drag_update, window.mouse_x, window.mouse_y, button) if @dragging_element
201
+ elsif @mouse_over
202
+ @mouse_over.publish(:"holding_#{button}_mouse_button", window.mouse_x, window.mouse_y)
203
+ end
124
204
  end
125
205
 
126
206
  def redirect_mouse_wheel(button)
@@ -131,5 +211,13 @@ module CyberarmEngine
131
211
  def request_recalculate
132
212
  @pending_recalculate_request = true
133
213
  end
214
+
215
+ def show_menu(list_box)
216
+ @menu = list_box
217
+ end
218
+
219
+ def hide_menu
220
+ @menu = nil
221
+ end
134
222
  end
135
- end
223
+ end
@@ -1,11 +1,11 @@
1
1
  module Gosu
2
2
  class Color
3
- def _dump(level)
3
+ def _dump(_level)
4
4
  [
5
- "%02X" % self.alpha,
6
- "%02X" % self.red,
7
- "%02X" % self.green,
8
- "%02X" % self.blue
5
+ "%02X" % alpha,
6
+ "%02X" % red,
7
+ "%02X" % green,
8
+ "%02X" % blue
9
9
  ].join
10
10
  end
11
11
 
@@ -21,17 +21,18 @@ module CyberarmEngine
21
21
  @hash = Marshal.load(Marshal.dump(hash))
22
22
  end
23
23
 
24
- def method_missing(method, *args, &block)
24
+ def method_missing(method, *args)
25
25
  if method.to_s.end_with?("=")
26
26
  raise "Did not expect more than 1 argument" if args.size > 1
27
- return @hash[method.to_s.sub("=", "").to_sym] = args.first
27
+
28
+ @hash[method.to_s.sub("=", "").to_sym] = args.first
28
29
 
29
30
  elsif args.size == 0
30
- return @hash[method]
31
+ @hash[method]
31
32
 
32
33
  else
33
34
  raise ArgumentError, "Did not expect arguments"
34
35
  end
35
36
  end
36
37
  end
37
- end
38
+ end
@@ -11,17 +11,21 @@ module CyberarmEngine
11
11
 
12
12
  def theme_defaults(options)
13
13
  raise "Error" unless self.class.ancestors.include?(CyberarmEngine::Element)
14
+
14
15
  _theme = THEME
15
- _theme = _theme.merge(options[:theme]) if options[:theme]
16
+ _theme = deep_merge(_theme, options[:theme]) if options[:theme]
16
17
  _theme.delete(:theme) if options[:theme]
17
18
 
18
19
  hash = {}
19
20
  class_names = self.class.ancestors
20
- class_names = class_names[0..class_names.index(CyberarmEngine::Element)].map! {|c| c.to_s.split("::").last.to_sym}.reverse!
21
+ class_names = class_names[0..class_names.index(CyberarmEngine::Element)].map! do |c|
22
+ c.to_s.split("::").last.to_sym
23
+ end.reverse!
21
24
 
22
25
  class_names.each do |klass|
23
26
  next unless data = _theme.dig(klass)
24
- data.each do |key, value|
27
+
28
+ data.each do |_key, _value|
25
29
  hash.merge!(data)
26
30
  end
27
31
  end
@@ -32,7 +36,7 @@ module CyberarmEngine
32
36
  # Derived from Rails Hash#deep_merge!
33
37
  # Enables passing partial themes through Element options without issue
34
38
  def deep_merge(original, intergrate, &block)
35
- hash = original.merge(intergrate) do |key, this_val, other_val|
39
+ original.merge(intergrate) do |key, this_val, other_val|
36
40
  if this_val.is_a?(Hash) && other_val.is_a?(Hash)
37
41
  deep_merge(this_val, other_val, &block)
38
42
  elsif block_given?
@@ -41,8 +45,6 @@ module CyberarmEngine
41
45
  other_val
42
46
  end
43
47
  end
44
-
45
- return hash
46
48
  end
47
49
 
48
50
  THEME = {
@@ -51,33 +53,40 @@ module CyberarmEngine
51
53
  y: 0,
52
54
  z: 30,
53
55
 
54
- width: nil,
56
+ width: nil,
55
57
  height: nil,
56
- color: Gosu::Color::WHITE,
58
+ color: Gosu::Color::WHITE,
57
59
  background: Gosu::Color::NONE,
58
- margin: 0,
59
- padding: 0,
60
+ margin: 0,
61
+ padding: 0,
60
62
  border_thickness: 0,
61
63
  border_color: Gosu::Color::NONE,
62
- border_radius: 0,
64
+ border_radius: 0
63
65
  },
64
66
 
65
67
  Button: { # < Label
66
- margin: 1,
67
- padding: 4,
68
+ margin: 1,
69
+ padding: 4,
68
70
  border_thickness: 1,
69
71
  border_color: ["ffd59674".hex, "ffff8746".hex],
70
72
  border_radius: 0,
71
73
  background: ["ffc75e61".to_i(16), "ffe26623".to_i(16)],
74
+ text_align: :center,
75
+ text_wrap: :none,
72
76
 
73
77
  hover: {
74
- color: Gosu::Color.rgb(200,200,200),
75
- background: ["ffB23E41".to_i(16), "ffFF7C00".to_i(16)],
78
+ color: Gosu::Color.rgb(200, 200, 200),
79
+ background: ["ffB23E41".to_i(16), "ffFF7C00".to_i(16)]
76
80
  },
77
81
 
78
82
  active: {
79
83
  color: Gosu::Color::BLACK,
80
84
  background: ["ffB23E41".to_i(16)]
85
+ },
86
+
87
+ disabled: {
88
+ color: Gosu::Color::GRAY,
89
+ background: 0xff303030
81
90
  }
82
91
  },
83
92
 
@@ -88,18 +97,63 @@ module CyberarmEngine
88
97
  caret_width: 2,
89
98
  caret_color: Gosu::Color::WHITE,
90
99
  caret_interval: 500,
100
+ selection_color: Gosu::Color.rgba(255, 128, 50, 200),
101
+ text_align: :left
91
102
  },
92
103
 
93
104
  Image: { # < Element
105
+ color: Gosu::Color::WHITE,
106
+ tileable: false,
94
107
  retro: false
95
108
  },
96
109
 
97
- Label: { # < Element
98
- text_size: 28,
99
- text_shadow: false,
100
- font: "Arial",
101
- margin: 0,
102
- padding: 2
110
+ TextBlock: { # < Element
111
+ text_size: 28,
112
+ text_wrap: :word_wrap, # :word_wrap, :break_word, :none
113
+ text_shadow: false,
114
+ text_align: :left,
115
+ font: "Arial",
116
+ margin: 0,
117
+ padding: 2
118
+ },
119
+
120
+ Banner: { # < TextBlock
121
+ text_size: 48
122
+ },
123
+
124
+ Title: { # < TextBlock
125
+ text_size: 34
126
+ },
127
+
128
+ Subtitle: { # < TextBlock
129
+ text_size: 26
130
+ },
131
+
132
+ Tagline: { # < TextBlock
133
+ text_size: 24
134
+ },
135
+
136
+ Caption: { # < TextBlock
137
+ text_size: 22
138
+ },
139
+
140
+ Para: { # < TextBlock
141
+ text_size: 18
142
+ },
143
+
144
+ Inscription: { # < TextBlock
145
+ text_size: 16
146
+ },
147
+
148
+ ToolTip: { # < TextBlock
149
+ color: Gosu::Color::WHITE,
150
+ padding_top: 4,
151
+ padding_bottom: 4,
152
+ padding_left: 8,
153
+ padding_right: 8,
154
+ border_thickness: 1,
155
+ border_color: 0xffaaaaaa,
156
+ background: 0xff404040
103
157
  },
104
158
 
105
159
  ToggleButton: { # < Button
@@ -111,7 +165,16 @@ module CyberarmEngine
111
165
  height: 36,
112
166
  background: 0xff111111,
113
167
  fraction_background: [0xffc75e61, 0xffe26623],
114
- border_thickness: 4,
168
+ border_thickness: 1,
169
+ border_color: [0xffd59674, 0xffff8746]
170
+ },
171
+
172
+ Slider: { # < Element
173
+ width: 250,
174
+ height: 36,
175
+ background: 0xff111111,
176
+ fraction_background: [0xffc75e61, 0xffe26623],
177
+ border_thickness: 1,
115
178
  border_color: [0xffd59674, 0xffff8746]
116
179
  }
117
180
  }.freeze
@@ -1,63 +1,101 @@
1
1
  module CyberarmEngine
2
2
  class Vector
3
+ ##
4
+ # Creates a up vector
5
+ #
6
+ # Vector.new(0, 1, 0)
7
+ #
8
+ # @return [CyberarmEngine::Vector]
3
9
  def self.up
4
10
  Vector.new(0, 1, 0)
5
11
  end
6
12
 
13
+ ##
14
+ # Creates a down vector
15
+ #
16
+ # Vector.new(0, -1, 0)
17
+ #
18
+ # @return [CyberarmEngine::Vector]
7
19
  def self.down
8
20
  Vector.new(0, -1, 0)
9
21
  end
10
22
 
23
+ ##
24
+ # Creates a left vector
25
+ #
26
+ # Vector.new(-1, 0, 0)
27
+ #
28
+ # @return [CyberarmEngine::Vector]
11
29
  def self.left
12
30
  Vector.new(-1, 0, 0)
13
31
  end
14
32
 
33
+ ##
34
+ # Creates a right vector
35
+ #
36
+ # Vector.new(1, 0, 0)
37
+ #
38
+ # @return [CyberarmEngine::Vector]
15
39
  def self.right
16
40
  Vector.new(1, 0, 0)
17
41
  end
18
42
 
43
+ ##
44
+ # Creates a forward vector
45
+ #
46
+ # Vector.new(0, 0, 1)
47
+ #
48
+ # @return [CyberarmEngine::Vector]
19
49
  def self.forward
20
50
  Vector.new(0, 0, 1)
21
51
  end
22
52
 
53
+ ##
54
+ # Creates a backward vector
55
+ #
56
+ # Vector.new(0, 0, -1)
57
+ #
58
+ # @return [CyberarmEngine::Vector]
23
59
  def self.backward
24
60
  Vector.new(0, 0, -1)
25
61
  end
26
62
 
63
+ attr_accessor :x, :y, :z, :weight
64
+
27
65
  def initialize(x = 0, y = 0, z = 0, weight = 0)
28
- @x, @y, @z, @weight = x, y, z, weight
66
+ @x = x
67
+ @y = y
68
+ @z = z
69
+ @weight = weight
29
70
  end
30
71
 
31
- def x; @x; end
32
- def x=(n); @x = n; end
33
-
34
- def y; @y; end
35
- def y=(n); @y = n; end
36
-
37
- def z; @z; end
38
- def z=(n); @z = n; end
39
-
40
- def weight; @weight; end
41
- def weight=(n); @weight = n; end
42
-
43
72
  alias w weight
44
73
  alias w= weight=
45
74
 
75
+ # @return [Boolean]
46
76
  def ==(other)
47
77
  if other.is_a?(Numeric)
48
78
  @x == other &&
49
- @y == other &&
50
- @z == other &&
51
- @weight == other
52
- else
79
+ @y == other &&
80
+ @z == other &&
81
+ @weight == other
82
+ elsif other.is_a?(Vector)
53
83
  @x == other.x &&
54
- @y == other.y &&
55
- @z == other.z &&
56
- @weight == other.weight
84
+ @y == other.y &&
85
+ @z == other.z &&
86
+ @weight == other.weight
87
+ else
88
+ other == self
57
89
  end
58
90
  end
59
91
 
60
- # Performs math operation, excluding @weight
92
+ # Create a new vector using {x} and {y} values
93
+ # @return [CyberarmEngine::Vector]
94
+ def xy
95
+ Vector.new(@x, @y)
96
+ end
97
+
98
+ # Performs math operation, excluding {weight}
61
99
  private def operator(function, other)
62
100
  if other.is_a?(Numeric)
63
101
  Vector.new(
@@ -74,22 +112,37 @@ module CyberarmEngine
74
112
  end
75
113
  end
76
114
 
77
- # Adds Vector and Numberic or Vector and Vector, excluding @weight
115
+ # Adds Vector and Numeric or Vector and Vector, excluding {weight}
116
+ # @return [CyberarmEngine::Vector]
78
117
  def +(other)
79
118
  operator("+", other)
80
119
  end
81
120
 
82
- # Subtracts Vector and Numberic or Vector and Vector, excluding @weight
121
+ # Subtracts Vector and Numeric or Vector and Vector, excluding {weight}
122
+ # @return [CyberarmEngine::Vector]
83
123
  def -(other)
84
124
  operator("-", other)
85
125
  end
86
126
 
87
- # Multiplies Vector and Numberic or Vector and Vector, excluding @weight
127
+ # Multiplies Vector and Numeric or Vector and Vector, excluding {weight}
128
+ # @return [CyberarmEngine::Vector]
88
129
  def *(other)
89
130
  operator("*", other)
90
131
  end
91
132
 
92
- # Divides Vector and Numberic or Vector and Vector, excluding @weight
133
+ def multiply_transform(transform)
134
+ e = transform.elements
135
+
136
+ x = @x * e[0] + @y * e[1] + @z * e[2] + 1 * e[3]
137
+ y = @x * e[4] + @y * e[5] + @z * e[6] + 1 * e[7]
138
+ z = @x * e[8] + @y * e[9] + @z * e[10] + 1 * e[11]
139
+ w = @x * e[12] + @y * e[13] + @z * e[14] + 1 * e[15]
140
+
141
+ Vector.new(x / 1, y / 1, z / 1, w / 1)
142
+ end
143
+
144
+ # Divides Vector and Numeric or Vector and Vector, excluding {weight}
145
+ # @return [CyberarmEngine::Vector]
93
146
  def /(other)
94
147
  # Duplicated to protect from DivideByZero
95
148
  if other.is_a?(Numeric)
@@ -107,21 +160,25 @@ module CyberarmEngine
107
160
  end
108
161
  end
109
162
 
163
+ # dot product of {Vector}
164
+ # @return [Integer|Float]
110
165
  def dot(other)
111
166
  product = 0
112
167
 
113
- a = self.to_a
168
+ a = to_a
114
169
  b = other.to_a
115
170
 
116
171
  3.times do |i|
117
- product = product + (a[i] * b[i])
172
+ product += (a[i] * b[i])
118
173
  end
119
174
 
120
- return product
175
+ product
121
176
  end
122
177
 
178
+ # cross product of {Vector}
179
+ # @return [CyberarmEngine::Vector]
123
180
  def cross(other)
124
- a = self.to_a
181
+ a = to_a
125
182
  b = other.to_a
126
183
 
127
184
  Vector.new(
@@ -132,24 +189,39 @@ module CyberarmEngine
132
189
  end
133
190
 
134
191
  # returns degrees
192
+ # @return [Float]
135
193
  def angle(other)
136
- Math.acos( self.normalized.dot(other.normalized) ) * 180 / Math::PI
194
+ Math.acos(normalized.dot(other.normalized)) * 180 / Math::PI
137
195
  end
138
196
 
139
197
  # returns magnitude of Vector, ignoring #weight
198
+ # @return [Float]
140
199
  def magnitude
141
200
  Math.sqrt((@x * @x) + (@y * @y) + (@z * @z))
142
201
  end
143
202
 
203
+ ##
204
+ # returns normalized {Vector}
205
+ #
206
+ # @example
207
+ # CyberarmEngine::Vector.new(50, 21.2, 45).normalized
208
+ # # => <CyberarmEngine::Vector:0x001 @x=0.7089... @y=0.3005... @z=0.6380... @weight=0>
209
+ #
210
+ # @return [CyberarmEngine::Vector]
144
211
  def normalized
145
212
  mag = magnitude
146
213
  self / Vector.new(mag, mag, mag)
147
214
  end
148
215
 
216
+ # returns a direction {Vector}
217
+ #
218
+ # z is pitch
219
+ #
220
+ # y is yaw
221
+ #
222
+ # x is roll
223
+ # @return [CyberarmEngine::Vector]
149
224
  def direction
150
- # z is pitch
151
- # y is yaw
152
- # x is roll
153
225
  _x = -Math.sin(@y.degrees_to_radians) * Math.cos(@z.degrees_to_radians)
154
226
  _y = Math.sin(@z.degrees_to_radians)
155
227
  _z = Math.cos(@y.degrees_to_radians) * Math.cos(@z.degrees_to_radians)
@@ -157,43 +229,65 @@ module CyberarmEngine
157
229
  Vector.new(_x, _y, _z)
158
230
  end
159
231
 
232
+ # returns an inverse {Vector}
233
+ # @return [CyberarmEngine::Vector]
160
234
  def inverse
161
235
  Vector.new(1.0 / @x, 1.0 / @y, 1.0 / @z)
162
236
  end
163
237
 
238
+ # Adds up values of {x}, {y}, and {z}
239
+ # @return [Integer|Float]
164
240
  def sum
165
241
  @x + @y + @z
166
242
  end
167
243
 
244
+ ##
245
+ # Linear interpolation: smoothly transition between two {Vector}
246
+ #
247
+ # CyberarmEngine::Vector.new(100, 100, 100).lerp( CyberarmEngine::Vector.new(0, 0, 0), 0.75 )
248
+ # # => <CyberarmEngine::Vector:0x0001 @x=75.0, @y=75.0, @z=75.0, @weight=0>
249
+ #
250
+ # @param other [CyberarmEngine::Vector | Integer | Float] value to subtract from
251
+ # @param factor [Float] how complete transition to _other_ is, in range [0.0..1.0]
252
+ # @return [CyberarmEngine::Vector]
168
253
  def lerp(other, factor)
169
254
  (self - other) * factor.clamp(0.0, 1.0)
170
255
  end
171
256
 
172
257
  # 2D distance using X and Y
258
+ # @return [Float]
173
259
  def distance(other)
174
- Math.sqrt((@x-other.x)**2 + (@y-other.y)**2)
260
+ Math.sqrt((@x - other.x)**2 + (@y - other.y)**2)
175
261
  end
176
262
 
177
263
  # 2D distance using X and Z
264
+ # @return [Float]
178
265
  def gl_distance2d(other)
179
- Math.sqrt((@x-other.x)**2 + (@z-other.z)**2)
266
+ Math.sqrt((@x - other.x)**2 + (@z - other.z)**2)
180
267
  end
181
268
 
182
269
  # 3D distance using X, Y, and Z
270
+ # @return [Float]
183
271
  def distance3d(other)
184
- Math.sqrt((@x-other.x)**2 + (@y-other.y)**2 + (@z-other.z)**2)
272
+ Math.sqrt((@x - other.x)**2 + (@y - other.y)**2 + (@z - other.z)**2)
185
273
  end
186
274
 
275
+ # Converts {Vector} to Array
276
+ # @return [Array]
187
277
  def to_a
188
278
  [@x, @y, @z, @weight]
189
279
  end
190
280
 
281
+ # Converts {Vector} to String
282
+ # @return [String]
191
283
  def to_s
192
284
  "X: #{@x}, Y: #{@y}, Z: #{@z}, Weight: #{@weight}"
193
285
  end
194
286
 
287
+ # Converts {Vector} to Hash
288
+ # @return [Hash]
195
289
  def to_h
196
- {x: @x, y: @y, z: @z, weight: @weight}
290
+ { x: @x, y: @y, z: @z, weight: @weight }
197
291
  end
198
292
  end
199
- end
293
+ end