chipmunk-ffi 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,10 +10,15 @@ module CP
10
10
  func :cpSimpleMotorNew, [:pointer, :pointer, CP_FLOAT], :pointer
11
11
 
12
12
  class SimpleMotor
13
- attr_reader :struct
13
+ include Constraint
14
+ struct_accessor SimpleMotorStruct, :rate
14
15
  def initialize(a_body, b_body, rate)
16
+ @body_a, @body_b = a_body, b_body
15
17
  @struct = SimpleMotorStruct.new(CP.cpSimpleMotorNew(
16
18
  a_body.struct.pointer,b_body.struct.pointer,rate))
17
19
  end
18
20
  end
19
21
  end
22
+
23
+ # Alias for compatibility with chipmunk C-Ruby bindings.
24
+ CP::Constraint::SimpleMotor = CP::SimpleMotor
@@ -18,10 +18,15 @@ module CP
18
18
  func :cpSlideJointNew, [:pointer, :pointer, Vect.by_value, Vect.by_value, CP_FLOAT, CP_FLOAT], :pointer
19
19
 
20
20
  class SlideJoint
21
- attr_reader :struct
21
+ include Constraint
22
+ struct_accessor SlideJointStruct, :anchr1, :anchr2, :min, :max
22
23
  def initialize(a_body, b_body, anchr_one, anchr_two, min, max)
24
+ @body_a, @body_b = a_body, b_body
23
25
  @struct = SlideJointStruct.new(CP.cpSlideJointNew(
24
26
  a_body.struct.pointer,b_body.struct.pointer,anchr_one.struct,anchr_two.struct,min,max))
25
27
  end
26
28
  end
27
29
  end
30
+
31
+ # Alias for compatibility with chipmunk C-Ruby bindings.
32
+ CP::Constraint::SlideJoint = CP::SlideJoint
@@ -30,7 +30,7 @@ module CP
30
30
 
31
31
  func :cpMomentForSegment, [CP_FLOAT,Vect.by_value,Vect.by_value], CP_FLOAT
32
32
  def self.moment_for_segment(m,v1,v2)
33
- cpMomentForCircle(m, v1.struct, v2.struct)
33
+ cpMomentForSegment(m, v1.struct, v2.struct)
34
34
  end
35
35
 
36
36
  func :cpMomentForPoly, [CP_FLOAT,:int,:pointer,Vect.by_value], CP_FLOAT
@@ -51,6 +51,7 @@ module CP
51
51
  func :cpResetShapeIdCounter, [], :void
52
52
  func :cpShapePointQuery, [:pointer, Vect.by_value], :int
53
53
  func :cpShapeSegmentQuery, [:pointer, Vect.by_value, Vect.by_value, :pointer], :int
54
+ func :cpPolyValidate, [:pointer, :int], :int
54
55
 
55
56
  module Shape
56
57
  class SegmentQueryInfo
@@ -200,6 +201,15 @@ module CP
200
201
  include Shape
201
202
  def initialize(body, verts, offset_vec)
202
203
  @body = body
204
+ verts = CP::Shape::Poly.make_vertices_valid(verts)
205
+ mem_pointer = CP::Shape::Poly.pointer_for_verts(verts)
206
+
207
+ ptr = CP.cpPolyShapeNew body.struct.pointer, verts.size, mem_pointer, offset_vec.struct
208
+ @struct = ShapeStruct.new ptr
209
+ set_data_pointer
210
+ end
211
+
212
+ def self.pointer_for_verts(verts)
203
213
  mem_pointer = FFI::MemoryPointer.new Vect, verts.size
204
214
  vert_structs = verts.collect{|s|s.struct}
205
215
 
@@ -209,9 +219,28 @@ module CP
209
219
  tmp.send(:put_bytes, 0, i.to_bytes, 0, size)
210
220
  tmp += size unless j == vert_structs.length-1 # avoid OOB
211
221
  }
