reflexion 0.5.2 → 0.6.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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/reflex/application.cpp +19 -1
  3. data/.doc/ext/reflex/chase_constraint.cpp +84 -0
  4. data/.doc/ext/reflex/clipboard.cpp +65 -0
  5. data/.doc/ext/reflex/constraint.cpp +135 -0
  6. data/.doc/ext/reflex/key_event.cpp +6 -6
  7. data/.doc/ext/reflex/link_constraint.cpp +178 -0
  8. data/.doc/ext/reflex/menu.cpp +302 -0
  9. data/.doc/ext/reflex/native.cpp +22 -4
  10. data/.doc/ext/reflex/pin.cpp +156 -0
  11. data/.doc/ext/reflex/pointer.cpp +2 -0
  12. data/.doc/ext/reflex/pointer_event.cpp +1 -1
  13. data/.doc/ext/reflex/reflex.cpp +42 -7
  14. data/.doc/ext/reflex/selector.cpp +4 -4
  15. data/.doc/ext/reflex/snap_constraint.cpp +125 -0
  16. data/.doc/ext/reflex/style_length.cpp +3 -2
  17. data/.doc/ext/reflex/view.cpp +96 -23
  18. data/.doc/ext/reflex/wheel_constraint.cpp +142 -0
  19. data/.doc/ext/reflex/window.cpp +59 -17
  20. data/.github/workflows/release-gem.yml +10 -1
  21. data/.github/workflows/test.yml +9 -0
  22. data/ChangeLog.md +30 -0
  23. data/README.md +1 -0
  24. data/VERSION +1 -1
  25. data/ext/reflex/application.cpp +21 -1
  26. data/ext/reflex/chase_constraint.cpp +89 -0
  27. data/ext/reflex/clipboard.cpp +69 -0
  28. data/ext/reflex/constraint.cpp +146 -0
  29. data/ext/reflex/key_event.cpp +6 -6
  30. data/ext/reflex/link_constraint.cpp +194 -0
  31. data/ext/reflex/menu.cpp +330 -0
  32. data/ext/reflex/native.cpp +22 -4
  33. data/ext/reflex/pin.cpp +165 -0
  34. data/ext/reflex/pointer.cpp +2 -0
  35. data/ext/reflex/pointer_event.cpp +1 -1
  36. data/ext/reflex/reflex.cpp +42 -7
  37. data/ext/reflex/selector.cpp +4 -4
  38. data/ext/reflex/selector.h +1 -1
  39. data/ext/reflex/snap_constraint.cpp +135 -0
  40. data/ext/reflex/style_length.cpp +3 -2
  41. data/ext/reflex/view.cpp +117 -37
  42. data/ext/reflex/wheel_constraint.cpp +154 -0
  43. data/ext/reflex/window.cpp +65 -18
  44. data/include/reflex/application.h +9 -0
  45. data/include/reflex/clipboard.h +53 -0
  46. data/include/reflex/constraint.h +275 -0
  47. data/include/reflex/defs.h +204 -195
  48. data/include/reflex/event.h +2 -0
  49. data/include/reflex/menu.h +120 -0
  50. data/include/reflex/pin.h +68 -0
  51. data/include/reflex/pointer.h +4 -0
  52. data/include/reflex/ruby/clipboard.h +23 -0
  53. data/include/reflex/ruby/constraint.h +94 -0
  54. data/include/reflex/ruby/menu.h +103 -0
  55. data/include/reflex/ruby/pin.h +40 -0
  56. data/include/reflex/ruby/view.h +36 -0
  57. data/include/reflex/ruby/window.h +36 -18
  58. data/include/reflex/view.h +39 -12
  59. data/include/reflex/window.h +17 -4
  60. data/include/reflex.h +5 -0
  61. data/lib/reflex/chase_constraint.rb +15 -0
  62. data/lib/reflex/clipboard.rb +19 -0
  63. data/lib/reflex/constraint.rb +29 -0
  64. data/lib/reflex/extension.rb +1 -1
  65. data/lib/reflex/helper.rb +28 -0
  66. data/lib/reflex/key_event.rb +7 -12
  67. data/lib/reflex/link_constraint.rb +31 -0
  68. data/lib/reflex/menu.rb +76 -0
  69. data/lib/reflex/pin.rb +39 -0
  70. data/lib/reflex/pointer.rb +18 -0
  71. data/lib/reflex/pointer_event.rb +1 -1
  72. data/lib/reflex/snap_constraint.rb +28 -0
  73. data/lib/reflex/view.rb +56 -4
  74. data/lib/reflex/wheel_constraint.rb +28 -0
  75. data/lib/reflex/wheel_event.rb +9 -0
  76. data/lib/reflex.rb +15 -6
  77. data/pod.rake +4 -2
  78. data/reflex.gemspec +3 -3
  79. data/samples/constraint.rb +152 -0
  80. data/samples/menu.rb +29 -0
  81. data/src/application.cpp +21 -2
  82. data/src/application.h +5 -0
  83. data/src/clipboard.cpp +81 -0
  84. data/src/constraint.cpp +1662 -0
  85. data/src/constraint.h +64 -0
  86. data/src/event.cpp +7 -1
  87. data/src/event.h +1 -2
  88. data/src/ios/application.mm +5 -0
  89. data/src/ios/clipboard.mm +44 -0
  90. data/src/ios/event.h +6 -2
  91. data/src/ios/event.mm +31 -12
  92. data/src/ios/menu.mm +36 -0
  93. data/src/ios/view_controller.mm +45 -12
  94. data/src/ios/window.mm +6 -0
  95. data/src/menu.cpp +325 -0
  96. data/src/menu.h +65 -0
  97. data/src/osx/app_delegate.mm +74 -125
  98. data/src/osx/application.mm +16 -0
  99. data/src/osx/clipboard.mm +45 -0
  100. data/src/osx/event.mm +40 -10
  101. data/src/osx/menu.h +25 -0
  102. data/src/osx/menu.mm +372 -0
  103. data/src/osx/native_window.mm +27 -0
  104. data/src/osx/opengl_view.mm +51 -0
  105. data/src/osx/window.mm +11 -0
  106. data/src/pin.cpp +137 -0
  107. data/src/pointer.cpp +27 -16
  108. data/src/pointer.h +6 -0
  109. data/src/sdl/application.cpp +5 -0
  110. data/src/sdl/clipboard.cpp +39 -0
  111. data/src/sdl/event.cpp +20 -3
  112. data/src/sdl/event.h +6 -3
  113. data/src/sdl/menu.cpp +35 -0
  114. data/src/sdl/window.cpp +26 -8
  115. data/src/view.cpp +313 -52
  116. data/src/view.h +18 -2
  117. data/src/win32/application.cpp +5 -0
  118. data/src/win32/clipboard.cpp +210 -0
  119. data/src/win32/event.cpp +11 -1
  120. data/src/win32/event.h +2 -0
  121. data/src/win32/menu.cpp +352 -0
  122. data/src/win32/menu.h +27 -0
  123. data/src/win32/window.cpp +62 -0
  124. data/src/win32/window.h +3 -0
  125. data/src/window.cpp +235 -22
  126. data/src/window.h +13 -2
  127. data/src/world.cpp +77 -14
  128. data/src/world.h +9 -2
  129. data/test/helper.rb +7 -0
  130. data/test/test_application.rb +11 -0
  131. data/test/test_chase_constraint.rb +124 -0
  132. data/test/test_clipboard.rb +54 -0
  133. data/test/test_constraint.rb +186 -0
  134. data/test/test_key_event.rb +6 -0
  135. data/test/test_link_constraint.rb +239 -0
  136. data/test/test_menu.rb +207 -0
  137. data/test/test_pin.rb +65 -0
  138. data/test/test_pointer.rb +23 -9
  139. data/test/test_pointer_event.rb +1 -1
  140. data/test/test_snap_constraint.rb +142 -0
  141. data/test/test_view.rb +113 -23
  142. data/test/test_wheel_constraint.rb +127 -0
  143. data/test/test_wheel_event.rb +15 -9
  144. data/test/test_window.rb +10 -0
  145. metadata +82 -8
