I'm trying to get the Chipmunk Physics library integrated with Racket.  
I've made some progress (https://www.youtube.com/watch?v=GjvLaP7I0gg), but 
I need to ask for some input on an issue.

The Chipmunk C library provides a primitive called a "cpBody" (i.e. a 
moving thing).  A cpBody struct has a velocity_func pointer which allows 
customization of how the body's velocity is updated during the simulation.

I have noticed odd behavior with these function pointers.  Perhaps this is 
a bug with the FFI?  Perhaps it's perfectly normal and I just don't 
understand something about the FFI?

In any event, when I have one body, this works:

(define body1
  (box 25 5 (cpv 25.0 80.0)
       #:mass 10
       #:meta 'green))

(define (f body gravity damping dt)
  (ffi:cpointer-push-tag! body 'cpBody)
  (cpBodySetVelocity body (cpv 0 -10))
    
  ffi:_void)

(set-cpBody-velocity_func! (chipmunk-body body1)
                           f)


But when I have two bodies that share the same velocity_func, I get a 
segfault:

(define body1
  (box 25 5 (cpv 25.0 80.0)
       #:mass 10
       #:meta 'green))

(define body2
  (box 25 5 (cpv 50.0 80.0)
       #:mass 10
       #:meta 'blue))


(define (f body gravity damping dt)
  (ffi:cpointer-push-tag! body 'cpBody)
  (cpBodySetVelocity body (cpv 0 -10))
    
  ffi:_void)

(set-cpBody-velocity_func! (chipmunk-body body1)
                           f)

(set-cpBody-velocity_func! (chipmunk-body body2)
                           f)


I've tried various ways to work around this issue.  But all of them either 
didn't work or feel dumb.


*Things that didn't work*

1) Wrapping the function f in separate lambdas still gives a segfault:

(set-cpBody-velocity_func! (chipmunk-body body1)
                           (lambda(b g d dt)
                             (f b d g dt)))

(set-cpBody-velocity_func! (chipmunk-body body2)
                           (lambda(b g d dt)
                             (f b d g dt)))


2) Storing a reference to the function f in a stab-in-the-dark attempt to 
"trick the system" or "prevent some kind of mysterious garbage collection" 
still segfaults:

(define probably-silly
  (list
    (lambda(b g d dt)
      (f b d g dt))
    (lambda(b g d dt)
      (f b d g dt))))

(set-cpBody-velocity_func! (chipmunk-body body1)
                           (list-ref probably-silly 0))

(set-cpBody-velocity_func! (chipmunk-body body2)
                           (list-ref probably-silly 1))

3) Storing the function f in two separate variables also segfaults:

(define (f body gravity damping dt)
  (ffi:cpointer-push-tag! body 'cpBody)
  (cpBodySetVelocity body (cpv 0 -10))
    
  ffi:_void)

(define f2 f)

(set-cpBody-velocity_func! (chipmunk-body body1)
                           f)

(set-cpBody-velocity_func! (chipmunk-body body2)
                           f2)



*Things that do work but feel dumb*

1) Explicitly duplicating the function verbatim:

(define (f body gravity damping dt)
  (ffi:cpointer-push-tag! body 'cpBody)
  (cpBodySetVelocity body (cpv 0 -10))
    
  ffi:_void)

(define (f2 body gravity damping dt)
  (ffi:cpointer-push-tag! body 'cpBody)
  (cpBodySetVelocity body (cpv 0 -10))
    
  ffi:_void)

(set-cpBody-velocity_func! (chipmunk-body body1)
                           f)

(set-cpBody-velocity_func! (chipmunk-body body2)
                           f2)

2) Duplicating the function with a macro lets me generalize the above 
workaround for more than two bodies, but it still feels gross.  I know the 
macro could be improved, but that isn't really the point.  The fact that I 
have to know how many  functions to make at compile time is gross.

;The stupid macro....
(define-syntax-rule (dumb-duplicate id body)
  (define id
    (list
     body
     body
     body
     body
     body)))

(dumb-duplicate fs
                (lambda (body gravity damping dt)
                  (f body gravity damping dt)))

(set-cpBody-velocity_func! (chipmunk-body body1)
                           (list-ref fs 0))

(set-cpBody-velocity_func! (chipmunk-body body2)
                           (list-ref fs 1))


That's all I have.  To sum up, sharing the same function pointer across two 
cpBodies seems to segfault to matter what clever tricks I try.  Using 
functions with distinct compile-time source locations does work, but it 
makes me feel icky.  I would prefer to construct the functions I need 
dynamically rather than statically.  In simulations where objects are 
created at runtime, it feels sillier and sillier to have to know beforehand 
how many objects there will be at runtime.

If someone has an explanation or a better workaround, that would be great.  
I'm assuming there's something about the Racket FFI that I haven't learned 
yet.  However, if others are as mystified as I am, I'll assume it's a 
Chipmunk issue, and I'll ask about it over on the Chipmunk forums.  


*Appendix A*

Below is a full program with some rendering, just for context.

It uses this newborn git repo for the Racket FFI bindings: 
https://github.com/thoughtstem/racket-chipmunk

Also, here are the latest docs for Chipmunk: 
https://chipmunk-physics.net/release/ChipmunkLatest-API-Reference

(require 2htdp/image
         lang/posn)
(require 2htdp/universe)
(require racket-chipmunk)

(require (prefix-in ffi: ffi/unsafe))

(define body1
  (box 25 5 (cpv 25.0 80.0)
       #:mass 10
       #:meta 'green))

(define body2
  (box 25 5 (cpv 50.0 80.0)
       #:mass 10
       #:meta 'blue))


(define (f body gravity damping dt)
  (ffi:cpointer-push-tag! body 'cpBody)
  (cpBodySetVelocity body (cpv 0 -10))
    
  ffi:_void)

;The stupid macro....
(define-syntax-rule (dumb-duplicate id body)
  (define id
    (list
     body
     body
     body
     body
     body)))

(dumb-duplicate fs
                (lambda (body gravity damping dt)
                  (f body gravity damping dt)))

(set-cpBody-velocity_func! (chipmunk-body body1)
                           (list-ref fs 0))

(set-cpBody-velocity_func! (chipmunk-body body2)
                           (list-ref fs 1))

  
(define boxes
  (list body1 body2))

(define *tick-rate* (/ 1 120.0))
(define *canvas* (square 100 'solid 'white))

(define (box->color b)
  (or (chipmunk-meta b) 'red))

(define (render-box b)
  (rotate (* -57.29 (angle b))
          (rectangle (w b) (h b) 'solid (box->color b))))

(define (box->posn b)
  (make-posn (x b)
             (y b)))



(big-bang
    0
  [on-tick (lambda (state)
             (step-chipmunk *tick-rate*)
             (+ state 1))
           *tick-rate*]
  [on-draw (lambda (state)
             (scale 6
                    (place-images
                     (map render-box boxes)
                     (map box->posn boxes)
                     *canvas*)))])





-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to