212
- ptr = CP.cpPolyShapeNew body.struct.pointer, verts.size, mem_pointer, offset_vec.struct
213
- @struct = ShapeStruct.new ptr
214
- set_data_pointer
222
+ return mem_pointer
223
+ end
224
+
225
+ def self.strictly_valid_vertices?(verts)
226
+ mem_pointer = CP::Shape::Poly.pointer_for_verts(verts)
227
+ result = CP.cpPolyValidate(mem_pointer,verts.size)
228
+ return (result != 0)
229
+ end
230
+
231
+ def self.valid_vertices?(verts)
232
+ CP::Shape::Poly.strictly_valid_vertices?(verts) ||
233
+ CP::Shape::Poly.strictly_valid_vertices?(verts.reverse)
234
+ end
235
+
236
+ def self.make_vertices_valid(verts)
237
+ if CP::Shape::Poly.strictly_valid_vertices?(verts)
238
+ return verts
239
+ elsif CP::Shape::Poly.strictly_valid_vertices?(verts.reverse)
240
+ return verts.reverse
241
+ else
242
+ raise "Chipmunk-FFI Error: Vertices do not form convex polygon."
243
+ end
215
244
  end
216
245
  end
217
246
  end
@@ -8,6 +8,7 @@ module CP
8
8
  callback :cpCollisionPostSolveFunc, [:pointer,:pointer,:pointer], :int
9
9
  callback :cpCollisionSeparateFunc, [:pointer,:pointer,:pointer], :int
10
10
  callback :cpSpacePointQueryFunc, [:pointer,:pointer], :void
11
+ callback :cpSpaceSegmentQueryFunc, [:pointer, :float, Vect.by_value, :pointer], :void
11
12
 
12
13
  class CollisionHandlerStruct < NiceFFI::Struct