@@ -11,6 +11,17 @@ class TestApplication < Test::Unit::TestCase
11
11
  assert_equal 'AppName', @@app.name
12
12
  end
13
13
 
14
+ def test_menu()
15
+ m = Reflex::Menu.new
16
+ assert_nil @@app.menu
17
+ @@app.menu = m
18
+ assert_equal m, @@app.menu
19
+ @@app.menu = nil
20
+ assert_nil @@app.menu
21
+
22
+ assert_raise(TypeError) {@@app.menu = 1}
23
+ end
24
+
14
25
  def test_inspect()
15
26
  assert_match %r|#<Reflex::Application:0x\w{16}>|, @@app.inspect
16
27
  end
@@ -0,0 +1,124 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestChaseConstraint < Test::Unit::TestCase
5
+
6
+ def window()
7
+ Reflex::Window.new
8
+ end
9
+
10
+ def view(x = 0, y = 0, w = 50, h = 50, **options, &block)
11
+ Reflex::View.new(
12
+ frame: [x, y, w, h],
13
+ shape: Reflex::RectShape.new(density: 1),
14
+ **options, &block)
15
+ end
16
+
17
+ def point(...)
18
+ Reflex::Point.new(...)
19
+ end
20
+
21
+ def setup_views(win = window)
22
+ v1 = view 100, 100, dynamic: true
23
+ v2 = view 200, 100, static: true
24
+ win&.add v1
25
+ win&.add v2
26
+ [v1, v2, win, win&.root]
27
+ end
28
+
29
+ def test_initial_state()
30
+ v1, v2, = setup_views
31
+
32
+ assert_equal v1, v1.chase(v2) .pins[0].view
33
+ assert_nil v1.chase(v2) .pins[1].view
34
+ assert_equal v2, v1.chase(v2) .target.view
35
+ assert_nil v1.chase(v2) .force
36
+ assert_equal 1, v1.chase(v2, force: 1) .force
37
+ assert_equal 5, v1.chase(v2) .spring
38
+ assert_equal 2, v1.chase(v2, spring: 2) .spring
39
+ assert_in_delta 0.7, v1.chase(v2) .damping
40
+ assert_in_delta 0.1, v1.chase(v2, damping: 0.1).damping
41
+ assert_false v1.chase(v2) .collide?
42
+ assert_true v1.chase(v2) .active?
43
+ assert_false v1.chase(v2) .removed?
44
+ end
45
+
46
+ def test_chase_view()
47
+ v1, v2, _, root = setup_views
48
+
49
+ c = v1.chase v2
50
+ assert_equal v2, c.target.view
51
+
52
+ root.update_world
53
+ assert_operator v1.linear_velocity.x, :>, 0
54
+ end
55
+
56
+ def test_chase_world_point()
57
+ v1, _, _, root = setup_views
58
+
59
+ c = v1.chase [300, 300]
60
+ assert_nil c.target.view
61
+ assert_equal_point point(300, 300), c.target.pos
62
+
63
+ root.update_world
64
+ assert_operator v1.linear_velocity.x, :>, 0
65
+ assert_operator v1.linear_velocity.y, :>, 0
66
+ end
67
+
68
+ def test_grabbing_corner_creates_torque()
69
+ v1, _, _, root = setup_views
70
+
71
+ c = v1.pin(0, 0).chase [300, 100], spring: 8
72
+ assert_equal_point point(0, 0), c.pins[0].pos
73
+
74
+ 3.times {root.update_world}
75
+ assert_operator v1.angular_velocity.abs, :>, 1
76
+ end
77
+
78
+ def test_chase_to_itself_error()
79
+ v1, v2, = setup_views
80
+
81
+ assert_raise(ArgumentError) {v1.chase v1}
82
+ assert_raise(ArgumentError) {v1.chase(v2).target = v1}
83
+ end
84
+
85
+ def test_target()
86
+ v1, v2, = setup_views
87
+
88
+ c = v1.chase [300, 300]
89
+ assert_nil c.target.view
90
+ assert_equal_point point(300, 300), c.target.pos
91
+
92
+ c.target = v2
93
+ assert_equal v2, c.target.view
94
+ assert_nil c.target.pos
95
+
96
+ c.target = v2.pin(0, 0)
97
+ assert_equal v2, c.target.view
98
+ assert_equal_point point(0, 0), c.target.pos
99
+
100
+ c.target = [v2, 5, 5]
101
+ assert_equal v2, c.target.view
102
+ assert_equal_point point(5, 5), c.target.pos
103
+
104
+ c.target = nil
105
+ assert_nil c.target.view
106
+ assert_nil c.target.pos
107
+ end
108
+
109
+ def test_force()
110
+ v1, = setup_views
111
+
112
+ c = v1.chase [300, 100], force: 500
113
+ assert_equal 500, c.force
114
+
115
+ c.force = nil
116
+ assert_nil c.force
117
+
118
+ c.force = 100
119
+ assert_equal 100, c.force
120
+
121
+ assert_raise(ArgumentError) {c.force = -1}
122
+ end
123
+
124
+ end# TestChaseConstraint
@@ -0,0 +1,54 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestClipboard < Test::Unit::TestCase
5
+
6
+ def setup()
7
+ @saved_text = Reflex::Clipboard.text
8
+ end
9
+
10
+ def teardown()
11
+ if @saved_text
12
+ Reflex::Clipboard.text = @saved_text
13
+ else
14
+ Reflex::Clipboard.clear
15
+ end
16
+ end
17
+
18
+ def test_initialize()
19
+ assert_raise(Reflex::ReflexError) {Reflex::Clipboard.new}
20
+ end
21
+
22
+ def test_text()
23
+ Reflex::Clipboard.text = 'ABC'
24
+ assert_equal 'ABC', Reflex::Clipboard.text
25
+ end
26
+
27
+ def test_text_utf8()
28
+ Reflex::Clipboard.text = 'あいう'
29
+ assert_equal 'あいう', Reflex::Clipboard.text
30
+ assert_equal Encoding::UTF_8, Reflex::Clipboard.text.encoding
31
+ end
32
+
33
+ def test_text_multiline()
34
+ Reflex::Clipboard.text = "A\nB\nC"
35
+ assert_equal "A\nB\nC", Reflex::Clipboard.text
36
+ end
37
+
38
+ def test_universal_accessor()
39
+ Reflex::Clipboard.text 'ABC'
40
+ assert_equal 'ABC', Reflex::Clipboard.text
41
+ end
42
+
43
+ def test_clear()
44
+ Reflex::Clipboard.text = 'ABC'
45
+ Reflex::Clipboard.clear
46
+ assert_nil Reflex::Clipboard.text
47
+ end unless linux?
48
+
49
+ def test_empty_text()
50
+ Reflex::Clipboard.text = ''
51
+ assert_equal '', Reflex::Clipboard.text
52
+ end unless linux?
53
+
54
+ end# TestClipboard
@@ -0,0 +1,186 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestConstraint < Test::Unit::TestCase
5
+
6
+ R = Reflex
7
+
8
+ def window()
9
+ R::Window.new
10
+ end
11
+
12
+ def view(x = 0, y = 0, w = 50, h = 50, **options, &block)
13
+ R::View.new(
14
+ frame: [x, y, w, h],
15
+ shape: R::RectShape.new(density: 1),
16
+ **options, &block)
17
+ end
18
+
19
+ def point(...)
20
+ R::Point.new(...)
21
+ end
22
+
23
+ def setup_views(win = window)
24
+ v1 = view 100, 100, dynamic: true
25
+ v2 = view 200, 100, static: true
26
+ win&.add v1
27
+ win&.add v2
28
+ [v1, v2, win, win&.root]
29
+ end
30
+
31
+ def test_activation()
32
+ v1, v2, = setup_views nil
33
+ w = window
34
+
35
+ c = v1.link v2; assert_false c.active?
36
+ w.add v1; assert_false c.active?
37
+ w.add v2; assert_true c.active?
38
+ w.remove v1; assert_false c.active?
39
+ w.add v1; assert_true c.active?
40
+ c = v1.link v2; assert_true c.active?
41
+
42
+ assert_in_delta 100, c.current_distance
43
+ end
44
+
45
+ def test_pins()
46
+ v1, v2, = setup_views
47
+
48
+ c = v1.pin(10, 20).snap v2.pin(30, 40)
49
+ assert_equal v1, c.pins[0].view
50
+ assert_equal_point point(10, 20), c.pins[0].pos
51
+ assert_equal v2, c.pins[1].view
52
+ assert_equal_point point(30, 40), c.pins[1].pos
53
+ assert_equal [v1, v2], c.views
54
+ assert_equal [v1, v2], c.pins.map(&:view)
55
+ end
56
+
57
+ def test_resolved_positions()
58
+ v1, v2, = setup_views
59
+
60
+ assert_equal_point point( 25, 25), v1.pin .snap(v2) .pins[0].pos
61
+ assert_equal_point point(-75, 25), v1.pin .snap(v2) .pins[1].pos
62
+ assert_equal_point point( 10, 20), v1.pin(10, 20).snap(v2) .pins[0].pos
63
+ assert_equal_point point(-90, 20), v1.pin(10, 20).snap(v2) .pins[1].pos
64
+ assert_equal_point point( 130, 40), v1.pin .snap(v2, 30, 40).pins[0].pos
65
+ assert_equal_point point( 30, 40), v1.pin .snap(v2, 30, 40).pins[1].pos
66
+ assert_equal_point point( 50, 60), v1.pin(50, 60).snap(v2, 70, 80).pins[0].pos
67
+ assert_equal_point point( 70, 80), v1.pin(50, 60).snap(v2, 70, 80).pins[1].pos
68
+ end
69
+
70
+ def test_spring()
71
+ v1, v2, = setup_views
72
+
73
+ c = v1.link v2, spring: 4, damping: 0.5
74
+ assert_in_delta 4, c.spring
75
+ assert_in_delta 0.5, c.damping
76
+
77
+ c.spring = nil; assert_nil c.spring
78
+ c.spring = 8; assert_in_delta 8, c.spring
79
+ end
80
+
81
+ def test_collide()
82
+ v1, v2, = setup_views
83
+
84
+ assert_false v1.snap(v2) .collide?
85
+ assert_false v1.snap(v2, collide: false).collide?
86
+ assert_true v1.snap(v2, collide: true) .collide?
87
+ end
88
+
89
+ def test_selector()
90
+ v1, v2, = setup_views
91
+
92
+ c = v1.link v2, name: :name1, tag: [:tag1, :tag2]
93
+ assert_equal 'name1', c.name
94
+ assert_equal ['tag1', 'tag2'], c.tags.to_a
95
+ assert_true c.tag?(:tag1)
96
+ assert_true c.tag?('tag1')
97
+ assert_true c.tag?(:tag2)
98
+ assert_false c.tag?(:tag3)
99
+
100
+ c.name = :name2
101
+ assert_equal 'name2', c.name
102
+
103
+ assert_false c.tag?(:tag9)
104
+ c.add_tag :tag9
105
+ assert_true c.tag?(:tag9)
106
+ assert_equal ['tag1', 'tag2', 'tag9'], c.tags.to_a
107
+ c.remove_tag :tag9
108
+ assert_false c.tag?(:tag9)
109
+ assert_equal ['tag1', 'tag2'], c.tags.to_a
110
+
111
+ c.clear_tags
112
+ assert_equal [], c.tags.to_a
113
+ end
114
+
115
+ def test_block()
116
+ v1, v2, = setup_views
117
+
118
+ c = v1.link v2 do
119
+ distance 80
120
+ spring 4
121
+ end
122
+ assert_equal 80, c.distance
123
+ assert_in_delta 4, c.spring
124
+ end
125
+
126
+ def test_constraints()
127
+ v1, v2, = setup_views
128
+
129
+ c1 = v1.link v2
130
+ c2 = v1.snap v2
131
+ assert_equal [c1, c2], v1.constraints.to_a
132
+ assert_equal [c1, c2], v2.constraints.to_a
133
+ assert_equal [R::LinkConstraint, R::SnapConstraint], v1.constraints.map(&:class)
134
+ end
135
+
136
+ def test_remove()
137
+ v1, v2, _, parent = setup_views
138
+
139
+ c = v1.link v2
140
+ c.remove
141
+ assert_false c.active?
142
+ assert_true c.removed?
143
+ assert_equal [], v1.constraints.to_a
144
+ assert_equal [], v2.constraints.to_a
145
+
146
+ c.remove # idempotent
147
+
148
+ parent.remove v1
149
+ parent.add v1
150
+ assert_false c.active? # never comes back
151
+
152
+ assert_nothing_raised do
153
+ c.spring = 4
154
+ c.damping = 0.5
155
+ c.distance = 50
156
+ end
157
+ assert_false c.active? # removed constraint is inert
158
+ end
159
+
160
+ def test_constrain_to_itself_error()
161
+ setup_views.tap do |v,|
162
+ assert_raise(ArgumentError) {v.snap v}
163
+ end
164
+ setup_views.tap do |v,|
165
+ assert_raise(ArgumentError) {v.chase v}
166
+ assert_equal [], v.constraints.to_a # rejected self-constraint leaves no orphan
167
+ end
168
+ end
169
+
170
+ def test_world_mismatch_error()
171
+ win = window
172
+ parent1, child1 = view(0, 0, 500, 500), view(100, 100, dynamic: true)
173
+ parent2, child2 = view(0, 0, 500, 500), view(100, 100, dynamic: true)
174
+ parent1.add child1
175
+ parent2.add child2
176
+ win.add parent1
177
+ win.add parent2
178
+ child1.meter2pixel # create parent1 world
179
+ child2.meter2pixel # create parent2 world
180
+
181
+ assert_raise(R::PhysicsError) {child1.snap child2}
182
+ assert_equal [], child1.constraints.to_a # rejected pins leave no trace
183
+ assert_equal [], child2.constraints.to_a
184
+ end
185
+
186
+ end# TestConstraint
@@ -43,4 +43,10 @@ class TestKeyEvent < Test::Unit::TestCase
43
43
  assert_nil event(DOWN, nil, 1, 2, 3).chars
