Jamoma API  0.6.0.a19
j.push.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup implementationMaxExternals
4  *
5  * @brief j.push : Simple physical modelling: Push an object about within a confined space.
6  *
7  * @details Can be used for e.g. trajectories.
8  *
9  * @authors Trond Lossius
10  *
11  * @copyright © 2011 by Trond Lossius @n
12  * This code is licensed under the terms of the "New BSD License" @n
13  * http://creativecommons.org/licenses/BSD/
14  */
15 
16 
17 #include "JamomaForMax.h"
18 #include "TTDSP.h" // use the Jamoma DSP clipping functions
19 
20 
21 #define nonzero(x) ((x > 0) ? x : 1.)
22 #define MAXDIMENSIONS 10
23 #define CLOSETOBORDER 0.9
24 
25 t_symbol* ps_clip;
26 t_symbol* ps_bounce;
27 t_symbol* ps_repel;
28 
29 typedef struct _push{ ///< Data structure for this object
30  t_object ob; ///< Must always be the first field; used by Max
31  float previousPosition[MAXDIMENSIONS]; ///< The previous position
32  float previousVelocity[MAXDIMENSIONS]; ///< The previous velocity
33  float attrFriction; ///< Friction coefficient
34  int attrDimensions; ///< The number of dimensions used
35  float attrSize[MAXDIMENSIONS]; ///< Room size, defined individually for each dimension
36  t_symbol* attrBoundaryMode; ///< Boundary mode
37  float force[MAXDIMENSIONS]; ///< Force applied on the object
38  void *outlet; ///< Pointer to outlet. Need one for each outlet
39 } t_push;
40 
41 // Prototypes for methods: need a method for each incoming message
42 void *push_new(t_symbol *msg, long argc, t_atom *argv);
43 void push_bang(t_push *x);
44 void push_force(t_push *x, t_symbol *s, long argc, t_atom *argv);
45 void push_position(t_push *x, t_symbol *s, long argc, t_atom *argv);
46 void push_clear(t_push *x);
47 void push_assist(t_push *x, void *b, long msg, long arg, char *dst);
48 t_max_err push_attr_setdimensions(t_push *x, void *attr, long argc, t_atom *argv);
49 t_max_err push_attr_setfriction(t_push *x, void *attr, long argc, t_atom *argv);
50 t_max_err push_attr_setsize(t_push *x, void *attr, long argc, t_atom *argv);
51 t_max_err push_attr_getsize(t_push *x, void *attr, long *argc, t_atom **argv);
52 t_max_err push_attr_setBoundaryMode(t_push *x, void *attr, long argc, t_atom *argv);
53 
54 
55 // Globals
56 t_class *this_class; // Required. Global pointing to this class
57 
58 
59 
60 /************************************************************************************/
61 // Main() Function
62 
63 int JAMOMA_EXPORT_MAXOBJ main(void)
64 {
65  t_class *c;
66 
67  common_symbols_init();
68  ps_clip = gensym("clip");
69  ps_bounce = gensym("bounce");
70  ps_repel = gensym("repel");
71 
72  // Define our class
73  c = class_new("j.push",(method)push_new, (method)0L, sizeof(t_push), (method)0L, A_GIMME, 0);
74 
75  // Make methods accessible for our class:
76  class_addmethod(c, (method)push_bang, "bang", A_CANT, 0);
77  class_addmethod(c, (method)push_force, "force", A_GIMME, 0);
78  class_addmethod(c, (method)push_position, "position", A_GIMME, 0);
79  class_addmethod(c, (method)push_clear, "clear", 0);
80  class_addmethod(c, (method)push_assist, "assist", A_CANT, 0);
81  class_addmethod(c, (method)object_obex_dumpout, "dumpout", A_CANT, 0);
82 
83  CLASS_ATTR_FLOAT(c, "friction", 0, t_push, attrFriction);
84  CLASS_ATTR_ACCESSORS(c, "friction", NULL, push_attr_setfriction);
85 
86  // Add attributes to our class:
87  CLASS_ATTR_LONG(c, "dimensions", 0, t_push, attrDimensions);
88  CLASS_ATTR_ACCESSORS(c, "dimensions", NULL, push_attr_setdimensions);
89 
90  CLASS_ATTR_ATOM(c, "size", 0, t_push, attrSize);
91  CLASS_ATTR_ACCESSORS(c, "size", push_attr_getsize, push_attr_setsize);
92 
93  // ATTRIBUTE: boundary mode - options are none, clip, wrap, fold, repel
94  CLASS_ATTR_SYM( c, "boundary", 0, t_push, attrBoundaryMode);
95  CLASS_ATTR_ACCESSORS(c, "boundary", NULL, push_attr_setBoundaryMode);
96  CLASS_ATTR_ENUM(c, "boundary", 0, (char*)"none clip bounce fold repel");
97 
98  // Finalize our class
99  class_register(CLASS_BOX, c);
100  this_class = c;
101  return 0;
102 }
103 
104 #pragma mark -
105 #pragma mark Object life
106 /************************************************************************************/
107 // Object Life
108 
109 void *push_new(t_symbol *msg, long argc, t_atom *argv)
110 {
111  t_push *x;
112  int i;
113 
114  x = (t_push *)object_alloc(this_class); // create the new instance and return a pointer to it
115  if (x) {
116  // create inlets and outlets
117  object_obex_store((void *)x, _sym_dumpout, (object *)outlet_new(x,NULL)); // dumpout
118  x->outlet = floatout(x); // velocity
119  x->attrDimensions = 3;
120  x->attrFriction = 0.1;
121  for (i=0; i<MAXDIMENSIONS; i++)
122  x->attrSize[i] = 40.0; // This is the same default as for ViMiC
123  x->attrBoundaryMode = gensym("none");
124  push_clear(x); // initilaize instance
125  attr_args_process(x, argc, argv); // handle attribute args
126 
127  }
128  return (x);
129 }
130 
131 
132 /************************************************************************************/
133 // Methods bound to attributes
134 #pragma mark -
135 #pragma mark attribute accessors
136 
137 
138 // ATTRIBUTE: dimensions
139 t_max_err push_attr_setdimensions(t_push *x, void *attr, long argc, t_atom *argv)
140 {
141  long n;
142 
143  if (argc && argv) {
144  n = atom_getlong(argv);
145  if (n<1) n = 1;
146  if (n>MAXDIMENSIONS) n = MAXDIMENSIONS;
147  x->attrDimensions = n;
148  }
149  push_clear(x);
150  return MAX_ERR_NONE;
151 }
152 
153 
154 // ATTRIBUTE: dimensions (1-3)
155 t_max_err push_attr_setfriction(t_push *x, void *attr, long argc, t_atom *argv)
156 {
157  float f;
158 
159  if (argc && argv) {
160  f = atom_getfloat(argv);
161  if (f<=0.0 || f>=1.0) {
162  error("Invalid argument for friction. Must be in the range (0, 1)");
163  return MAX_ERR_NONE;
164  }
165  x->attrFriction = f;
166  }
167  return MAX_ERR_NONE;
168 }
169 
170 
171 t_max_err push_attr_setsize(t_push *x, void *attr, long argc, t_atom *argv)
172 {
173  int i;
174  float f;
175 
176  if ((argc==x->attrDimensions) && argv) {
177  for (i=0; i<x->attrDimensions; i++) {
178  f = atom_getfloat(argv);
179  if (f>0.0) {
180  x->attrSize[i] = f;
181  argv++;
182  }
183  else
184  post("j.push: Ignored faulty value for size");
185  }
186  }
187  return MAX_ERR_NONE;
188 }
189 
190 t_max_err push_attr_getsize(t_push *x, void *attr, long *argc, t_atom **argv)
191 {
192  int i;
193 
194  *argc = x->attrDimensions;
195  *argv = (t_atom*)sysmem_newptr((sizeof(t_atom))*x->attrDimensions);
196  for (i=0; i<x->attrDimensions; i++) {
197  atom_setfloat(*argv+i, x->attrSize[i]);
198  }
199 
200  return MAX_ERR_NONE;
201  // TODO: We need to free the memory assigned for argv
202 }
203 
204 
205 t_max_err push_attr_setBoundaryMode(t_push *x, void *attr, long argc, t_atom *argv)
206 {
207  t_symbol* s;
208 
209  if (argc && argv) {
210  s = atom_getsym(argv);
211  if ((s==gensym("none")) || (s==ps_clip) || (s==gensym("wrap")) || (s==ps_bounce) || (s==ps_repel))
212  x->attrBoundaryMode = s;
213  }
214  return MAX_ERR_NONE;
215 }
216 
217 
218 #pragma mark -
219 #pragma mark methods
220 /************************************************************************************/
221 // Methods bound to input/inlets
222 
223 
224 // BANG input
225 void push_bang(t_push *x)
226 {
227  int i;
228  float position, velocity, vicinity, repel;
229  float rangeLow, rangeHigh;
230  t_atom *a = NULL;
231  a = (t_atom*)sysmem_newptr(sizeof(Atom) * x->attrDimensions);
232 
233  for (i=0; i<x->attrDimensions; i++) {
234  // Determine range
235  rangeHigh = 0.5*x->attrSize[i];
236  rangeLow = -rangeHigh;
237 
238  // repel mode: Push away from border
239  if (x->attrBoundaryMode == ps_repel) {
240  // Are we in the vicinity of a border?
241  vicinity = fabs(x->previousPosition[i])/rangeHigh;
242  vicinity = (vicinity-CLOSETOBORDER)/(1.0-CLOSETOBORDER);
243  TTLimit(vicinity, (float)0.0, (float)1.0);
244  repel = vicinity*x->previousPosition[i]/rangeHigh;
245  }
246  else
247  repel = 0.0;
248 
249  // Numerical integration
250  position = x->previousPosition[i] + (1.0-x->attrFriction)*x->previousVelocity[i] + x->force[i] - repel;
251  velocity = position - x->previousPosition[i];
252 
253  // Boundary conditions
254  if ((x->attrBoundaryMode==ps_clip) || (x->attrBoundaryMode==ps_repel)) {
255  TTLimit(position, rangeLow, rangeHigh);
256  velocity = position - x->previousPosition[i];
257  }
258  else if (x->attrBoundaryMode == gensym("wrap"))
259  TTInfWrap(position, rangeLow, rangeHigh);
260  else if (x->attrBoundaryMode == ps_bounce) {
261  // Do we need to fold? The following test works because of symetry while avoiding fabs()
262  if ((position*position) >= (rangeHigh*rangeHigh)) {
263  TTFold(position, rangeLow, rangeHigh);
264  // Invert direction of velocity
265  velocity = -velocity;
266  }
267  }
268 
269  x->previousPosition[i] = position;
270  x->previousVelocity[i] = velocity;
271  x->force[i] = 0; // Force is reset to zero when it has been applied
272  atom_setfloat(&a[i], position);
273  }
274 
275 
276  outlet_anything(x->outlet, _sym_list, x->attrDimensions, a);
277  sysmem_freeptr(a);
278 }
279 
280 
281 // FORCE input
282 void push_force(t_push *x, t_symbol *s, long argc, t_atom *argv)
283 {
284  int i;
285 
286  if (argc==x->attrDimensions) {
287  for (i=0; i<x->attrDimensions; i++) {
288  x->force[i] = atom_getfloat(argv);
289  argv++;
290  }
291  }
292  else {
293  post("j.push: force vector has wrong dimension");
294  return;
295  }
296 }
297 
298 
299 // CLEAR input
300 void push_clear(t_push *x)
301 {
302  int i;
303 
304  for (i=0; i< x->attrDimensions; i++) {
305  x->previousPosition[i] = 0.0;
306  x->previousVelocity[i] = 0.0;
307  x->force[i] = 0.0;
308  }
309  push_bang(x);
310 }
311 
312 
313 // POSITION input
314 void push_position(t_push *x, t_symbol *s, long argc, t_atom *argv)
315 {
316  int i;
317  float f, min, max;
318 
319  if ((argc==x->attrDimensions) && argv) {
320  for (i=0; i<x->attrDimensions; i++) {
321  max = 0.5*x->attrSize[i];
322  min = -max;
323  f = atom_getfloat(argv);
324  TTLimit(f, min, max);
325  x->previousPosition[i] = f;
326  x->previousVelocity[i] = 0.0;
327  x->force[i] = 0.0;
328  argv++;
329  }
330  }
331  push_bang(x);
332 }
333 
334 
335 // Method for Assistance Messages
336 void push_assist(t_push *x, void *b, long msg, long arg, char *dst) // Display assistance messages
337 {
338  if (msg==1)
339  {
340  switch(arg)
341  {
342  case 0:
343  strcpy(dst, "(list) force applied to object");
344  break;
345  }
346  }
347  else if (msg==2)
348  {
349  switch(arg)
350  {
351  case 0:
352  strcpy(dst, "(list) resulting position of object");
353  break;
354  case 1:
355  strcpy(dst, "dumpout");
356  break;
357  }
358  }
359 }
Jamoma DSP Library.
Various utilities for interfacing with Max that are not specific to JamomaModular as such...
int JAMOMA_EXPORT_MAXOBJ main(void)
Set up this class as a Max external the first time an object of this kind is instantiated.
Definition: j.push.cpp:63
t_class * this_class
Required. Global pointing to this class.
Definition: j.envexp.cpp:108