13
14
  layout(
@@ -26,6 +27,7 @@ module CP
26
27
  :elastic_iterations, :int,
27
28
  :gravity, Vect.by_value,
28
29
  :damping, CP_FLOAT,
30
+ :locked, :int,
29
31
  :stamp, :int,
30
32
  :static_shapes, :pointer,
31
33
  :active_shapes, :pointer,
@@ -42,7 +44,6 @@ module CP
42
44
  end
43
45
  end
44
46
 
45
-
46
47
  func :cpSpaceNew, [], :pointer
47
48
  func :cpSpaceFreeChildren, [:pointer], :void
48
49
 
@@ -71,7 +72,10 @@ module CP
71
72
  func :cpSpacePointQuery, [:pointer, Vect.by_value, :uint, :uint, :cpSpacePointQueryFunc, :pointer], :pointer
72
73
  func :cpSpacePointQueryFirst, [:pointer, Vect.by_value, :uint, :uint], :pointer
73
74
 
74
- class Space
75
+ func :cpSpaceSegmentQuery, [:pointer, Vect.by_value, Vect.by_value, :uint, :uint, :cpSpaceSegmentQueryFunc, :pointer], :int
76
+ func :cpSpaceSegmentQueryFirst, [:pointer, Vect.by_value, Vect.by_value, :uint, :uint, :pointer], :pointer
77
+
78
+ class Space
75
79
  attr_reader :struct
76
80
  def initialize
77
81
  @struct = SpaceStruct.new(CP.cpSpaceNew)
@@ -81,6 +85,7 @@ module CP
81
85
  @constraints = []
82
86
  @blocks = {}
83
87
  @callbacks = {}
88
+ @test_callbacks = Hash.new {|h,k| h[k] = {:begin => nil, :pre => nil, :post => nil, :sep => nil}}
84
89
  end
85
90
 
86
91
  def iterations
@@ -111,58 +116,58 @@ module CP
111
116
  @struct.gravity.pointer.put_bytes 0, v.struct.to_bytes, 0,Vect.size
112
117
  end
113
118
 
114
- def add_collision_func(a,b,&block)
115
- beg = nil
116
- pre = nil
117
- unless block.nil?
118
- pre = Proc.new do |arb_ptr,space_ptr,data_ptr|
119
- begin
120
- arb = ArbiterStruct.new(arb_ptr)
121
-
122
- swapped = arb.swapped_col == 0 ? false : true
123
- arba = swapped ? arb.b : arb.a
124
- arbb = swapped ? arb.a : arb.b
125
-
126
- as = ShapeStruct.new(arba)
127
- a_obj_id = as.data.get_long 0
128
- rb_a = ObjectSpace._id2ref a_obj_id
129
-
130
- bs = ShapeStruct.new(arbb)
131
- b_obj_id = bs.data.get_long 0
132
- rb_b = ObjectSpace._id2ref b_obj_id
133
-
134
- block.call rb_a, rb_b
135
- 1
136
- rescue Exception => ex
137
- puts ex.message
138
- puts ex.backtrace
139
- 0
140
- end
141
- end
142
- else
143
- # needed for old chipmunk style
144
- pre = Proc.new do |arb_ptr,space_ptr,data_ptr|
145
- 0
146
- end
147
- end
148
- post = nil
149
- sep = nil
150
- data = nil
151
- a_id = a.object_id
152
- b_id = b.object_id
153
- CP.cpSpaceAddCollisionHandler(@struct.pointer, a_id, b_id,
154
- beg,pre,post,sep,data)
155
- @blocks[[a_id,b_id]] = pre
156
- nil
157
- end
158
-
159
- def remove_collision_func(a,b)
160
- a_id = a.object_id
161
- b_id = b.object_id
162
- CP.cpSpaceRemoveCollisionHandler(@struct.pointer, a_id, b_id)
163
- @blocks.delete [a_id,b_id]
164
- nil
165
- end
119
+ #def add_collision_func(a,b,&block)
120
+ # beg = nil
121
+ # pre = nil
122
+ # unless block.nil?
123
+ # pre = Proc.new do |arb_ptr,space_ptr,data_ptr|
124
+ # begin
125
+ # arb = ArbiterStruct.new(arb_ptr)
126
+ #
127
+ # swapped = arb.swapped_col == 0 ? false : true
128
+ # arba = swapped ? arb.b : arb.a
129
+ # arbb = swapped ? arb.a : arb.b
130
+ #
131
+ # as = ShapeStruct.new(arba)
132
+ # a_obj_id = as.data.get_long 0
133
+ # rb_a = ObjectSpace._id2ref a_obj_id
134
+ #
135
+ # bs = ShapeStruct.new(arbb)
136
+ # b_obj_id = bs.data.get_long 0
137
+ # rb_b = ObjectSpace._id2ref b_obj_id
138
+ #
139
+ # block.call rb_a, rb_b
140
+ # 1
141
+ # rescue Exception => ex
142
+ # puts ex.message
143
+ # puts ex.backtrace
144
+ # 0
145
+ # end
146
+ # end
147
+ # else
148
+ # # needed for old chipmunk style
149
+ # pre = Proc.new do |arb_ptr,space_ptr,data_ptr|
150
+ # 0
151
+ # end
152
+ # end
153
+ # post = nil
154
+ # sep = nil
155
+ # data = nil
156
+ # a_id = a.object_id
157
+ # b_id = b.object_id
158
+ # CP.cpSpaceAddCollisionHandler(@struct.pointer, a_id, b_id,
159
+ # beg,pre,post,sep,data)
160
+ # @blocks[[a_id,b_id]] = pre
161
+ # nil
162
+ #end
163
+
164
+ #def remove_collision_func(a,b)
165
+ # a_id = a.object_id
166
+ # b_id = b.object_id
167
+ # CP.cpSpaceRemoveCollisionHandler(@struct.pointer, a_id, b_id)
168
+ # @blocks.delete [a_id,b_id]
169
+ # nil
170
+ #end
166
171
 
167
172
  def set_default_collision_func(&block)
168
173
  raise "Not Implmented yet"
@@ -183,6 +188,7 @@ module CP
183
188
  ret ? 1 : 0
184
189
  end
185
190
  @callbacks[[a,b,type]] = [handler,callback]
191
+ @test_callbacks[[a,b]][type] = callback
186
192
  callback
187
193
  end
188
194
 
@@ -201,6 +207,35 @@ module CP
201
207
  a_id, b_id, beg,pre,post,sep,data)
202
208
  end
203
209
 