44
44
  end
45
45
 
46
+ def test_modifiers()
47
+ e = event DOWN, 'a', 1, Reflex::MOD_CONTROL | Reflex::MOD_CAPS, 0
48
+ assert_equal [:control, :caps], e.modifiers
49
+ assert_equal [:control], e.modifiers(locks: false)
50
+ end
51
+
46
52
  end# TestKeyEvent
@@ -0,0 +1,239 @@
1
+ require_relative 'helper'
2
+
3
+
4
+ class TestLinkConstraint < Test::Unit::TestCase
5
+
6
+ def window()
7
+ Reflex::Window.new
8
+ end
9
+
10
+ def view(x = 0, y = 0, w = 50, h = 50, **options, &block)
11
+ Reflex::View.new(
12
+ frame: [x, y, w, h],
13
+ shape: Reflex::RectShape.new(density: 1),
14
+ **options, &block)
15
+ end
16
+
17
+ def point(...)
18
+ Reflex::Point.new(...)
19
+ end
20
+
21
+ def setup_views(win = window)
22
+ v1 = view 100, 100, dynamic: true
23
+ v2 = view 200, 100, static: true
24
+ win&.add v1
25
+ win&.add v2
26
+ [v1, v2, win, win&.root]
27
+ end
28
+
29
+ def test_initial_state()
30
+ v1, v2, = setup_views
31
+
32
+ assert_equal v1, v1.link(v2) .pins[0].view
33
+ assert_equal v2, v1.link(v2) .pins[1].view
34
+ assert_nil v1.link(v2) .axis
35
+ assert_equal_point point(1, 0), v1.link(v2, axis: [1, 0]).axis
36
+ assert_equal 100, v1.link(v2) .distance
37
+ assert_equal 1, v1.link(v2, distance: 1) .distance
38
+ assert_equal 2, v1.link(v2, dist: 2) .distance
39
+ assert_nil v1.link(v2) .range
40
+ assert_equal 3..3, v1.link(v2, range: 3) .range
41
+ assert_equal 4..5, v1.link(v2, range: 4..5) .range
42
+ assert_nil v1.link(v2) .motor
43
+ assert_equal 6, v1.link(v2, motor: 6) .motor
44
+ assert_nil v1.link(v2) .force
45
+ assert_equal 8, v1.link(v2, force: 8) .force
46
+ assert_nil v1.link(v2) .spring
47
+ assert_equal 7, v1.link(v2, spring: 7) .spring
48
+ assert_in_delta 0.7, v1.link(v2) .damping
49
+ assert_in_delta 0.1, v1.link(v2, damping: 0.1).damping
50
+ assert_false v1.link(v2) .collide?
51
+ assert_true v1.link(v2) .active?
52
+ assert_false v1.link(v2) .removed?
53
+ end
54
+
55
+ def test_link_to_view()
56
+ v1, v2, _, root = setup_views
57
+ root.gravity(-900, 0)
58
+
59
+ c = v1.link v2, dist: 50
60
+ assert_equal v2, c.pins[1].view
61
+
62
+ 60.times {root.update_world}
63
+ assert_equal_point point(150, 100), v1.pos
64
+ assert_in_delta 50, c.current_distance, 0.01
65
+ end
66
+
67
+ def test_link_to_world()
68
+ v1, _, _, root = setup_views
69
+ root.gravity(-900, 0)
70
+
71
+ c = v1.pin.link [200, 125], dist: 50
72
+ assert_nil c.pins[1].view
73
+
74
+ 60.times {root.update_world}
75
+ assert_equal_point point(125, 100), v1.pos
76
+ assert_in_delta 50, c.current_distance, 0.01
77
+ end
78
+
79
+ def test_distance()
80
+ v1, v2, = setup_views
81
+
82
+ c = v1.link v2, dist: 80, spring: 4
83
+ assert_equal 80, c.distance
84
+ assert_equal 80, c.dist
85
+
86
+ c.dist = 90
87
+ assert_equal 90, c.distance
88
+ end
89
+
90
+ def test_range()
91
+ v1, v2, _, root = setup_views
92
+ root.gravity 0, 900
93
+
94
+ c = v1.link v2, distance: 100, range: 0..120, spring: 1, damping: 0
95
+ 60.times {root.update_world}
96
+ assert_operator c.current_distance, :<=, 121
97
+
98
+ c.range = nil
99
+ assert_nil c.range
100
+
101
+ assert_raise(ArgumentError) {c.range = 'invalid'}
102
+ end
103
+
104
+ def test_motor()
105
+ v1, v2, = setup_views
106
+
107
+ c = v1.link v2, motor: 10
108
+ assert_equal 10, c.motor
109
+
110
+ c.motor = nil
111
+ assert_nil c.motor
112
+ end
113
+
114
+ def test_force()
115
+ v1, v2, = setup_views
116
+
117
+ c = v1.link v2, motor: 10, force: 500
118
+ assert_equal 500, c.force
119
+
120
+ c.force = nil
121
+ assert_nil c.force
122
+
123
+ c.force = 100
124
+ assert_equal 100, c.force
125
+
126
+ assert_raise(ArgumentError) {c.force = -1}
127
+ end
128
+
129
+ def test_axis_to_view()
130
+ v1, v2, _, root = setup_views
131
+ root.gravity 900, 900
132
+
133
+ c = v1.link v2, axis: [1, 0]
134
+ assert_equal_point point(1, 0), c.axis
135
+ assert_equal v2, c.pins[1].view
136
+
137
+ root.update_world
138
+ assert_operator v1.linear_velocity.x, :>, 0
139
+ assert_in_delta 0, v1.linear_velocity.y # locked to the axis
140
+ end
141
+
142
+ def test_axis_to_world()
143
+ v1, _, _, root = setup_views
144
+ root.gravity 900, 900
145
+
146
+ c = v1.pin.link axis: [1, 0]
147
+ assert_nil c.pins[1].view
148
+
149
+ root.update_world
150
+ assert_operator v1.linear_velocity.x, :>, 0
151
+ assert_in_delta 0, v1.linear_velocity.y
152
+ end
153
+
154
+ def test_axis_locks_rotation()
155
+ v1, v2, _, root = setup_views
156
+
157
+ v1.link v2, axis: [1, 0]
158
+
159
+ v1.angular_velocity = 90
160
+ 3.times {root.update_world}
161
+ assert_in_delta 0, v1.angular_velocity # prismatic locks relative rotation
162
+ end
163
+
164
+ def test_axis_change()
165
+ v1, v2, _, root = setup_views
166
+ root.gravity 900, 900
167
+
168
+ c = v1.link v2, axis: [1, 0]
169
+ root.update_world
170
+ assert_operator v1.linear_velocity.x, :>, 0
171
+ assert_in_delta 0, v1.linear_velocity.y
172
+
173
+ c.axis = [0, 1]
174
+ assert_equal_point point(0, 1), c.axis
175
+
176
+ root.update_world
177
+ assert_operator v1.linear_velocity.y, :>, 0
178
+ end
179
+
180
+ def test_axis_morphs_between_distance_and_rail()
181
+ v1, v2, = setup_views
182
+
183
+ c = v1.link v2
184
+ assert_nil c.axis # a distance joint has no axis
185
+
186
+ c.axis = [1, 0]
187
+ assert_equal_point point(1, 0), c.axis # now a rail
188
+
189
+ c.axis = nil
190
+ assert_nil c.axis # back to a distance joint
191
+ end
192
+
193
+ def test_axis_distance()
194
+ v1, v2, _, root = setup_views
195
+
196
+ c = v1.link v2, axis: [1, 0], distance: 30, spring: 4, damping: 1
197
+ assert_equal 30, c.distance
198
+
199
+ 60.times {root.update_world}
200
+ assert_in_delta 30, c.current_distance, 1 # the spring drives to the rest
201
+ end
202
+
203
+ def test_axis_distance_is_rest_not_translation()
204
+ v1, v2, _, root = setup_views
205
+ root.gravity 900, 0
206
+
207
+ c = v1.link v2, axis: [1, 0]
208
+ 3.times {root.update_world}
209
+ assert_operator c.current_distance.abs, :>, 0 # slides along the axis
210
+ assert_in_delta 0, c.distance # but the rest stays put
211
+ end
212
+
213
+ def test_axis_range()
214
+ v1, v2, _, root = setup_views
215
+ root.gravity 0, 900
216
+
217
+ c = v1.pin(25, 25).link v2.pin(25, 25), axis: [0, 1], range: 100..200
218
+ assert_equal 100..200, c.range
219
+
220
+ 60.times {root.update_world}
221
+ assert_equal_point point(200, 300), v1.pos
222
+ end
223
+
224
+ def test_axis_motor()
225
+ v1, v2, _, root = setup_views
226
+ root.gravity(-900, 0)
227
+
228
+ c = v1.link v2, axis: [1, 0], motor: 100
229
+ assert_equal 100, c.motor
230
+
231
+ 3.times {root.update_world}
232
+ assert_operator v1.linear_velocity.x, :>, 1
233
+ assert_in_delta 0, v1.linear_velocity.y
234
+
235
+ c.motor = nil
236
+ assert_nil c.motor
237
+ end
238
+
239
+ end# TestLinkConstraint