210
+ def add_collision_func(a,b,type=:pre,&block)
211
+ arity = block.arity
212
+ callback = Proc.new do |arb_ptr,space_ptr,data_ptr|
213
+ arbiter = Arbiter.new(arb_ptr)
214
+ ret = case arity
215
+ when 1 then block.call(arbiter)
216
+ when 2 then block.call(*arbiter.shapes)
217
+ when 3 then block.call(arbiter,*arbiter.shapes)
218
+ else raise ArgumentError
219
+ end
220
+ ret ? 1 : 0
221
+ end
222
+ @test_callbacks[[a,b]][type] = callback
223
+ setup_callbacks(a,b)
224
+ end
225
+
226
+ def remove_collision_func(a,b,type=:pre)
227
+ @test_callbacks[[a,b]][type] = nil
228
+ setup_callbacks(a,b)
229
+ end
230
+
231
+ def setup_callbacks(a,b)
232
+ a_id = a.object_id
233
+ b_id = b.object_id
234
+ cbs = @test_callbacks[[a,b]]
235
+ CP.cpSpaceAddCollisionHandler(@struct.pointer,a_id,b_id,
236
+ cbs[:begin],cbs[:pre],cbs[:post],cbs[:sep],nil)
237
+ end
238
+
204
239
  def add_shape(shape)
205
240
  CP.cpSpaceAddShape(@struct.pointer, shape.struct.pointer)
206
241
  @active_shapes << shape
@@ -307,6 +342,53 @@ module CP
307
342
  CP.cpSpacePointQuery(@struct.pointer, point.struct, layers, group,query_proc,nil)
308
343
  end
309
344
 
345
+ class SegmentQueryInfo
346
+ attr_reader :hit,:shape, :t, :n
347
+ def initialize(hit,shape,t=nil,n=nil,info=nil)
348
+ @hit = hit
349
+ @shape = shape
350
+ @t = t
351
+ @n = n
352
+ @info = info
353
+ end
354
+ end
355
+
356
+ def shape_segment_query(a,b,layers=ALL_ONES,group=0)
357
+ segment_query_first(a,b,layers,group).shape
358
+ end
359
+
360
+ def info_segment_query(a,b,layers=ALL_ONES,group=0)
361
+ segment_query_first(a,b,layers,group)
362
+ end
363
+
364
+ def segment_query_first(a,b,layers,group)
365
+ out_ptr = FFI::MemoryPointer.new(SegmentQueryInfoStruct.size)
366
+ info = SegmentQueryInfoStruct.new out_ptr
367
+
368
+ shape_ptr = CP.cpSpaceSegmentQueryFirst(@struct.pointer, a.struct.pointer, b.struct.pointer,layers,group,out_ptr)
369
+ if shape_ptr.null?
370
+ SegmentQueryInfo.new(false,nil,nil,nil,info)
371
+ else
372
+ shape_struct = ShapeStruct.new(shape_ptr)
373
+ obj_id = shape_struct.data.get_long(0)
374
+ shape = ObjectSpace._id2ref(obj_id)
375
+ n_vec = Vec2.new(info.n)
376
+ SegmentQueryInfo.new(true,shape,info.t,n_vec,info)
377
+ end
378
+ end
379
+
380
+ def segment_query(a,b,layers,group,&block)
381
+ return nil unless block_given?
382
+ query_proc = Proc.new do |shape_ptr,t,n,data|
383
+ shape_struct = ShapeStruct.new(shape_ptr)
384
+ obj_id = shape_struct.data.get_long(0)
385
+ shape = ObjectSpace._id2ref(obj_id)
386
+ block.call(shape,t,n)
387
+ end
388
+
389
+ CP.cpSpaceSegmentQuery(@struct.pointer, a.struct, b.struct, layers, group, query_proc, nil)
390
+ end
391
+
310
392
  end
311
393
  end
312
394
 
@@ -8,8 +8,10 @@ module CP
8
8
  :cell_dim, CP_FLOAT,
9
9
  :bb_func, :cpSpaceHashBBFunc,
10
10
  :handle_set, :pointer,
11
+ :pooled_handles, :pointer,
11
12
  :table, :pointer,
12
13
  :bins, :pointer,
14
+ :allocated_buffers, :pointer,
13
15
  :stamp, :int)
14
16
  end
15
17
  func :cpSpaceHashNew, [CP_FLOAT,:int,:cpSpaceHashBBFunc], :pointer
@@ -0,0 +1,48 @@
1
+ module CP
2
+ module StructAccessor
3
+ private
4
+
5
+ def struct_reader(struct,*args)
6
+ raise(ArgumentError,"First argument must be an FFI::Struct subclass, got #{struct.inspect} instead.") unless struct < FFI::Struct
7
+ args.each {|attribute| add_struct_reader(struct,attribute) }
8
+ end
9
+
10
+ def struct_writer(struct,*args)
11
+ raise(ArgumentError,"First argument must be an FFI::Struct subclass, got #{struct.inspect} instead.") unless struct < FFI::Struct
12
+ args.each {|attribute| add_struct_writer(struct,attribute) }
13
+ end
14
+
15
+ def struct_accessor(struct,*args)
16
+ struct_reader(struct,*args)
17
+ struct_writer(struct,*args)
18
+ end
19
+
20
+ def add_struct_reader(struct,attribute)
21
+ type = resolve_type(struct,attribute)
22
+ if type == CP::Vect
23
+ define_method(attribute) { Vec2.new(self.struct[attribute]) }
24
+ else
25
+ define_method(attribute) { self.struct[attribute] }
26
+ end
27
+ end
28
+
29
+ def add_struct_writer(struct,attribute)
30
+ type = resolve_type(struct,attribute)
31
+ if type == CP::Vect
32
+ define_method("#{attribute}=") {|val| self.struct[attribute].pointer.put_bytes(0,val.struct.to_bytes,0,Vect.size) }
33
+ else
34
+ define_method("#{attribute}=") {|val| self.struct[attribute] = val }
35
+ end
36
+ end
37
+
38
+ def resolve_type(struct,attribute)
39
+ t = struct.layout[attribute].type
40
+ if t.is_a?(FFI::StructByValue)
41
+ t.struct_class
42
+ else
43
+ t
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -195,9 +195,15 @@ module CP
195
195
  CP::cpvlength @struct
196
196
  end
197
197
 
198
+ def ==(other)
199
+ self.x == other.x && self.y == other.y
200
+ end
201
+
198
202
  end
199
203
  ZERO_VEC_2 = Vec2.new(0,0).freeze
200
-
204
+ def zero
205
+ ZERO_VEC_2
206
+ end
201
207
  end
202
208
  def vec2(x,y)
203
209
  CP::Vec2.new x, y
@@ -152,5 +152,49 @@ describe 'A new Body' do
152
152
  b.v.x.should be_close(0.1,0.001)
153
153
  b.v.y.should be_close(0,0.001)
154
154
  end
155
+
156
+ it 'can get its velocity function' do
157
+ b = CP::Body.new(5, 7)
158
+ b.apply_impulse(vec2(1,0), ZERO_VEC_2)
159
+ b.velocity_func.call(b,vec2(0,0), 0.5, 25)
160
+ b.v.x.should be_close(0.1,0.001)
161
+ b.v.y.should be_close(0,0.001)
162
+ end
163
+
164
+ it 'can set its velocity function' do
165
+ b = CP::Body.new(5, 7)
166
+
167
+ b.velocity_func = Proc.new do |body,gravity,damping,dt|
168
+ d = body.p - vec2(5,5)
169
+ g = (d * (-500.0/(d.dot(d))))
170
+ body.update_velocity(g,damping,dt)
171
+ end
172
+
173
+ b.velocity_func.call(b,ZERO_VEC_2,0.5,25)
174
+ b.v.x.should be_close(1250.0,10)
175
+ b.v.y.should be_close(1250.0,10)
176
+
177
+ end
178
+
179
+ it 'can get its position function' do
180
+ b = CP::Body.new(5, 7)
181
+ b.apply_impulse(vec2(1,0), ZERO_VEC_2)
182
+ b.position_func.call(b,25)
183
+ b.p.x.should be_close(5,0.001)
184
+ b.p.y.should be_close(0,0.001)
185
+ end
186
+
187
+ it 'can set its position function' do
188
+ b = CP::Body.new(5,7)
189
+
190
+ b.position_func = Proc.new do |body,dt|
191
+ body.p = vec2(5,5)
192
+ end
193
+
194
+ b.position_func.call(b,25)
195
+ b.p.should == vec2(5,5)
196
+ end
197
+
198
+
155
199
 
156
200
  end