aboutsummaryrefslogtreecommitdiff
path: root/externals/gridflow/base
diff options
context:
space:
mode:
authorN.N. <matju@users.sourceforge.net>2005-10-04 02:02:15 +0000
committerN.N. <matju@users.sourceforge.net>2005-10-04 02:02:15 +0000
commit5e2a1bc9e56003349e533f7e5841041ba5c04e28 (patch)
treead040f6894d9383b732423a74420e732f62a66a5 /externals/gridflow/base
parent520a243c297175386ab31c78c84693a664934a69 (diff)
starting to commit gridflow 0.8.0 ...
if you know how to use "cvs import" please mail me and i'll use it for 0.8.1 svn path=/trunk/; revision=3646
Diffstat (limited to 'externals/gridflow/base')
-rw-r--r--externals/gridflow/base/bitpacking.c309
-rw-r--r--externals/gridflow/base/flow_objects.c1189
-rw-r--r--externals/gridflow/base/flow_objects.rb1476
-rw-r--r--externals/gridflow/base/flow_objects_for_image.c618
-rw-r--r--externals/gridflow/base/flow_objects_for_matrix.c77
-rw-r--r--externals/gridflow/base/grid.c604
-rw-r--r--externals/gridflow/base/grid.h1109
-rw-r--r--externals/gridflow/base/main.c647
-rw-r--r--externals/gridflow/base/main.rb369
-rw-r--r--externals/gridflow/base/number.c365
-rw-r--r--externals/gridflow/base/source_filter.rb274
-rw-r--r--externals/gridflow/base/test.rb1086
12 files changed, 8123 insertions, 0 deletions
diff --git a/externals/gridflow/base/bitpacking.c b/externals/gridflow/base/bitpacking.c
new file mode 100644
index 00000000..143f6edb
--- /dev/null
+++ b/externals/gridflow/base/bitpacking.c
@@ -0,0 +1,309 @@
+/*
+ $Id: bitpacking.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "grid.h.fcs"
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define CONVERT1 t = \
+ (((in[0] << hb[0]) >> 7) & mask[0]) | \
+ (((in[1] << hb[1]) >> 7) & mask[1]) | \
+ (((in[2] << hb[2]) >> 7) & mask[2])
+
+#define CONVERT2 \
+ for (t=0,i=0; i<self->size; i++) t |= (((in[i] << hb[i]) >> 7) & mask[i]);
+
+#define CONVERT3 \
+ for (t=0,i=0; i<self->size; i++) { \
+ t |= ((in[i]>>(7-hb[i]))|(in[i]<<(hb[i]-7))) & mask[i]; \
+ }
+
+#define WRITE_LE \
+ for (int bytes = self->bytes; bytes; bytes--, t>>=8) *out++ = t;
+
+#define WRITE_BE { int bytes; \
+ bytes = self->bytes; \
+ while (bytes--) { out[bytes] = t; t>>=8; }\
+ out += self->bytes; }
+
+/* this macro would be faster if the _increment_
+ was done only once every loop. or maybe gcc does it, i dunno */
+#define NTIMES(_x_) \
+ for (; n>=4; n-=4) { _x_ _x_ _x_ _x_ } \
+ for (; n; n--) { _x_ }
+
+/* this could be faster (use asm) */
+void swap32 (int n, Pt<uint32> data) {
+ NTIMES({
+ uint32 x = *data;
+ x = (x<<16) | (x>>16);
+ x = ((x&0xff00ff)<<8) | ((x>>8)&0xff00ff);
+ *data++ = x;
+ })
+}
+
+/* this could be faster (use asm or do it in int32 chunks) */
+void swap16 (int n, Pt<uint16> data) {
+ NTIMES({ uint16 x = *data; *data++ = (x<<8) | (x>>8); })
+}
+
+/* **************************************************************** */
+
+template <class T>
+static void default_pack(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+ uint32 t;
+ int i;
+ int hb[4];
+ uint32 mask[4];
+ int sameorder = self->endian==2 || self->endian==::is_le();
+ int size = self->size;
+
+ for (i=0; i<self->size; i++) hb[i] = highest_bit(self->mask[i]);
+ memcpy(mask,self->mask,size*sizeof(uint32));
+
+ if (sameorder && size==3) {
+ switch(self->bytes) {
+ case 2: NTIMES(t=CONVERT1; *((int16 *)out)=t; out+=2; in+=3;) return;
+ case 4: NTIMES(t=CONVERT1; *((int32 *)out)=t; out+=4; in+=3;) return;
+ }
+ }
+ if (self->is_le()) {
+ switch (size) {
+ case 3: for (; n--; in+=3) {CONVERT1; WRITE_LE;} break;
+ case 4: for (; n--; in+=4) {CONVERT3; WRITE_LE;} break;
+ default:for (; n--; in+=size) {CONVERT2; WRITE_LE;}}
+ } else {
+ switch (size) {
+ case 3: for (; n--; in+=3) {CONVERT1; WRITE_BE;} break;
+ case 4: for (; n--; in+=4) {CONVERT3; WRITE_BE;} break;
+ default:for (; n--; in+=size) {CONVERT2; WRITE_BE;}}
+ }
+}
+
+#define LOOP_UNPACK(_reader_) \
+ for (; n; n--) { \
+ int bytes=0; uint32 temp=0; _reader_; \
+ for (int i=0; i<self->size; i++, out++) { \
+ uint32 t=temp&self->mask[i]; \
+ *out = (t<<(7-hb[i]))|(t>>(hb[i]-7)); \
+ } \
+ }
+// *out++ = ((temp & self->mask[i]) << 7) >> hb[i];
+
+template <class T>
+static void default_unpack(BitPacking *self, int n, Pt<uint8> in, Pt<T> out) {
+ int hb[4];
+ for (int i=0; i<self->size; i++) hb[i] = highest_bit(self->mask[i]);
+ if (is_le()) { // smallest byte first
+ LOOP_UNPACK(
+ for(; self->bytes>bytes; bytes++, in++) temp |= *in<<(8*bytes);
+ )
+ } else { // biggest byte first
+ LOOP_UNPACK(
+ bytes=self->bytes; for (; bytes; bytes--, in++) temp=(temp<<8)|*in;
+ )
+ }
+}
+
+/* **************************************************************** */
+
+template <class T>
+static void pack2_565(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+ const int hb[3] = {15,10,4};
+ const uint32 mask[3] = {0x0000f800,0x000007e0,0x0000001f};
+ uint32 t;
+ NTIMES( t=CONVERT1; *((short *)out)=t; out+=2; in+=3; )
+}
+
+template <class T>
+static void pack3_888(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+ Pt<int32> o32 = (Pt<int32>)out;
+ while (n>=4) {
+ o32[0] = (in[5]<<24) | (in[ 0]<<16) | (in[ 1]<<8) | in[2];
+ o32[1] = (in[7]<<24) | (in[ 8]<<16) | (in[ 3]<<8) | in[4];
+ o32[2] = (in[9]<<24) | (in[10]<<16) | (in[11]<<8) | in[6];
+ o32+=3; in+=12;
+ n-=4;
+ }
+ out = (Pt<uint8>)o32;
+ NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=3; in+=3; )
+}
+
+/*
+template <>
+static void pack3_888(BitPacking *self, int n, Pt<uint8> in, Pt<uint8> out) {
+ Pt<uint32> o32 = Pt<uint32>((uint32 *)out.p,n*3/4);
+ Pt<uint32> i32 = Pt<uint32>((uint32 *)in.p,n*3/4);
+ while (n>=4) {
+#define Z(w,i) ((word##w>>(i*8))&255)
+ uint32 word0 = i32[0];
+ uint32 word1 = i32[1];
+ uint32 word2 = i32[2];
+ o32[0] = (Z(1,1)<<24) | (Z(0,0)<<16) | (Z(0,1)<<8) | Z(0,2);
+ o32[1] = (Z(1,3)<<24) | (Z(2,0)<<16) | (Z(0,3)<<8) | Z(1,0);
+ o32[2] = (Z(2,1)<<24) | (Z(2,2)<<16) | (Z(2,3)<<8) | Z(1,2);
+ o32+=3; i32+=3;
+ n-=4;
+ }
+#undef Z
+ out = (Pt<uint8>)o32;
+ in = (Pt<uint8>)i32;
+ NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=3; in+=3; )
+}
+*/
+
+template <class T>
+static void pack3_888b(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+ Pt<int32> o32 = (Pt<int32>)out;
+ while (n>=4) {
+ o32[0] = (in[0]<<16) | (in[1]<<8) | in[2];
+ o32[1] = (in[3]<<16) | (in[4]<<8) | in[5];
+ o32[2] = (in[6]<<16) | (in[7]<<8) | in[8];
+ o32[3] = (in[9]<<16) | (in[10]<<8) | in[11];
+ o32+=4; in+=12;
+ n-=4;
+ }
+ NTIMES( o32[0] = (in[0]<<16) | (in[1]<<8) | in[2]; o32++; in+=3; )
+}
+
+/* (R,G,B,?) -> B:8,G:8,R:8,0:8 */
+template <class T>
+static void pack3_bgrn8888(BitPacking *self, int n, Pt<T> in, Pt<uint8> out) {
+/* NTIMES( out[2]=in[0]; out[1]=in[1]; out[0]=in[2]; out+=4; in+=4; ) */
+ Pt<int32> i32 = (Pt<int32>)in;
+ Pt<int32> o32 = (Pt<int32>)out;
+ while (n>=4) {
+ o32[0] = ((i32[0]&0xff)<<16) | (i32[0]&0xff00) | ((i32[0]>>16)&0xff);
+ o32[1] = ((i32[1]&0xff)<<16) | (i32[1]&0xff00) | ((i32[1]>>16)&0xff);
+ o32[2] = ((i32[2]&0xff)<<16) | (i32[2]&0xff00) | ((i32[2]>>16)&0xff);
+ o32[3] = ((i32[3]&0xff)<<16) | (i32[3]&0xff00) | ((i32[3]>>16)&0xff);
+ o32+=4; i32+=4; n-=4;
+ }
+ NTIMES( o32[0] = ((i32[0]&0xff)<<16) | (i32[0]&0xff00) | ((i32[0]>>16)&0xff); o32++; i32++; )
+}
+
+static uint32 bp_masks[][4] = {
+ {0x0000f800,0x000007e0,0x0000001f,0},
+ {0x00ff0000,0x0000ff00,0x000000ff,0},
+};
+
+static Packer bp_packers[] = {
+ {default_pack, default_pack, default_pack},
+ {pack2_565, pack2_565, pack2_565},
+ {pack3_888, pack3_888, pack3_888},
+ {pack3_888b, default_pack, default_pack},
+ {pack3_bgrn8888, default_pack, default_pack},
+};
+
+static Unpacker bp_unpackers[] = {
+ {default_unpack, default_unpack, default_unpack},
+};
+
+static BitPacking builtin_bitpackers[] = {
+ BitPacking(2, 2, 3, bp_masks[0], &bp_packers[1], &bp_unpackers[0]),
+ BitPacking(1, 3, 3, bp_masks[1], &bp_packers[2], &bp_unpackers[0]),
+ BitPacking(1, 4, 3, bp_masks[1], &bp_packers[3], &bp_unpackers[0]),
+ BitPacking(1, 4, 4, bp_masks[1], &bp_packers[4], &bp_unpackers[0]),
+};
+
+/* **************************************************************** */
+
+bool BitPacking::eq(BitPacking *o) {
+ if (!(bytes == o->bytes)) return false;
+ if (!(size == o->size)) return false;
+ for (int i=0; i<size; i++) {
+ if (!(mask[i] == o->mask[i])) return false;
+ }
+ if (endian==o->endian) return true;
+ /* same==little on a little-endian; same==big on a big-endian */
+ return (endian ^ o->endian ^ ::is_le()) == 2;
+}
+
+BitPacking::BitPacking(int endian, int bytes, int size, uint32 *mask,
+Packer *packer, Unpacker *unpacker) {
+ this->endian = endian;
+ this->bytes = bytes;
+ this->size = size;
+ for (int i=0; i<size; i++) this->mask[i] = mask[i];
+ if (packer) {
+ this->packer = packer;
+ this->unpacker = unpacker;
+ return;
+ }
+ int packeri=-1;
+ this->packer = &bp_packers[0];
+ this->unpacker = &bp_unpackers[0];
+
+ for (int i=0; i<(int)(sizeof(builtin_bitpackers)/sizeof(BitPacking)); i++) {
+ BitPacking *bp = &builtin_bitpackers[i];
+ if (this->eq(bp)) {
+ this->packer = bp->packer;
+ this->unpacker = bp->unpacker;
+ packeri=i;
+ goto end;
+ }
+ }
+end:;
+/*
+ ::gfpost("Bitpacking: endian=%d bytes=%d size=%d packeri=%d",
+ endian, bytes, size, packeri);
+ ::gfpost(" packer=0x%08x unpacker=0x%08x",this->packer,this->unpacker);
+ ::gfpost(" mask=[0x%08x,0x%08x,0x%08x,0x%08x]",mask[0],mask[1],mask[2],mask[3]);
+*/
+}
+
+bool BitPacking::is_le() {
+ return endian==1 || (endian ^ ::is_le())==3;
+}
+
+template <class T>
+void BitPacking::pack(int n, Pt<T> in, Pt<uint8> out) {
+ switch (NumberTypeE_type_of(*in)) {
+ case uint8_e: packer->as_uint8(this,n,(Pt<uint8>)in,out); break;
+ case int16_e: packer->as_int16(this,n,(Pt<int16>)in,out); break;
+ case int32_e: packer->as_int32(this,n,(Pt<int32>)in,out); break;
+ default: RAISE("argh");
+ }
+}
+
+template <class T>
+void BitPacking::unpack(int n, Pt<uint8> in, Pt<T> out) {
+ switch (NumberTypeE_type_of(*out)) {
+ case uint8_e: unpacker->as_uint8(this,n,in,(Pt<uint8>)out); break;
+ case int16_e: unpacker->as_int16(this,n,in,(Pt<int16>)out); break;
+ case int32_e: unpacker->as_int32(this,n,in,(Pt<int32>)out); break;
+ default: RAISE("argh");
+ }
+}
+
+// i'm sorry... see the end of grid.c for an explanation...
+//static
+void make_hocus_pocus () {
+// exit(1);
+#define FOO(S) \
+ ((BitPacking*)0)->pack(0,Pt<S>(),Pt<uint8>()); \
+ ((BitPacking*)0)->unpack(0,Pt<uint8>(),Pt<S>());
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+}
diff --git a/externals/gridflow/base/flow_objects.c b/externals/gridflow/base/flow_objects.c
new file mode 100644
index 00000000..ac3305d6
--- /dev/null
+++ b/externals/gridflow/base/flow_objects.c
@@ -0,0 +1,1189 @@
+/*
+ $Id: flow_objects.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <math.h>
+#include "grid.h.fcs"
+
+// BAD HACK: GCC complains: unimplemented (--debug|--debug-harder mode only)
+#ifdef HAVE_DEBUG
+#define SCOPY(a,b,n) COPY(a,b,n)
+#else
+#define SCOPY(a,b,n) SCopy<n>::f(a,b)
+#endif
+
+template <int n> class SCopy {
+public: template <class T> static inline void __attribute__((always_inline)) f(Pt<T> a, Pt<T> b) {
+ *a=*b; SCopy<n-1>::f(a+1,b+1);}};
+template <> class SCopy<0> {
+public: template <class T> static inline void __attribute__((always_inline)) f(Pt<T> a, Pt<T> b) {}};
+
+/*template <> class SCopy<4> {
+public: template <class T>
+ static inline void __attribute__((always_inline)) f(Pt<T> a, Pt<T> b) {
+ *a=*b; SCopy<3>::f(a+1,b+1);}
+ // wouldn't gcc 2.95 complain here?
+ static inline void __attribute__((always_inline)) f(Pt<uint8> a, Pt<uint8> b)
+ { *(int32 *)a=*(int32 *)b; }
+};*/
+
+Numop *op_add, *op_sub, *op_mul, *op_div, *op_mod, *op_shl, *op_and, *op_put;
+
+static void expect_dim_dim_list (P<Dim> d) {
+ if (d->n!=1) RAISE("dimension list should be Dim[n], not %s",d->to_s());}
+//static void expect_min_one_dim (P<Dim> d) {
+// if (d->n<1) RAISE("minimum 1 dimension");}
+static void expect_max_one_dim (P<Dim> d) {
+ if (d->n>1) { RAISE("expecting Dim[] or Dim[n], got %s",d->to_s()); }}
+//static void expect_exactly_one_dim (P<Dim> d) {
+// if (d->n!=1) { RAISE("expecting Dim[n], got %s",d->to_s()); }}
+
+//****************************************************************
+\class GridCast < GridObject
+struct GridCast : GridObject {
+ \attr NumberTypeE nt;
+ \decl void initialize (NumberTypeE nt);
+ \grin 0
+};
+
+GRID_INLET(GridCast,0) {
+ out = new GridOutlet(this,0,in->dim,nt);
+} GRID_FLOW {
+ out->send(n,data);
+} GRID_END
+
+\def void initialize (NumberTypeE nt) {
+ rb_call_super(argc,argv);
+ this->nt = nt;
+}
+
+\classinfo { IEVAL(rself,"install '#cast',1,1"); }
+\end class GridCast
+
+//****************************************************************
+//{ ?,Dim[B] -> Dim[*Cs] }
+// out0 nt to be specified explicitly
+\class GridImport < GridObject
+struct GridImport : GridObject {
+ \attr NumberTypeE cast;
+ \attr P<Dim> dim; // size of grids to send
+ PtrGrid dim_grid;
+ GridImport() { dim_grid.constrain(expect_dim_dim_list); }
+ ~GridImport() {}
+ \decl void initialize(Ruby x, NumberTypeE cast=int32_e);
+ \decl void _0_cast(NumberTypeE cast);
+ \decl void _0_reset();
+ \decl void _0_symbol(Symbol x);
+ \decl void _0_list(...);
+ \decl void _1_per_message();
+ \grin 0
+ \grin 1 int32
+ template <class T> void process (int n, Pt<T> data) {
+ while (n) {
+ if (!out || !out->dim) out = new GridOutlet(this,0,dim?dim:in[0]->dim,cast);
+ int32 n2 = min((int32)n,out->dim->prod()-out->dex);
+ out->send(n2,data);
+ n-=n2; data+=n2;
+ }
+ }
+};
+
+GRID_INLET(GridImport,0) {} GRID_FLOW { process(n,data); } GRID_END
+GRID_INPUT(GridImport,1,dim_grid) { dim = dim_grid->to_dim(); } GRID_END
+
+\def void _0_symbol(Symbol x) {
+ const char *name = rb_sym_name(argv[0]);
+ int n = strlen(name);
+ if (!dim) out=new GridOutlet(this,0,new Dim(n));
+ process(n,Pt<uint8>((uint8 *)name,n));
+}
+
+\def void _0_cast(NumberTypeE cast) { this->cast = cast; }
+
+\def void _0_list(...) {
+ if (in.size()<1 || !in[0]) _0_grid(0,0); //HACK: enable grid inlet...
+ in[0]->from_ruby_list(argc,argv,cast);
+}
+
+\def void _1_per_message() { dim=0; dim_grid=0; }
+
+\def void initialize(Ruby x, NumberTypeE cast) {
+ rb_call_super(argc,argv);
+ this->cast = cast;
+ if (argv[0]!=SYM(per_message)) {
+ dim_grid=new Grid(argv[0]);
+ dim = dim_grid->to_dim();
+ }
+}
+
+\def void _0_reset() {
+ STACK_ARRAY(int32,foo,1); *foo=0;
+ while (out->dim) out->send(1,foo);
+}
+
+\classinfo { IEVAL(rself,"install '#import',2,1"); }
+\end class GridImport
+
+//****************************************************************
+/*{ Dim[*As] -> ? }*/
+/* in0: integer nt */
+\class GridExport < GridObject
+struct GridExport : GridObject {
+ \grin 0
+};
+
+template <class T>
+static Ruby INTORFLOAT2NUM(T value) {return INT2NUM(value);}
+static Ruby INTORFLOAT2NUM(int64 value) {return gf_ll2num(value);}
+static Ruby INTORFLOAT2NUM(float32 value) {return rb_float_new(value);}
+static Ruby INTORFLOAT2NUM(float64 value) {return rb_float_new(value);}
+
+GRID_INLET(GridExport,0) {
+} GRID_FLOW {
+ for (int i=0; i<n; i++) {
+ Ruby a[] = { INT2NUM(0), INTORFLOAT2NUM(data[i]) };
+ send_out(COUNT(a),a);
+ }
+} GRID_END
+\classinfo { IEVAL(rself,"install '#export',1,1"); }
+\end class GridExport
+
+/* **************************************************************** */
+/*{ Dim[*As] -> ? }*/
+/* in0: integer nt */
+\class GridExportList < GridObject
+struct GridExportList : GridObject {
+ Ruby /*Array*/ list;
+ int n;
+ \grin 0
+};
+
+GRID_INLET(GridExportList,0) {
+ int n = in->dim->prod();
+ if (n>250000) RAISE("list too big (%d elements)", n);
+ list = rb_ary_new2(n+2);
+ this->n = n;
+ rb_ivar_set(rself,SI(@list),list); // keep
+ rb_ary_store(list,0,INT2NUM(0));
+ rb_ary_store(list,1,bsym._list);
+} GRID_FLOW {
+ for (int i=0; i<n; i++, data++)
+ rb_ary_store(list,in->dex+i+2,INTORFLOAT2NUM(*data));
+} GRID_FINISH {
+ send_out(rb_ary_len(list),rb_ary_ptr(list));
+ list = 0;
+ rb_ivar_set(rself,SI(@list),Qnil); // unkeep
+} GRID_END
+
+\classinfo { IEVAL(rself,"install '#export_list',1,1"); }
+\end class GridExportList
+
+/* **************************************************************** */
+// GridStore ("@store") is the class for storing a grid and restituting
+// it on demand. The right inlet receives the grid. The left inlet receives
+// either a bang (which forwards the whole image) or a grid describing what
+// to send.
+//{ Dim[*As,B],Dim[*Cs,*Ds] -> Dim[*As,*Ds] }
+// in0: integer nt
+// in1: whatever nt
+// out0: same nt as in1
+\class GridStore < GridObject
+struct GridStore : GridObject {
+ PtrGrid r; // can't be \attr
+ PtrGrid put_at; // can't be //\attr
+ \attr Numop *op;
+ int32 wdex [Dim::MAX_DIMENSIONS]; // temporary buffer, copy of put_at
+ int32 fromb[Dim::MAX_DIMENSIONS];
+ int32 to2 [Dim::MAX_DIMENSIONS];
+ int lsd; // lsd = Last Same Dimension (for put_at)
+ int d; // goes with wdex
+ \decl void initialize (Grid *r=0);
+ \decl void _0_bang ();
+ \decl void _0_op (Numop *op);
+ \decl void _1_reassign ();
+ \decl void _1_put_at (Grid *index);
+ \grin 0 int
+ \grin 1
+ GridStore() { put_at.constrain(expect_max_one_dim); }
+ template <class T> void compute_indices(Pt<T> v, int nc, int nd);
+};
+
+// takes the backstore of a grid and puts it back into place. a backstore
+// is a grid that is filled while the grid it would replace has not
+// finished being used.
+static void snap_backstore (PtrGrid &r) {
+ if (r.next) {r=r.next.p; r.next=0;}
+}
+
+template <class T> void GridStore::compute_indices(Pt<T> v, int nc, int nd) {
+ for (int i=0; i<nc; i++) {
+ uint32 wrap = r->dim->v[i];
+ bool fast = lowest_bit(wrap)==highest_bit(wrap); // is power of two?
+ if (i) {
+ if (fast) op_shl->map(nd,v,(T)highest_bit(wrap));
+ else op_mul->map(nd,v,(T)wrap);
+ }
+ if (fast) op_and->map(nd,v+nd*i,(T)(wrap-1));
+ else op_mod->map(nd,v+nd*i,(T)(wrap));
+ if (i) op_add->zip(nd,v,v+nd*i);
+ }
+}
+
+// !@#$ i should ensure that n is not exceedingly large
+// !@#$ worse: the size of the foo buffer may still be too large
+GRID_INLET(GridStore,0) {
+ // snap_backstore must be done before *anything* else
+ snap_backstore(r);
+ int na = in->dim->n;
+ int nb = r->dim->n;
+ int nc = in->dim->get(na-1);
+ STACK_ARRAY(int32,v,Dim::MAX_DIMENSIONS);
+ if (na<1) RAISE("must have at least 1 dimension.",na,1,1+nb);
+ int lastindexable = r->dim->prod()/r->dim->prod(nc) - 1;
+ int ngreatest = nt_greatest((T *)0);
+ if (lastindexable > ngreatest) {
+ RAISE("lastindexable=%d > ngreatest=%d (ask matju)",lastindexable,ngreatest);
+ }
+ if (nc > nb)
+ RAISE("wrong number of elements in last dimension: "
+ "got %d, expecting <= %d", nc, nb);
+ int nd = nb - nc + na - 1;
+ COPY(v,in->dim->v,na-1);
+ COPY(v+na-1,r->dim->v+nc,nb-nc);
+ out=new GridOutlet(this,0,new Dim(nd,v),r->nt);
+ if (nc>0) in->set_factor(nc);
+} GRID_FLOW {
+ int na = in->dim->n;
+ int nc = in->dim->get(na-1);
+ int size = r->dim->prod(nc);
+ assert((n % nc) == 0);
+ int nd = n/nc;
+ STACK_ARRAY(T,w,n);
+ Pt<T> v=w;
+ if (sizeof(T)==1 && nc==1 && r->dim->v[0]<=256) {
+ // bug? shouldn't modulo be done here?
+ v=data;
+ } else {
+ COPY(v,data,n);
+ for (int k=0,i=0; i<nc; i++) for (int j=0; j<n; j+=nc) v[k++] = data[i+j];
+ compute_indices(v,nc,nd);
+ }
+#define FOO(type) { \
+ Pt<type> p = (Pt<type>)*r; \
+ if (size<=16) { \
+ Pt<type> foo = ARRAY_NEW(type,nd*size); \
+ int i=0; \
+ switch (size) { \
+ case 1: for (; i<nd&-4; i+=4, foo+=4) { \
+ foo[0] = p[v[i+0]]; \
+ foo[1] = p[v[i+1]]; \
+ foo[2] = p[v[i+2]]; \
+ foo[3] = p[v[i+3]]; \
+ } break; \
+ case 2: for (; i<nd; i++, foo+=2) SCOPY(foo,p+2*v[i],2); break; \
+ case 3: for (; i<nd; i++, foo+=3) SCOPY(foo,p+3*v[i],3); break; \
+ case 4: for (; i<nd; i++, foo+=4) SCOPY(foo,p+4*v[i],4); break; \
+ default:; }; \
+ for (; i<nd; i++, foo+=size) COPY(foo,p+size*v[i],size); \
+ out->give(size*nd,foo-size*nd); \
+ } else { \
+ for (int i=0; i<nd; i++) out->send(size,p+size*v[i]); \
+ } \
+}
+ TYPESWITCH(r->nt,FOO,)
+#undef FOO
+} GRID_FINISH {
+ if (in->dim->prod()==0) {
+ int n = in->dim->prod(0,-2);
+ int size = r->dim->prod();
+#define FOO(T) while (n--) out->send(size,(Pt<T>)*r);
+ TYPESWITCH(r->nt,FOO,)
+#undef FOO
+ }
+} GRID_END
+
+GRID_INLET(GridStore,1) {
+ NumberTypeE nt = NumberTypeE_type_of(*data);
+ if (!put_at) { // reassign
+ if (in[0].dim)
+ r.next = new Grid(in->dim,nt);
+ else
+ r = new Grid(in->dim,nt);
+ return;
+ }
+ // put_at ( ... )
+ //!@#$ should check types. if (r->nt!=in->nt) RAISE("shoo");
+ int nn=r->dim->n, na=put_at->dim->v[0], nb=in->dim->n;
+ STACK_ARRAY(int32,sizeb,nn);
+ for (int i=0; i<nn; i++) { fromb[i]=0; sizeb[i]=1; }
+ COPY(Pt<int32>(wdex,nn) ,(Pt<int32>)*put_at ,put_at->dim->prod());
+ COPY(Pt<int32>(fromb,nn)+nn-na,(Pt<int32>)*put_at ,na);
+ COPY(Pt<int32>(sizeb,nn)+nn-nb,(Pt<int32>)in->dim->v,nb);
+ for (int i=0; i<nn; i++) to2[i] = fromb[i]+sizeb[i];
+ d=0;
+ // find out when we can skip computing indices
+ //!@#$ should actually also stop before blowing up packet size
+ lsd=nn;
+ while (lsd>=nn-in->dim->n) {
+ lsd--;
+ int cs = in->dim->prod(lsd-nn+in->dim->n);
+ if (cs>GridOutlet::MAX_PACKET_SIZE || fromb[lsd]!=0 || sizeb[lsd]!=r->dim->v[lsd]) break;
+ }
+ lsd++;
+ int cs = in->dim->prod(lsd-nn+in->dim->n);
+ in->set_factor(cs);
+} GRID_FLOW {
+ if (!put_at) { // reassign
+ COPY(((Pt<T>)*(r.next ? r.next.p : &*r.p))+in->dex, data, n);
+ return;
+ }
+ // put_at ( ... )
+ int nn=r->dim->n;
+ int cs = in->factor(); // chunksize
+ STACK_ARRAY(int32,v,lsd);
+ Pt<int32> x = Pt<int32>(wdex,nn);
+ while (n) {
+ // here d is the dim# to reset; d=n for none
+ for(;d<lsd;d++) x[d]=fromb[d];
+ COPY(v,x,lsd);
+ compute_indices(v,lsd,1);
+ op->zip(cs,(Pt<T>)*r+v[0]*cs,data);
+ data+=cs;
+ n-=cs;
+ // find next set of indices; here d is the dim# to increment
+ for(;;) {
+ d--;
+ if (d<0) goto end;
+ x[d]++;
+ if (x[d]<to2[d]) break;
+ }
+ end:; // why here ??? or why at all?
+ d++;
+ }
+ //end:; // why not here ???
+} GRID_END
+\def void _0_op(Numop *op) { this->op=op; }
+\def void _0_bang () { rb_funcall(rself,SI(_0_list),3,INT2NUM(0),SYM(#),INT2NUM(0)); }
+\def void _1_reassign () { put_at=0; }
+\def void _1_put_at (Grid *index) { put_at=index; }
+\def void initialize (Grid *r) {
+ rb_call_super(argc,argv);
+ this->r = r?r:new Grid(new Dim(),int32_e,true);
+ op = op_put;
+}
+\classinfo { IEVAL(rself,"install '#store',2,1"); }
+\end class GridStore
+
+//****************************************************************
+//{ Dim[*As]<T> -> Dim[*As]<T> } or
+//{ Dim[*As]<T>,Dim[*Bs]<T> -> Dim[*As]<T> }
+\class GridOp < GridObject
+struct GridOp : GridObject {
+ \attr Numop *op;
+ PtrGrid r;
+ \decl void initialize(Numop *op, Grid *r=0);
+ \grin 0
+ \grin 1
+ \decl void _0_op(Numop *op);
+};
+
+GRID_INLET(GridOp,0) {
+ snap_backstore(r);
+ SAME_TYPE(in,r);
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_mode(6);
+} GRID_FLOW {
+ Pt<T> rdata = (Pt<T>)*r;
+ int loop = r->dim->prod();
+ if (sizeof(T)==8) {
+ fprintf(stderr,"1: data=%p rdata=%p\n",data.p,rdata.p);
+ WATCH(n,data);
+ }
+ if (loop>1) {
+ if (in->dex+n <= loop) {
+ op->zip(n,data,rdata+in->dex);
+ } else {
+ // !@#$ should prebuild and reuse this array when "loop" is small
+ STACK_ARRAY(T,data2,n);
+ int ii = mod(in->dex,loop);
+ int m = min(loop-ii,n);
+ COPY(data2,rdata+ii,m);
+ int nn = m+((n-m)/loop)*loop;
+ for (int i=m; i<nn; i+=loop) COPY(data2+i,rdata,loop);
+ if (n>nn) COPY(data2+nn,rdata,n-nn);
+ if (sizeof(T)==8) {
+ fprintf(stderr,"2: data=%p data2=%p\n",data.p,data2.p);
+ WATCH(n,data); WATCH(n,data2);
+ }
+ op->zip(n,data,data2);
+ if (sizeof(T)==8) {WATCH(n,data); WATCH(n,data2);}
+ }
+ } else {
+ op->map(n,data,*rdata);
+ }
+ out->give(n,data);
+} GRID_END
+
+GRID_INPUT2(GridOp,1,r) {} GRID_END
+\def void _0_op(Numop *op) { this->op=op; }
+
+\def void initialize(Numop *op, Grid *r=0) {
+ rb_call_super(argc,argv);
+ this->op=op;
+ this->r = r?r:new Grid(new Dim(),int32_e,true);
+}
+
+\classinfo { IEVAL(rself,"install '#',2,1"); }
+\end class GridOp
+
+//****************************************************************
+\class GridFold < GridObject
+struct GridFold : GridObject {
+ \attr Numop *op;
+ \attr PtrGrid seed;
+ \decl void initialize (Numop *op);
+ \decl void _0_op (Numop *op);
+ \decl void _0_seed (Grid *seed);
+ \grin 0
+};
+
+GRID_INLET(GridFold,0) {
+ //{ Dim[*As,B,*Cs]<T>,Dim[*Cs]<T> -> Dim[*As,*Cs]<T> }
+ if (seed) SAME_TYPE(in,seed);
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ if (an<=bn) RAISE("minimum 1 more dimension than the seed (%d vs %d)",an,bn);
+ STACK_ARRAY(int32,v,an-1);
+ int yi = an-bn-1;
+ COPY(v,in->dim->v,yi);
+ COPY(v+yi,in->dim->v+an-bn,bn);
+ if (seed) SAME_DIM(an-(yi+1),in->dim,(yi+1),seed->dim,0);
+ out=new GridOutlet(this,0,new Dim(an-1,v),in->nt);
+ int k = seed ? seed->dim->prod() : 1;
+ in->set_factor(in->dim->get(yi)*k);
+} GRID_FLOW {
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ int yn = in->dim->v[an-bn-1];
+ int zn = in->dim->prod(an-bn);
+ STACK_ARRAY(T,buf,n/yn);
+ int nn=n;
+ int yzn=yn*zn;
+ for (int i=0; n; i+=zn, data+=yzn, n-=yzn) {
+ if (seed) COPY(buf+i,((Pt<T>)*seed),zn);
+ else CLEAR(buf+i,zn);
+ op->fold(zn,yn,buf+i,data);
+ }
+ out->send(nn/yn,buf);
+} GRID_END
+
+\def void _0_op (Numop *op ) { this->op =op; }
+\def void _0_seed (Grid *seed) { this->seed=seed; }
+\def void initialize (Numop *op) { rb_call_super(argc,argv); this->op=op; }
+\classinfo { IEVAL(rself,"install '#fold',1,1"); }
+\end class GridFold
+
+\class GridScan < GridObject
+struct GridScan : GridObject {
+ \attr Numop *op;
+ \attr PtrGrid seed;
+ \decl void initialize (Numop *op);
+ \decl void _0_op (Numop *op);
+ \decl void _0_seed (Grid *seed);
+ \grin 0
+};
+
+GRID_INLET(GridScan,0) {
+ //{ Dim[*As,B,*Cs]<T>,Dim[*Cs]<T> -> Dim[*As,B,*Cs]<T> }
+ if (seed) SAME_TYPE(in,seed);
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ if (an<=bn) RAISE("minimum 1 more dimension than the right hand");
+ if (seed) SAME_DIM(bn,in->dim,an-bn,seed->dim,0);
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_factor(in->dim->prod(an-bn-1));
+} GRID_FLOW {
+ int an = in->dim->n;
+ int bn = seed?seed->dim->n:0;
+ int yn = in->dim->v[an-bn-1];
+ int zn = in->dim->prod(an-bn);
+ int factor = in->factor();
+ STACK_ARRAY(T,buf,n);
+ COPY(buf,data,n);
+ if (seed) {
+ for (int i=0; i<n; i+=factor) op->scan(zn,yn,(Pt<T>)*seed,buf+i);
+ } else {
+ STACK_ARRAY(T,seed,zn);
+ CLEAR(seed,zn);
+ for (int i=0; i<n; i+=factor) op->scan(zn,yn,seed,buf+i);
+ }
+ out->send(n,buf);
+} GRID_END
+
+\def void _0_op (Numop *op ) { this->op =op; }
+\def void _0_seed (Grid *seed) { this->seed=seed; }
+\def void initialize (Numop *op) { rb_call_super(argc,argv); this->op = op; }
+\classinfo { IEVAL(rself,"install '#scan',1,1"); }
+\end class GridScan
+
+//****************************************************************
+//{ Dim[*As,C]<T>,Dim[C,*Bs]<T> -> Dim[*As,*Bs]<T> }
+\class GridInner < GridObject
+struct GridInner : GridObject {
+ \attr Numop *op_para;
+ \attr Numop *op_fold;
+ \attr PtrGrid seed;
+ PtrGrid r;
+ PtrGrid r2;
+ GridInner() {}
+ \decl void initialize (Grid *r=0);
+ \decl void _0_op (Numop *op);
+ \decl void _0_fold (Numop *op);
+ \decl void _0_seed (Grid *seed);
+ \grin 0
+ \grin 1
+};
+
+template <class T> void inner_child_a (Pt<T> buf, Pt<T> data, int rrows, int rcols, int chunk) {
+ Pt<T> bt = buf, dt = data;
+ for (int j=0; j<chunk; j++, bt+=rcols, dt+=rrows) op_put->map(rcols,bt,*dt);
+}
+template <class T, int rcols> void inner_child_b (Pt<T> buf, Pt<T> data, int rrows, int chunk) {
+ Pt<T> bt = buf, dt = data;
+ for (int j=0; j<chunk; j++, bt+=rcols, dt+=rrows) {
+ for (int k=0; k<rcols; k++) bt[k] = *dt;
+ }
+}
+GRID_INLET(GridInner,0) {
+ SAME_TYPE(in,r);
+ SAME_TYPE(in,seed);
+ P<Dim> a = in->dim;
+ P<Dim> b = r->dim;
+ if (a->n<1) RAISE("a: minimum 1 dimension");
+ if (b->n<1) RAISE("b: minimum 1 dimension");
+ if (seed->dim->n != 0) RAISE("seed must be a scalar");
+ int a_last = a->get(a->n-1);
+ int n = a->n+b->n-2;
+ SAME_DIM(1,a,a->n-1,b,0);
+ STACK_ARRAY(int32,v,n);
+ COPY(v,a->v,a->n-1);
+ COPY(v+a->n-1,b->v+1,b->n-1);
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+ in->set_factor(a_last);
+
+ int rrows = in->factor();
+ int rsize = r->dim->prod();
+ int rcols = rsize/rrows;
+ Pt<T> rdata = (Pt<T>)*r;
+ int chunk = GridOutlet::MAX_PACKET_SIZE/rsize;
+ r2=new Grid(new Dim(chunk*rsize),r->nt);
+ Pt<T> buf3 = (Pt<T>)*r2;
+ for (int i=0; i<rrows; i++)
+ for (int j=0; j<chunk; j++)
+ COPY(buf3+(j+i*chunk)*rcols,rdata+i*rcols,rcols);
+} GRID_FLOW {
+ int rrows = in->factor();
+ int rsize = r->dim->prod();
+ int rcols = rsize/rrows;
+ int chunk = GridOutlet::MAX_PACKET_SIZE/rsize;
+ STACK_ARRAY(T,buf ,chunk*rcols);
+ STACK_ARRAY(T,buf2,chunk*rcols);
+ int off = chunk;
+ while (n) {
+ if (chunk*rrows>n) chunk=n/rrows;
+ op_put->map(chunk*rcols,buf2,*(T *)*seed);
+ for (int i=0; i<rrows; i++) {
+ switch (rcols) {
+ case 1: inner_child_b<T,1>(buf,data+i,rrows,chunk); break;
+ case 2: inner_child_b<T,2>(buf,data+i,rrows,chunk); break;
+ case 3: inner_child_b<T,3>(buf,data+i,rrows,chunk); break;
+ case 4: inner_child_b<T,4>(buf,data+i,rrows,chunk); break;
+ default: inner_child_a(buf,data+i,rrows,rcols,chunk);
+ }
+ op_para->zip(chunk*rcols,buf,(Pt<T>)*r2+i*off*rcols);
+ op_fold->zip(chunk*rcols,buf2,buf);
+ }
+ out->send(chunk*rcols,buf2);
+ n-=chunk*rrows;
+ data+=chunk*rrows;
+ }
+} GRID_FINISH {
+ r2=0;
+} GRID_END
+
+GRID_INPUT(GridInner,1,r) {} GRID_END
+
+\def void initialize (Grid *r) {
+ rb_call_super(argc,argv);
+ this->op_para = op_mul;
+ this->op_fold = op_add;
+ this->seed = new Grid(new Dim(),int32_e,true);
+ this->r = r ? r : new Grid(new Dim(),int32_e,true);
+}
+
+\def void _0_op (Numop *op ) { this->op_para=op; }
+\def void _0_fold (Numop *op ) { this->op_fold=op; }
+\def void _0_seed (Grid *seed) { this->seed=seed; }
+\classinfo { IEVAL(rself,"install '#inner',2,1"); }
+\end class GridInner
+
+/* **************************************************************** */
+/*{ Dim[*As]<T>,Dim[*Bs]<T> -> Dim[*As,*Bs]<T> }*/
+\class GridOuter < GridObject
+struct GridOuter : GridObject {
+ \attr Numop *op;
+ PtrGrid r;
+ \decl void initialize (Numop *op, Grid *r=0);
+ \grin 0
+ \grin 1
+};
+
+GRID_INLET(GridOuter,0) {
+ SAME_TYPE(in,r);
+ P<Dim> a = in->dim;
+ P<Dim> b = r->dim;
+ int n = a->n+b->n;
+ STACK_ARRAY(int32,v,n);
+ COPY(v,a->v,a->n);
+ COPY(v+a->n,b->v,b->n);
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+} GRID_FLOW {
+ int b_prod = r->dim->prod();
+ if (b_prod > 4) {
+ STACK_ARRAY(T,buf,b_prod);
+ while (n) {
+ for (int j=0; j<b_prod; j++) buf[j] = *data;
+ op->zip(b_prod,buf,(Pt<T>)*r);
+ out->send(b_prod,buf);
+ data++; n--;
+ }
+ return;
+ }
+ n*=b_prod;
+ Pt<T> buf = ARRAY_NEW(T,n);
+ STACK_ARRAY(T,buf2,b_prod*64);
+ for (int i=0; i<64; i++) COPY(buf2+i*b_prod,(Pt<T>)*r,b_prod);
+ switch (b_prod) {
+ #define Z buf[k++]=data[i]
+ case 1: for (int i=0,k=0; k<n; i++) {Z;} break;
+ case 2: for (int i=0,k=0; k<n; i++) {Z;Z;} break;
+ case 3: for (int i=0,k=0; k<n; i++) {Z;Z;Z;} break;
+ case 4: for (int i=0,k=0; k<n; i++) {Z;Z;Z;Z;} break;
+ default:for (int i=0,k=0; k<n; i++) for (int j=0; j<b_prod; j++, k++) Z;
+ }
+ #undef Z
+ int ch=64*b_prod;
+ int nn=(n/ch)*ch;
+ for (int j=0; j<nn; j+=ch) op->zip(ch,buf+j,buf2);
+ op->zip(n-nn,buf+nn,buf2);
+ out->give(n,buf);
+} GRID_END
+
+GRID_INPUT(GridOuter,1,r) {} GRID_END
+
+\def void initialize (Numop *op, Grid *r) {
+ rb_call_super(argc,argv);
+ this->op = op;
+ this->r = r ? r : new Grid(new Dim(),int32_e,true);
+}
+
+\classinfo { IEVAL(rself,"install '#outer',2,1"); }
+\end class GridOuter
+
+//****************************************************************
+//{ Dim[]<T>,Dim[]<T>,Dim[]<T> -> Dim[A]<T> } or
+//{ Dim[B]<T>,Dim[B]<T>,Dim[B]<T> -> Dim[*As,B]<T> }
+\class GridFor < GridObject
+struct GridFor : GridObject {
+ \attr PtrGrid from;
+ \attr PtrGrid to;
+ \attr PtrGrid step;
+ GridFor () {
+ from.constrain(expect_max_one_dim);
+ to .constrain(expect_max_one_dim);
+ step.constrain(expect_max_one_dim);
+ }
+ \decl void initialize (Grid *from, Grid *to, Grid *step);
+ \decl void _0_set (Grid *r=0);
+ \decl void _0_bang ();
+ \grin 0 int
+ \grin 1 int
+ \grin 2 int
+ template <class T> void trigger (T bogus);
+};
+
+\def void initialize (Grid *from, Grid *to, Grid *step) {
+ rb_call_super(argc,argv);
+ this->from=from;
+ this->to =to;
+ this->step=step;
+}
+
+template <class T>
+void GridFor::trigger (T bogus) {
+ int n = from->dim->prod();
+ int32 nn[n+1];
+ STACK_ARRAY(T,x,64*n);
+ Pt<T> fromb = (Pt<T>)*from;
+ Pt<T> tob = (Pt<T>)*to ;
+ Pt<T> stepb = (Pt<T>)*step;
+ STACK_ARRAY(T,to2,n);
+
+ for (int i=step->dim->prod()-1; i>=0; i--)
+ if (!stepb[i]) RAISE("step must not contain zeroes");
+ for (int i=0; i<n; i++) {
+ nn[i] = (tob[i] - fromb[i] + stepb[i] - cmp(stepb[i],(T)0)) / stepb[i];
+ if (nn[i]<0) nn[i]=0;
+ to2[i] = fromb[i]+stepb[i]*nn[i];
+ }
+ P<Dim> d;
+ if (from->dim->n==0) { d = new Dim(*nn); }
+ else { nn[n]=n; d = new Dim(n+1,nn); }
+ int total = d->prod();
+ out=new GridOutlet(this,0,d,from->nt);
+ if (total==0) return;
+ int k=0;
+ for(int d=0;;d++) {
+ // here d is the dim# to reset; d=n for none
+ for(;d<n;d++) x[k+d]=fromb[d];
+ k+=n;
+ if (k==64*n) {out->send(k,x); k=0; COPY(x,x+63*n,n);}
+ else { COPY(x+k,x+k-n,n);}
+ d--;
+ // here d is the dim# to increment
+ for(;;d--) {
+ if (d<0) goto end;
+ x[k+d]+=stepb[d];
+ if (x[k+d]!=to2[d]) break;
+ }
+ }
+ end: if (k) out->send(k,x);
+}
+
+\def void _0_bang () {
+ SAME_TYPE(from,to);
+ SAME_TYPE(from,step);
+ if (!from->dim->equal(to->dim) || !to->dim->equal(step->dim))
+ RAISE("dimension mismatch");
+#define FOO(T) trigger((T)0);
+ TYPESWITCH_NOFLOAT(from->nt,FOO,);
+#undef FOO
+}
+
+\def void _0_set (Grid *r) { from=new Grid(argv[0]); }
+GRID_INPUT(GridFor,2,step) {} GRID_END
+GRID_INPUT(GridFor,1,to) {} GRID_END
+GRID_INPUT(GridFor,0,from) {_0_bang(0,0);} GRID_END
+\classinfo { IEVAL(rself,"install '#for',3,1"); }
+\end class GridFor
+
+//****************************************************************
+\class GridFinished < GridObject
+struct GridFinished : GridObject {
+ \grin 0
+};
+GRID_INLET(GridFinished,0) {
+ in->set_mode(0);
+} GRID_FINISH {
+ Ruby a[] = { INT2NUM(0), bsym._bang };
+ send_out(COUNT(a),a);
+} GRID_END
+\classinfo { IEVAL(rself,"install '#finished',1,1"); }
+\end class GridFinished
+
+\class GridDim < GridObject
+struct GridDim : GridObject {
+ \grin 0
+};
+GRID_INLET(GridDim,0) {
+ GridOutlet out(this,0,new Dim(in->dim->n));
+ out.send(in->dim->n,Pt<int32>(in->dim->v,in->dim->n));
+ in->set_mode(0);
+} GRID_END
+\classinfo { IEVAL(rself,"install '#dim',1,1"); }
+\end class GridDim
+
+\class GridType < GridObject
+struct GridType : GridObject {
+ \grin 0
+};
+GRID_INLET(GridType,0) {
+ Ruby a[] = { INT2NUM(0), SYM(symbol), number_type_table[in->nt].sym };
+ send_out(COUNT(a),a);
+ in->set_mode(0);
+} GRID_END
+\classinfo { IEVAL(rself,"install '#type',1,1"); }
+\end class GridType
+
+//****************************************************************
+//{ Dim[*As]<T>,Dim[B] -> Dim[*Cs]<T> }
+\class GridRedim < GridObject
+struct GridRedim : GridObject {
+ \attr P<Dim> dim;
+ PtrGrid dim_grid;
+ PtrGrid temp; // temp->dim is not of the same shape as dim
+ GridRedim() { dim_grid.constrain(expect_dim_dim_list); }
+ ~GridRedim() {}
+ \decl void initialize (Grid *d);
+ \grin 0
+ \grin 1 int32
+};
+
+GRID_INLET(GridRedim,0) {
+ int a = in->dim->prod(), b = dim->prod();
+ if (a<b) temp=new Grid(new Dim(a),in->nt);
+ out=new GridOutlet(this,0,dim,in->nt);
+} GRID_FLOW {
+ int i = in->dex;
+ if (!temp) {
+ int b = dim->prod();
+ int n2 = min(n,b-i);
+ if (n2>0) out->send(n2,data);
+ // discard other values if any
+ } else {
+ int a = in->dim->prod();
+ int n2 = min(n,a-i);
+ COPY((Pt<T>)*temp+i,data,n2);
+ if (n2>0) out->send(n2,data);
+ }
+} GRID_FINISH {
+ if (!!temp) {
+ int a = in->dim->prod(), b = dim->prod();
+ if (a) {
+ for (int i=a; i<b; i+=a) out->send(min(a,b-i),(Pt<T>)*temp);
+ } else {
+ STACK_ARRAY(T,foo,1);
+ foo[0]=0;
+ for (int i=0; i<b; i++) out->send(1,foo);
+ }
+ }
+ temp=0;
+} GRID_END
+
+GRID_INPUT(GridRedim,1,dim_grid) { dim = dim_grid->to_dim(); } GRID_END
+
+\def void initialize (Grid *d) {
+ rb_call_super(argc,argv);
+ dim_grid=d;
+ dim = dim_grid->to_dim();
+}
+
+\classinfo { IEVAL(rself,"install '#redim',2,1"); }
+\end class GridRedim
+
+//****************************************************************
+\class GridJoin < GridObject
+struct GridJoin : GridObject {
+ \attr int which_dim;
+ PtrGrid r;
+ \grin 0
+ \grin 1
+ \decl void initialize (int which_dim=-1, Grid *r=0);
+};
+
+GRID_INLET(GridJoin,0) {
+ NOTEMPTY(r);
+ SAME_TYPE(in,r);
+ P<Dim> d = in->dim;
+ if (d->n != r->dim->n) RAISE("wrong number of dimensions");
+ int w = which_dim;
+ if (w<0) w+=d->n;
+ if (w<0 || w>=d->n)
+ RAISE("can't join on dim number %d on %d-dimensional grids",
+ which_dim,d->n);
+ STACK_ARRAY(int32,v,d->n);
+ for (int i=0; i<d->n; i++) {
+ v[i] = d->get(i);
+ if (i==w) {
+ v[i]+=r->dim->v[i];
+ } else {
+ if (v[i]!=r->dim->v[i]) RAISE("dimensions mismatch: dim #%i, left is %d, right is %d",i,v[i],r->dim->v[i]);
+ }
+ }
+ out=new GridOutlet(this,0,new Dim(d->n,v),in->nt);
+ if (d->prod(w)) in->set_factor(d->prod(w));
+} GRID_FLOW {
+ int w = which_dim;
+ if (w<0) w+=in->dim->n;
+ int a = in->factor();
+ int b = r->dim->prod(w);
+ Pt<T> data2 = (Pt<T>)*r + in->dex*b/a;
+ if (a==3 && b==1) {
+ int m = n+n*b/a;
+ STACK_ARRAY(T,data3,m);
+ Pt<T> data4 = data3;
+ while (n) {
+ SCOPY(data4,data,3); SCOPY(data4+3,data2,1);
+ n-=3; data+=3; data2+=1; data4+=4;
+ }
+ out->send(m,data3);
+ } else if (a+b<=16) {
+ int m = n+n*b/a;
+ STACK_ARRAY(T,data3,m);
+ int i=0;
+ while (n) {
+ COPY(data3+i,data,a); data+=a; i+=a; n-=a;
+ COPY(data3+i,data2,b); data2+=b; i+=b;
+ }
+ out->send(m,data3);
+ } else {
+ while (n) {
+ out->send(a,data);
+ out->send(b,data2);
+ data+=a; data2+=b; n-=a;
+ }
+ }
+} GRID_FINISH {
+ if (in->dim->prod()==0) out->send(r->dim->prod(),(Pt<T>)*r);
+} GRID_END
+
+GRID_INPUT(GridJoin,1,r) {} GRID_END
+
+\def void initialize (int which_dim, Grid *r) {
+ rb_call_super(argc,argv);
+ this->which_dim = which_dim;
+ if (r) this->r=r;
+}
+
+\classinfo { IEVAL(rself,"install '@join',2,1"); }
+\end class GridJoin
+
+//****************************************************************
+\class GridGrade < GridObject
+struct GridGrade : GridObject {
+ \grin 0
+};
+
+template <class T> struct GradeFunction {
+ static int comparator (const void *a, const void *b) {
+ return **(T**)a - **(T**)b;}};
+#define FOO(S) \
+template <> struct GradeFunction<S> { \
+ static int comparator (const void *a, const void *b) { \
+ S x = **(S**)a - **(S**)b; \
+ return x<0 ? -1 : x>0;}};
+FOO(int64)
+FOO(float32)
+FOO(float64)
+#undef FOO
+
+GRID_INLET(GridGrade,0) {
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_factor(in->dim->get(in->dim->n-1));
+} GRID_FLOW {
+ int m = in->factor();
+ STACK_ARRAY(T*,foo,m);
+ STACK_ARRAY(T,bar,m);
+ for (; n; n-=m,data+=m) {
+ for (int i=0; i<m; i++) foo[i] = &data[i];
+ qsort(foo,m,sizeof(T),GradeFunction<T>::comparator);
+ for (int i=0; i<m; i++) bar[i] = foo[i]-(T *)data;
+ out->send(m,bar);
+ }
+} GRID_END
+
+\classinfo { IEVAL(rself,"install '#grade',1,1"); }
+\end class GridGrade
+
+//****************************************************************
+//\class GridMedian < GridObject
+//****************************************************************
+
+\class GridTranspose < GridObject
+struct GridTranspose : GridObject {
+ \attr int dim1;
+ \attr int dim2;
+ int d1,d2,na,nb,nc,nd; // temporaries
+ \decl void initialize (int dim1=0, int dim2=1);
+ \decl void _1_float (int dim1);
+ \decl void _2_float (int dim2);
+ \grin 0
+};
+
+\def void _1_float (int dim1) { this->dim1=dim1; }
+\def void _2_float (int dim2) { this->dim2=dim2; }
+
+GRID_INLET(GridTranspose,0) {
+ STACK_ARRAY(int32,v,in->dim->n);
+ COPY(v,in->dim->v,in->dim->n);
+ d1=dim1; d2=dim2;
+ if (d1<0) d1+=in->dim->n;
+ if (d2<0) d2+=in->dim->n;
+ if (d1>=in->dim->n || d2>=in->dim->n || d1<0 || d2<0)
+ RAISE("would swap dimensions %d and %d but this grid has only %d dimensions",
+ dim1,dim2,in->dim->n);
+ memswap(v+d1,v+d2,1);
+ if (d1==d2) {
+ out=new GridOutlet(this,0,new Dim(in->dim->n,v), in->nt);
+ } else {
+ nd = in->dim->prod(1+max(d1,d2));
+ nc = in->dim->v[max(d1,d2)];
+ nb = in->dim->prod(1+min(d1,d2))/nc/nd;
+ na = in->dim->v[min(d1,d2)];
+ out=new GridOutlet(this,0,new Dim(in->dim->n,v), in->nt);
+ in->set_factor(na*nb*nc*nd);
+ }
+ // Turns a Grid[*,na,*nb,nc,*nd] into a Grid[*,nc,*nb,na,*nd].
+} GRID_FLOW {
+ STACK_ARRAY(T,res,na*nb*nc*nd);
+ if (dim1==dim2) { out->send(n,data); return; }
+ for (; n; n-=na*nb*nc*nd, data+=na*nb*nc*nd) {
+ for (int a=0; a<na; a++)
+ for (int b=0; b<nb; b++)
+ for (int c=0; c<nc; c++)
+ COPY(res +((c*nb+b)*na+a)*nd,
+ data+((a*nb+b)*nc+c)*nd,nd);
+ out->send(na*nb*nc*nd,res);
+ }
+} GRID_END
+
+\def void initialize (int dim1=0, int dim2=1) {
+ rb_call_super(argc,argv);
+ this->dim1 = dim1;
+ this->dim2 = dim2;
+}
+
+\classinfo { IEVAL(rself,"install '#transpose',3,1"); }
+\end class GridTranspose
+
+//****************************************************************
+\class GridReverse < GridObject
+struct GridReverse : GridObject {
+ \attr int dim1; // dimension to act upon
+ int d; // temporaries
+ \decl void initialize (int dim1=0);
+ \decl void _1_float (int dim1);
+ \grin 0
+};
+
+\def void _1_float (int dim1) { this->dim1=dim1; }
+
+GRID_INLET(GridReverse,0) {
+ d=dim1;
+ if (d<0) d+=in->dim->n;
+ if (d>=in->dim->n || d<0)
+ RAISE("would reverse dimension %d but this grid has only %d dimensions",
+ dim1,in->dim->n);
+ out=new GridOutlet(this,0,new Dim(in->dim->n,in->dim->v), in->nt);
+ in->set_factor(in->dim->prod(d));
+} GRID_FLOW {
+ int f1=in->factor(), f2=in->dim->prod(d+1);
+ while (n) {
+ int hf1=f1/2;
+ Pt<T> data2 = data+f1-f2;
+ for (int i=0; i<hf1; i+=f2) memswap(data+i,data2-i,f2);
+ out->send(f1,data);
+ data+=f1; n-=f1;
+ }
+} GRID_END
+
+\def void initialize (int dim1=0) {
+ rb_call_super(argc,argv);
+ this->dim1 = dim1;
+}
+
+\classinfo { IEVAL(rself,"install '#reverse',2,1"); }
+\end class GridReverse
+
+//****************************************************************
+\class GridCentroid2 < GridObject
+struct GridCentroid2 : GridObject {
+ \decl void initialize ();
+ \grin 0 int
+ int sumx,sumy,sum,y; // temporaries
+};
+
+GRID_INLET(GridCentroid2,0) {
+ if (in->dim->n != 3) RAISE("expecting 3 dims");
+ if (in->dim->v[2] != 1) RAISE("expecting 1 channel");
+ in->set_factor(in->dim->prod(1));
+ out=new GridOutlet(this,0,new Dim(2), in->nt);
+ sumx=0; sumy=0; sum=0; y=0;
+} GRID_FLOW {
+ int sx = in->dim->v[1];
+ while (n) {
+ for (int x=0; x<sx; x++) {
+ sumx+=x*data[x];
+ sumy+=y*data[x];
+ sum += data[x];
+ }
+ n-=sx;
+ data+=sx;
+ y++;
+ }
+} GRID_FINISH {
+ STACK_ARRAY(int32,blah,2);
+ blah[0] = sum ? sumy/sum : 0;
+ blah[1] = sum ? sumx/sum : 0;
+ out->send(2,blah);
+} GRID_END
+
+\def void initialize () {
+ rb_call_super(argc,argv);
+}
+
+\classinfo { IEVAL(rself,"install '#centroid2',1,1"); }
+\end class GridCentroid2
+
+//****************************************************************
+\class GridPerspective < GridObject
+struct GridPerspective : GridObject {
+ \attr int32 z;
+ \grin 0
+ \decl void initialize (int32 z=256);
+};
+
+GRID_INLET(GridPerspective,0) {
+ int n = in->dim->n;
+ STACK_ARRAY(int32,v,n);
+ COPY(v,in->dim->v,n);
+ v[n-1]--;
+ in->set_factor(in->dim->get(in->dim->n-1));
+ out=new GridOutlet(this,0,new Dim(n,v),in->nt);
+} GRID_FLOW {
+ int m = in->factor();
+ for (; n; n-=m,data+=m) {
+ op_mul->map(m-1,data,(T)z);
+ op_div->map(m-1,data,data[m-1]);
+ out->send(m-1,data);
+ }
+} GRID_END
+
+\def void initialize (int32 z) {rb_call_super(argc,argv); this->z=z; }
+\classinfo { IEVAL(rself,"install '#perspective',1,1"); }
+\end class GridPerspective
+
+static Numop *OP(Ruby x) { return FIX2PTR(Numop,rb_hash_aref(op_dict,x)); }
+
+void startup_flow_objects () {
+ op_add = OP(SYM(+));
+ op_sub = OP(SYM(-));
+ op_mul = OP(SYM(*));
+ op_shl = OP(SYM(<<));
+ op_mod = OP(SYM(%));
+ op_and = OP(SYM(&));
+ op_div = OP(SYM(/));
+ op_put = OP(SYM(put));
+ \startall
+}
diff --git a/externals/gridflow/base/flow_objects.rb b/externals/gridflow/base/flow_objects.rb
new file mode 100644
index 00000000..6ea06c56
--- /dev/null
+++ b/externals/gridflow/base/flow_objects.rb
@@ -0,0 +1,1476 @@
+=begin
+ $Id: flow_objects.rb,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+=end
+
+module GridFlow
+
+#-------- fClasses for: control + misc
+
+# a dummy class that gives access to any stuff global to GridFlow.
+FObject.subclass("gridflow",1,1) {
+ def _0_profiler_reset
+ GridFlow.fobjects.each {|o,*| o.total_time = 0 }
+ GridFlow.profiler_reset2 if GridFlow.respond_to? :profiler_reset2
+ end
+ def _0_profiler_dump
+ ol = []
+ total=0
+ post "-"*32
+ post "microseconds percent pointer constructor"
+ GridFlow.fobjects.each {|o,*| ol.push o }
+
+ # HACK: BitPacking is not a real fobject
+ # !@#$ is this still necessary?
+ ol.delete_if {|o| not o.respond_to? :total_time }
+
+ ol.sort! {|a,b| a.total_time <=> b.total_time }
+ ol.each {|o| total += o.total_time }
+ total=1 if total<1
+ total_us = 0
+ ol.each {|o|
+ ppm = o.total_time * 1000000 / total
+ us = (o.total_time*1E6/GridFlow.cpu_hertz).to_i
+ total_us += us
+ post "%12d %2d.%04d %08x %s", us,
+ ppm/10000, ppm%10000, o.object_id, o.args
+ }
+ post "-"*32
+ post "sum of accounted microseconds: #{total_us}"
+ if GridFlow.respond_to? :memcpy_calls then
+ post "memcpy calls: #{GridFlow.memcpy_calls} "+
+ "; bytes: #{GridFlow.memcpy_bytes}"+
+ "; time: #{GridFlow.memcpy_time}"
+ end
+ if GridFlow.respond_to? :malloc_calls then
+ post "malloc calls: #{GridFlow.malloc_calls} "+
+ "; bytes: #{GridFlow.malloc_bytes}"+
+ "; time: #{GridFlow.malloc_time}"
+ end
+ post "-"*32
+ end
+ def _0_formats
+ post "-"*32
+ GridFlow.fclasses.each {|k,v|
+ next if not /#in:/ =~ k
+ modes = case v.flags
+ when 2; "#out"
+ when 4; "#in"
+ when 6; "#in/#out"
+ end
+ post "%s %s: %s", modes, k, v.description
+ if v.respond_to? :info then
+ post "-> %s", v.info
+ end
+ }
+ post "-"*32
+ end
+ # security issue if patches shouldn't be allowed to do anything they want
+ def _0_eval(*l)
+ s = l.map{|x|x.to_i.chr}.join""
+ post "ruby: %s", s
+ post "returns: %s", eval(s).inspect
+ end
+ add_creator "@global"
+ GridFlow.bind "gridflow", "gridflow" rescue Exception
+}
+FObject.subclass("fps",1,1) {
+ def initialize(*options)
+ super
+ @history = [] # list of delays between incoming messages
+ @last = 0.0 # when was last time
+ @duration = 0.0 # how much delay since last summary
+ @period = 1 # minimum delay between summaries
+ @detailed = false
+ @mode = :real
+ options.each {|o|
+ case o
+ when :detailed; @detailed=true
+ when :real,:user,:system,:cpu; @mode=o
+ end
+ }
+ def @history.moment(n=1)
+ sum = 0
+ each {|x| sum += x**n }
+ sum/length
+ end
+ end
+ def method_missing(*a) end # ignore non-bangs
+ def _0_period x; @period=x end
+ def publish
+ @history.sort!
+ n=@history.length
+ fps = @history.length/@duration
+ if not @detailed then send_out 0, fps; return end
+ send_out 0, fps,
+ 1000*@history.min,
+ 500*(@history[n/2]+@history[(n-1)/2]),
+ 1000*@history.max,
+ 1000/fps,
+ 1000*(@history.moment(2) - @history.moment(1)**2)**0.5
+ end
+ def _0_bang
+ t = case @mode
+ when :real; Time.new.to_f
+ when :user; Process.times.utime
+ when :system; Process.times.stime
+ when :cpu; GridFlow.rdtsc/GridFlow.cpu_hertz
+ end
+ @history.push t-@last
+ @duration += t-@last
+ @last = t
+ return if @duration<@period
+ fps = @history.length/@duration
+ publish if fps>0.001
+ @history.clear
+ @duration = 0
+ end
+}
+
+# to see what the messages look like when they get on the Ruby side.
+FObject.subclass("rubyprint",1,0) {
+ def initialize(*a)
+ super
+ @time = !!(a.length and a[0]==:time)
+ end
+
+ def method_missing(s,*a)
+ s=s.to_s
+ pre = if @time then sprintf "%10.6f ", Time.new.to_f else "" end
+ case s
+ when /^_0_/; post "%s","#{pre}#{s[3..-1]}: #{a.inspect}"
+ else super
+ end
+ end
+}
+FObject.subclass("printargs",0,0) {
+ def initialize(*a) super; post a.inspect end
+}
+GridObject.subclass("#print",1,0) {
+ install_rgrid 0, true
+ attr_accessor :name
+ def initialize(name=nil)
+ super # don't forget super!!!
+ if name then @name = name.to_s+": " else @name="" end
+ @base=10; @format="d"; @trunc=70; @maxrows=50
+ end
+ def end_hook; end # other hijackability
+ def format
+ case @nt
+ when :float32; '%6.6f'
+ when :float64; '%14.14f'
+ else "%#{@columns}#{@format}" end
+ end
+ def _0_base(x)
+ @format = (case x
+ when 2; "b"
+ when 8; "o"
+ when 10; "d"
+ when 16; "x"
+ else raise "base #{x} not supported" end)
+ @base = x
+ end
+ def _0_trunc(x)
+ x=x.to_f
+ (0..240)===x or raise "out of range (not in 0..240 range)"
+ @trunc = x
+ end
+ def _0_maxrows(x) @maxrows = x.to_i end
+ def make_columns udata
+ min = udata.min
+ max = udata.max
+ @columns = "" # huh?
+ @columns = [
+ sprintf(format,min).length,
+ sprintf(format,max).length].max
+ end
+ def unpack data
+ ps = GridFlow.packstring_for_nt @nt
+ data.unpack ps
+ end
+ def _0_rgrid_begin
+ @dim = inlet_dim 0
+ @nt = inlet_nt 0
+ @data = ""
+ end
+ def _0_rgrid_flow(data) @data << data end
+ def _0_rgrid_end
+ head = "#{name}Dim[#{@dim.join','}]"
+ head << "(#{@nt})" if @nt!=:int32
+ head << ": "
+ if @dim.length > 3 then
+ post head+" (not printed)"
+ elsif @dim.length < 2 then
+ udata = unpack @data
+ make_columns udata
+ post trunc(head + dump(udata))
+ elsif @dim.length == 2 then
+ post head
+ udata = unpack @data
+ make_columns udata
+ sz = udata.length/@dim[0]
+ rown = 1
+ for row in 0...@dim[0] do
+ post trunc(dump(udata[sz*row,sz]))
+ rown += 1
+ (post "..."; break) if rown>@maxrows
+ end
+ elsif @dim.length == 3 then
+ post head
+ make_columns unpack(@data)
+ sz = @data.length/@dim[0]
+ sz2 = sz/@dim[1]
+ rown = 1
+ for row in 0...@dim[0]
+ column=0; str=""
+ for col in 0...@dim[1]
+ str << "(" << dump(unpack(@data[sz*row+sz2*col,sz2])) << ")"
+ break if str.length>@trunc
+ end
+ post trunc(str)
+ rown += 1
+ (post "..."; break) if rown>@maxrows
+ end
+ end
+ @data,@dim,@nt = nil
+ end_hook
+ end
+ def dump(udata,sep=" ")
+ f = format
+ udata.map{|x| sprintf f,x }.join sep
+ end
+ def trunc s
+ if s.length>@trunc then s[0...@trunc]+" [...]" else s end
+ end
+}
+GridPack =
+GridObject.subclass("#pack",1,1) {
+ install_rgrid 0
+ class<<self;attr_reader :ninlets;end
+ def initialize(n=nil,cast=:int32)
+ n||=self.class.ninlets
+ n>=16 and raise "too many inlets"
+ super
+ @data=[0]*n
+ @cast=cast
+ @ps =GridFlow.packstring_for_nt cast
+ end
+ def initialize2
+ return if self.class.ninlets>1
+ add_inlets @data.length-1
+ end
+ def _0_cast(cast)
+ @ps = GridFlow.packstring_for_nt cast
+ @cast = cast
+ end
+ def self.define_inlet i
+ module_eval "
+ def _#{i}_int x; @data[#{i}]=x; _0_bang; end
+ def _#{i}_float x; @data[#{i}]=x; _0_bang; end
+ "
+ end
+ (0...15).each {|x| define_inlet x }
+ def _0_bang
+ send_out_grid_begin 0, [@data.length], @cast
+ send_out_grid_flow 0, @data.pack(@ps), @cast
+ end
+ self
+}
+
+# the install_rgrids in the following are hacks so that
+# outlets can work. (install_rgrid is supposed to be for receiving)
+# maybe GF-0.8 doesn't need that.
+GridPack.subclass("@two", 2,1) { install_rgrid 0 }
+GridPack.subclass("@three",3,1) { install_rgrid 0 }
+GridPack.subclass("@four", 4,1) { install_rgrid 0 }
+GridPack.subclass("@eight",8,1) { install_rgrid 0 }
+GridObject.subclass("#unpack",1,0) {
+ install_rgrid 0, true
+ def initialize(n)
+ @n=n
+ n>=10 and raise "too many outlets"
+ super
+ end
+ def initialize2; add_outlets @n end
+ def _0_rgrid_begin
+ inlet_dim(0)==[@n] or raise "expecting Dim[#{@n}], got Dim#{@dim}"
+ inlet_set_factor 0,@n
+ end
+ def _0_rgrid_flow data
+ @ps = GridFlow.packstring_for_nt inlet_nt(0)
+ duh = data.unpack(@ps)
+ i=duh.size-1
+ until i<0 do send_out i,duh[i]; i-=1 end
+ end
+ def _0_rgrid_end; end
+}
+
+GridObject.subclass("#export_symbol",1,1) {
+ install_rgrid 0
+ def _0_rgrid_begin; @data="" end
+ def _0_rgrid_flow data; @data << data; end
+ def _0_rgrid_end
+ send_out 0, :symbol, @data.unpack("I*").pack("c*").intern
+ end
+}
+GridObject.subclass("unix_time",1,3) {
+ install_rgrid 0
+ def _0_bang
+ t = Time.new
+ tt = t.to_s
+ send_out_grid_begin 0, [tt.length], :uint8
+ send_out_grid_flow 0, tt, :uint8
+ send_out 1, t.to_i
+ send_out 2, t.to_f-t.to_f.floor
+ end
+}
+### test with "shell xlogo &" -> [exec]
+FObject.subclass("exec",1,0) {
+ def _0_shell(*a) system(a.map!{|x| x.to_s }.join(" ")) end
+}
+FObject.subclass("renamefile",1,0) {
+ def initialize; end
+ def _0_list(a,b) File.rename(a.to_s,b.to_s) end
+}
+FObject.subclass("ls",1,1) {
+ def _0_symbol(s) send_out 0, :list, *Dir.new(s.to_s).map {|x| x.intern } end
+}
+
+#-------- fClasses for: math
+
+FPatcher.subclass("gfmessagebox",1,1) {
+ def initialize(*a) @a=a end
+ def _0_float(x) send_out 0, *@a.map {|y| if y=="$1".intern then x else y end } end
+ def _0_symbol(x) send_out 0, *@a.map {|y| if y=="$1".intern then x else y end } end
+}
+
+FPatcher.subclass("@!",1,1) {
+ @fobjects = ["# +","#type","gfmessagebox list $1 #"]
+ @wires = [-1,0,1,0, 1,0,2,0, 2,0,0,1, -1,0,0,0, 0,0,-1,0]
+ def initialize(sym)
+ super
+ @fobjects[0].send_in 0, case sym
+ when :rand; "op rand"; when :sqrt; "op sqrt"
+ when :abs; "op abs-"; when :sq; "op sq-"
+ else raise "bork BORK bork" end
+ end
+}
+FPatcher.subclass("@fold",2,1) {
+ @fobjects = ["#fold +","gfmessagebox seed $1"]
+ @wires = [-1,0,0,0, -1,1,1,0, 1,0,0,1, 0,0,-1,0]
+ def initialize(op,seed=0) super; o=@fobjects[0]
+ o.send_in 0, :op, op; o.send_in 0, :seed, seed end
+}
+FPatcher.subclass("@scan",2,1) {
+ @fobjects = ["#scan +","gfmessagebox seed $1"]
+ @wires = [-1,0,0,0, -1,1,1,0, 1,0,0,1, 0,0,-1,0]
+ def initialize(op,seed=0) super; o=@fobjects[0]
+ o.send_in 0, :op, op; o.send_in 0, :seed, seed end
+}
+FPatcher.subclass("@inner",3,1) {
+ @fobjects = ["#inner","gfmessagebox seed $1"]
+ @wires = [-1,0,0,0, -1,1,1,0, 1,0,0,0, 0,0,-1,0, -1,2,0,1]
+ def initialize(op=:*,fold=:+,seed=0,r=0) super; o=@fobjects[0]
+ o.send_in 0, :op, op; o.send_in 0, :fold, fold
+ o.send_in 0, :seed, seed; o.send_in 1, r end
+}
+FPatcher.subclass("@convolve",2,1) {
+ @fobjects = ["#convolve"]
+ @wires = [-1,0,0,0, -1,2,0,1, 0,0,-1,0]
+ def initialize(op=:*,fold=:+,seed=0,r=0) super; o=@fobjects[0]
+ o.send_in 0, :op, op; o.send_in 0, :fold, fold
+ o.send_in 0, :seed, seed; o.send_in 1, r end
+}
+
+#-------- fClasses for: video
+
+FPatcher.subclass("@scale_to",2,1) {
+ @fobjects = [
+ "@for {0 0} {42 42} {1 1}","@ *","@ /",
+ "@store","#dim","@redim {2}","#finished",
+ ]
+ @wires = []
+ for i in 1..3 do @wires.concat [i-1,0,i,0] end
+ @wires.concat [3,0,-1,0, 4,0,5,0, 5,0,1,1, 6,0,0,0,
+ -1,0,4,0, -1,0,3,1, -1,0,6,0, -1,1,0,1, -1,1,2,1]
+ def initialize(size)
+ (size.length==2 and Numeric===size[0] and Numeric===size[1]) or
+ raise "expecting {height width}"
+ super
+ send_in 1, size
+ end
+}
+
+#<vektor> told me to:
+# RGBtoYUV : @fobjects = ["#inner ( 3 3 # 66 -38 112 128 -74 -94 25 112 -18 )",
+# "@ >> 8","@ + {16 128 128}"]
+# YUVtoRGB : @fobjects = ["@ - ( 16 128 128 )",
+# "#inner ( 3 3 # 298 298 298 0 -100 516 409 -208 0 )","@ >> 8"]
+
+FPatcher.subclass("#rotate",2,1) {
+ @fobjects = ["@inner * + 0","@ >> 8"]
+ @wires = [-1,0,0,0, 0,0,1,0, 1,0,-1,0]
+ def update_rotator
+ rotator = (0...@axis[2]).map {|i|
+ (0...@axis[2]).map {|j|
+ if i==j then 256 else 0 end
+ }
+ }
+ th = @angle * Math::PI / 18000
+ scale = 1<<8
+ (0...2).each {|i|
+ (0...2).each {|j|
+ rotator[@axis[i]][@axis[j]] =
+ (scale*Math.cos(th+(j-i)*Math::PI/2)).to_i
+ }
+ }
+ @fobjects[0].send_in 2,
+ @axis[2], @axis[2], "#".intern, *rotator.flatten
+ end
+ def _0_axis(from,to,total)
+ total>=0 or raise "total-axis number incorrect"
+ from>=0 and from<total or raise "from-axis number incorrect"
+ to >=0 and to <total or raise "to-axis number incorrect"
+ @axis = [from,to,total]
+ update_rotator
+ end
+ def initialize(rot=0,axis=[0,1,2])
+ super
+ @angle=0
+ _0_axis(*axis)
+ send_in 1, rot
+ end
+ def _1_int(angle) @angle = angle; update_rotator end
+ alias _1_float _1_int
+}
+
+FObject.subclass("foreach",1,1) {
+ def initialize() super end
+ def _0_list(*a)
+ a.each {|e|
+ if Symbol===e then
+ send_out 0,:symbol,e
+ else
+ send_out 0,e
+ end
+ }
+ end
+}
+FObject.subclass("listflatten",1,1) {
+ def initialize() super end
+ def _0_list(*a) send_out 0,:list,*a.flatten end
+}
+FObject.subclass("rubysprintf",2,1) {
+ def initialize(*format) _1_list(format) end
+ def _0_list(*a) send_out 0, :symbol, (sprintf @format, *a).intern end
+ alias _0_float _0_list
+ alias _0_symbol _0_list
+ def _1_list(*format) @format = format.join(" ") end
+ alias _1_symbol _1_list
+}
+
+#-------- fClasses for: jMax compatibility
+
+class JMaxUDPSend < FObject
+ def initialize(host,port)
+ super
+ @socket = UDPSocket.new
+ @host,@port = host.to_s,port.to_i
+ end
+ def encode(x)
+ case x
+ when Integer; "\x03" + [x].pack("N")
+ when Float; "\x04" + [x].pack("g")
+ when Symbol, String; "\x01" + x.to_s + "\x02"
+ end
+ end
+ def method_missing(sel,*args)
+ sel=sel.to_s.sub(/^_\d_/, "")
+ @socket.send encode(sel) +
+ args.map{|arg| encode(arg) }.join("") + "\x0b",
+ 0, @host, @port
+ end
+ def delete; @socket.close end
+ install "jmax_udpsend", 1, 0
+end
+
+class JMaxUDPReceive < FObject
+ def initialize(port)
+ super
+ @socket = UDPSocket.new
+ @port = port.to_i
+ @socket.bind nil, @port
+ @clock = Clock.new self
+ @clock.delay 0
+ end
+ def decode s
+ n = s.length
+ i=0
+ m = []
+ case s[i]
+ when 3; i+=5; m << s[i-4,4].unpack("N")[0]
+ when 4; i+=5; m << s[i-4,4].unpack("g")[0]
+ when 1; i2=s.index("\x02",i); m << s[i+1..i2-1].intern; i=i2+1
+ when 11; break
+ else raise "unknown code in udp packet"
+ end while i<n
+ m
+ end
+ def call
+ ready_to_read = IO.select [@socket],[],[],0
+ return if not ready_to_read
+ data,sender = @socket.recvfrom 1024
+ return if not data
+ send_out 1, sender.map {|x| x=x.intern if String===x; x }
+ send_out 0, *(decode data)
+ @clock.delay 50
+ end
+ def delete; @clock.unset; @socket.close end
+ install "jmax_udpreceive", 0, 2
+end
+
+class JMax4UDPSend < FObject
+ def initialize(host,port)
+ super
+ @socket = UDPSocket.new
+ @host,@port = host.to_s,port.to_i
+ @symbols = {}
+ end
+ def encode(x)
+ case x
+ when Integer; "\x01" + [x].pack("N")
+ when Float; "\x02" + [x].pack("G")
+ when Symbol, String
+ x = x.to_s
+ y = x.intern
+ if not @symbols[y]
+ @symbols[y]=true
+ "\x04" + [y].pack("N") + x + "\0"
+ else
+ "\x03" + [y].pack("N")
+ end
+ end
+ end
+ def method_missing(sel,*args)
+ sel=sel.to_s.sub(/^_\d_/, "")
+ sel=(case sel; when "int","float"; ""; else encode(sel) end)
+ args=args.map{|arg| encode(arg) }.join("")
+ @socket.send(sel+args+"\x0f", 0, @host, @port)
+ end
+ def delete; @socket.close end
+ install "jmax4_udpsend", 1, 0
+end
+
+class JMax4UDPReceive < FObject
+ def initialize(port)
+ super
+ @socket = UDPSocket.new
+ @port = port.to_i
+ @socket.bind nil, @port
+ @clock = Clock.new self
+ @clock.delay 0
+ @symbols = {}
+ end
+ def decode s
+ n = s.length
+ i=0
+ m = []
+ case s[i]
+ when 1; i+=5; m << s[i-4,4].unpack("N")[0]
+ when 2; i+=9; m << s[i-8,8].unpack("G")[0]
+ when 3
+ i+=5; sid = s[i-4,4].unpack("N")[0]
+ m << @symbols[sid]
+ when 4
+ i+=5; sid = s[i-4,4].unpack("N")[0]
+ i2=s.index("\x00",i)
+ @symbols[sid] = s[i..i2-1].intern
+ m << @symbols[sid]
+ i=i2+1
+ when 15; break
+ else post "unknown code %d in udp packet %s", s[i], s.inspect; return m
+ end while i<n
+ m
+ end
+ def call
+ ready_to_read = IO.select [@socket],[],[],0
+ return if not ready_to_read
+ data,sender = @socket.recvfrom 1024
+ return if not data
+ send_out 1, sender.map {|x| x=x.intern if String===x; x }
+ send_out 0, *(decode data)
+ @clock.delay 50
+ end
+ def delete; @clock.unset; @socket.close end
+ install "jmax4_udpreceive", 0, 2
+end
+
+class PDNetSocket < FObject
+ def initialize(host,port,protocol=:udp,*options)
+ super
+ _1_connect(host,port,protocol)
+ @options = {}
+ options.each {|k|
+ k=k.intern if String===k
+ @options[k]=true
+ }
+ end
+ def _1_connect(host,port,protocol=:udp)
+ host = host.to_s
+ port = port.to_i
+ @host,@port,@protocol = host.to_s,port.to_i,protocol
+ case protocol
+ when :udp
+ @socket = UDPSocket.new
+ if host=="-" then
+ @socket.bind nil, port
+ end
+ when :tcp
+ if host=="-" then
+ @server = TCPServer.new("localhost",port)
+ else
+ @socket = TCPSocket.new(host,port)
+ end
+
+ end
+ @clock = Clock.new self
+ @clock.delay 0
+ @data = ""
+ end
+ def encode(x)
+ x=x.to_i if @options[:nofloat] and Float===x
+ x.to_s
+ end
+ def method_missing(sel,*args)
+ sel=sel.to_s
+ sel.sub!(/^_\d_/, "") or return super
+ sel=(case sel; when "int","float"; ""; else encode(sel) end)
+ msg = [sel,*args.map{|arg| encode(arg) }].join(" ")
+ if @options[:nosemicolon] then msg << "\n" else msg << ";\n" end
+ post "encoding as: %s", msg.inspect if @options[:debug]
+ case @protocol
+ when :udp; @socket.send msg, 0, @host, @port
+ when :tcp; @socket.send msg, 0
+ end
+ end
+ def delete; @clock.unset; @socket.close end
+ def decode s
+ post "decoding from: %s", s.inspect if @options[:debug]
+ s.chomp!("\n")
+ s.chomp!("\r")
+ s.chomp!(";")
+ a=s.split(/[\s\0]+/)
+ a.shift if a[0]==""
+ a.map {|x|
+ case x
+ when /-?\d+$/; x.to_i
+ when /-?\d/; x.to_f
+ else x.intern
+ end
+ }
+ end
+ def call
+ ready_to_accept = IO.select [@server],[],[],0 if @server
+ if ready_to_accept
+ @socket.close if @socket
+ @socket = @server.accept
+ end
+ ready_to_read = IO.select [@socket],[],[],0 if @socket
+ return if not ready_to_read
+ case @protocol
+ when :udp
+ data,sender = @socket.recvfrom 1024
+ send_out 1, sender.map {|x| x=x.intern if String===x; x }
+ send_out 0, *(decode data)
+ when :tcp
+ @data << @socket.sysread(1024)
+ sender = @socket.peeraddr
+ loop do
+ n = /\n/ =~ @data
+ break if not n
+ send_out 1, sender.map {|x| x=x.intern if String===x; x }
+ send_out 0, *(decode @data.slice!(0..n))
+ end
+ end
+ @clock.delay 50
+ end
+ install "pd_netsocket", 2, 2
+end
+
+PDNetSocket.subclass("pd_netsend",1,0) {}
+PDNetSocket.subclass("pd_netreceive",0,2) {
+ def initialize(port) super("-",port) end
+}
+
+# bogus class for representing objects that have no recognized class.
+FObject.subclass("broken",0,0) {
+ def args; a=@args.dup; a[7,0] = " "+classname; a end
+}
+
+FObject.subclass("fork",1,2) {
+ def method_missing(sel,*args)
+ sel.to_s =~ /^_(\d)_(.*)$/ or super
+ send_out 1,$2.intern,*args
+ send_out 0,$2.intern,*args
+ end
+}
+FObject.subclass("shunt",2,0) {
+ def initialize(n=2,i=0) super; @n=n; @i=i end
+ def initialize2; add_outlets @n end
+ def method_missing(sel,*args)
+ sel.to_s =~ /^_(\d)_(.*)$/ or super
+ send_out @i,$2.intern,*args
+ end
+ def _1_int i; @i=i.to_i % @n end
+ alias :_1_float :_1_int
+ # hack: this is an alias.
+ class Demux < self; install "demux", 2, 0; end
+}
+
+#-------- fClasses for: jmax2pd
+
+ FObject.subclass("button",1,1) {
+ def method_missing(*) send_out 0 end
+ }
+ FObject.subclass("toggle",1,1) {
+ def _0_bang; @state ^= true; trigger end
+ def _0_int x; @state = x!=0; trigger end
+ def trigger; send_out 0, (if @state then 1 else 0 end) end
+ }
+ FObject.subclass("jpatcher",0,0) {
+ def initialize(*a) super; @subobjects={} end
+ attr_accessor :subobjects
+ }
+ FObject.subclass("jcomment",0,0) {}
+ FObject.subclass("loadbang",0,1) { def trigger; send_out 0 end }
+ FObject.subclass("messbox",1,1) {
+ def _0_bang; send_out 0, *@argv end
+ def clear; @argv=[]; end
+ def append(*argv) @argv<<argv; end
+ }
+
+#-------- fClasses for: list manipulation (jMax-compatible)
+
+ FObject.subclass("listmake",2,1) {
+ def initialize(*a) @a=a end
+ def _0_list(*a) @a=a; _0_bang end
+ def _1_list(*a) @a=a end
+ def _0_bang; send_out 0, :list, *@a end
+ }
+ FObject.subclass("listlength",1,1) {
+ def initialize() super end
+ def _0_list(*a) send_out 0, a.length end
+ }
+ FObject.subclass("listelement",2,1) {
+ def initialize(i=0) super; @i=i.to_i end
+ def _1_int(i) @i=i.to_i end; alias _1_float _1_int
+ def _0_list(*a)
+ e=a[@i]
+ if Symbol===e then
+ send_out 0, :symbol, e
+ else
+ send_out 0, e
+ end
+ end
+ }
+ FObject.subclass("listsublist",3,1) {
+ def initialize(i=0,n=1) super; @i,@n=i.to_i,n.to_i end
+ def _1_int(i) @i=i.to_i end; alias _1_float _1_int
+ def _2_int(n) @n=n.to_i end; alias _2_float _2_int
+ def _0_list(*a) send_out 0, :list, *a[@i,@n] end
+ }
+ FObject.subclass("listprepend",2,1) {
+ def initialize(*b) super; @b=b end
+ def _0_list(*a) a[0,0]=@b; send_out 0, :list, *a end
+ def _1_list(*b) @b=b end
+ }
+ FObject.subclass("listappend",2,1) {
+ def initialize(*b) super; @b=b end
+ def _0_list(*a) a[a.length,0]=@b; send_out 0, :list, *a end
+ def _1_list(*b) @b=b end
+ }
+ FObject.subclass("listreverse",1,1) {
+ def initialize() super end
+ def _0_list(*a) send_out 0,:list,*a.reverse end
+ }
+ FObject.subclass("messageprepend",2,1) {
+ def initialize(*b) super; @b=b end
+ def _0_(*a) a[0,0]=@b; send_out 0, *a end
+ def _1_list(*b) @b=b end
+ def method_missing(sym,*a)
+ (m = /(_\d_)(.*)/.match sym.to_s) or return super
+ _0_ m[2].intern, *a
+ end
+ }
+ FObject.subclass("messageappend",2,1) {
+ def initialize(*b) super; @b=b end
+ def _0_(*a) a[a.length,0]=@b; send_out 0, *a end
+ def _1_list(*b) @b=b end
+ def method_missing(sym,*a)
+ (m = /(_\d_)(.*)/.match sym.to_s) or return super
+ _0_ m[2].intern, *a
+ end
+ }
+
+# this was the original demo for the Ruby/jMax/PureData bridges
+# FObjects are Ruby Objects that are exported to the PureData system.
+# _0_bang means bang message on inlet 0
+# FObject#send_out sends a message through an outlet
+FObject.subclass("for",3,1) {
+ attr_accessor :start, :stop, :step
+ def cast(key,val)
+ val = Integer(val) if Float===val
+ raise ArgumentError, "#{key} isn't a number" unless Integer===val
+ end
+ def initialize(start,stop,step)
+ super
+ cast("start",start)
+ cast("stop",stop)
+ cast("step",step)
+ @start,@stop,@step = start,stop,step
+ end
+ def _0_bang
+ x = start
+ if step > 0
+ (send_out 0, x; x += step) while x < stop
+ elsif step < 0
+ (send_out 0, x; x += step) while x > stop
+ end
+ end
+ def _0_float(x) self.start=x; _0_bang end
+ alias _1_float stop=
+ alias _2_float stop=
+}
+FObject.subclass("oneshot",2,1) {
+ def initialize(state=true) @state=state!=0 end
+ def method_missing(sel,*a)
+ m = /^_0_(.*)$/.match(sel.to_s) or return super
+ send_out 0, m[1].intern, *a if @state
+ @state=false
+ end
+ def _1_int(state) @state=state!=0 end
+ alias _1_float _1_int
+ def _1_bang; @state=true end
+}
+FObject.subclass("inv+",2,1) {
+ def initialize(b=0) @b=b end; def _1_float(b) @b=b end
+ def _0_float(a) send_out 0, :float, @b-a end
+}
+FObject.subclass("inv*",2,1) {
+ def initialize(b=0) @b=b end; def _1_float(b) @b=b end
+ def _0_float(a) send_out 0, :float, @b/a end
+}
+FObject.subclass("range",1,1) {
+ def initialize(*a) @a=a end
+ def initialize2
+ add_inlets @a.length
+ add_outlets @a.length
+ end
+ def _0_float(x) i=0; i+=1 until @a[i]==nil or x<@a[i]; send_out i,x end
+ def method_missing(sel,*a)
+ m = /^(_\d+_)(.*)/.match(sel.to_s) or return super
+ m[2]=="float" or return super
+ @a[m[1].to_i-1] = a[0]
+ post "setting a[#{m[1].to_i-1}] = #{a[0]}"
+ end
+}
+
+#-------- fClasses for: GUI
+
+module Gooey # to be included in any FObject class
+ def initialize(*)
+ super
+ @selected=false
+ @bg = "#ffffff" # white background
+ @bgb = "#000000" # black border
+ @bgs = "#0000ff" # blue border when selected
+ @fg = "#000000" # black foreground
+ @rsym = "#{self.class}#{self.object_id}".intern # unique id for use in Tcl
+ @can = nil # the canvas number
+ @canvas = nil # the canvas string
+ @y,@x = 0,0 # position on canvas
+ @sy,@sx = 16,16 # size on canvas
+ @font = "Courier -12"
+ @vis = nil
+ end
+ attr_reader :canvas
+ attr_reader :selected
+ def canvas=(can)
+ @can = can if Integer===can
+ @canvas = case can
+ when String; can
+ when Integer; ".x%x.c"%(4*can)
+ else raise "huh?"
+ end
+ end
+ def initialize2(*) GridFlow.bind self, @rsym.to_s end
+ def pd_displace(can,x,y) self.canvas||=can; @x+=x; @y+=y; pd_show(can) end
+ def pd_activate(can,*) self.canvas||=can end
+ def quote(text) # for tcl (isn't completely right ?)
+ text=text.gsub(/[\{\}]/) {|x| "\\"+x }
+ "{#{text}}"
+ end
+ def pd_vis(can,vis)
+ self.canvas||=can; @vis=vis!=0; update end
+ def update; pd_show @can if @vis end
+ def pd_getrect(can)
+ self.canvas||=can
+ @x,@y = get_position(can)
+ # the extra one-pixel on each side was for #peephole only
+ # not sure what to do with this
+ [@x-1,@y-1,@x+@sx+1,@y+@sy+1]
+ end
+ def pd_click(can,x,y,shift,alt,dbl,doit) return 0 end
+ def outline; if selected then @bgs else "#000000" end end
+ def pd_select(can,sel)
+ self.canvas||=can
+ @selected=sel!=0
+ GridFlow.gui %{ #{canvas} itemconfigure #{@rsym} -outline #{outline} \n }
+ end
+ def pd_delete(can) end
+ def pd_show(can)
+ self.canvas||=can
+ @x,@y = get_position can if can
+ end
+ def highlight(color,ratio) # doesn't use self
+ c = /^#(..)(..)(..)/.match(color)[1..3].map {|x| x.hex }
+ c.map! {|x| [255,(x*ratio).to_i].min }
+ "#%02x%02x%02x" % c
+ end
+end
+
+class Display < FObject; include Gooey
+ attr_accessor :text
+ def initialize()
+ super
+ @sel = nil; @args = [] # contents of last received message
+ @text = "..."
+ @sy,@sx = 16,80 # default size of the widget
+ @bg,@bgs,@fg = "#6774A0","#00ff80","#ffff80"
+ end
+ def _0_set_size(sy,sx) @sy,@sx=sy,sx end
+ def atom_to_s a
+ case a
+ when Float; sprintf("%.5f",a).gsub(/\.?0+$/, "")
+ else a.to_s
+ end
+ end
+ def method_missing(sel,*args)
+ m = /^(_\d+_)(.*)/.match(sel.to_s) or return super
+ @sel,@args = m[2].intern,args
+ @text = case @sel
+ when nil; "..."
+ when :float; atom_to_s @args[0]
+ else @sel.to_s + ": " + @args.map{|a| atom_to_s a }.join(' ')
+ end
+ update
+ end
+ def pd_show(can)
+ super
+ return if not canvas or not @vis # can't show for now...
+ GridFlow.gui %{
+ set canvas #{canvas}
+ $canvas delete #{@rsym}TEXT
+ set y #{@y+2}
+ foreach line [split #{quote @text} \\n] {
+ $canvas create text #{@x+2} $y -fill #{@fg} -font #{quote @font}\
+ -text $line -anchor nw -tag #{@rsym}TEXT
+ set y [expr $y+14]
+ }
+ foreach {x1 y1 x2 y2} [$canvas bbox #{@rsym}TEXT] {}
+ set sx [expr $x2-$x1+1]
+ set sy [expr $y2-$y1+3]
+ $canvas delete #{@rsym}
+ $canvas create rectangle #{@x} #{@y} \
+ [expr #{@x}+$sx] [expr #{@y}+$sy] -fill #{@bg} \
+ -tags #{@rsym} -outline #{outline}
+ $canvas lower #{@rsym} #{@rsym}TEXT
+ pd \"#{@rsym} set_size $sy $sx;\n\";
+ }
+ end
+ def pd_delete(can)
+ if @vis
+ GridFlow.gui %{ #{canvas} delete #{@rsym} #{@rsym}TEXT \n}
+ end
+ super
+ end
+ def delete; super end
+ def _0_grid(*foo) # big hack!
+ # hijacking a [#print]
+ gp = FObject["#print"]
+ @text = ""
+ overlord = self
+ gp.instance_eval { @overlord = overlord }
+ def gp.post(fmt,*args) @overlord.text << sprintf(fmt,*args) << "\n" end
+ def gp.end_hook
+ @overlord.instance_eval{@text.chomp!}
+ @overlord.update
+ end
+ #gp.send_in 0, :trunc, 70
+ gp.send_in 0, :maxrows, 20
+ gp.send_in 0, :grid, *foo
+ end
+
+ install "display", 1, 1
+ gui_enable if GridFlow.bridge_name =~ /puredata/
+end
+
+class GridEdit < GridObject; include Gooey
+ def initialize(grid)
+ super
+ @store = GridStore.new
+ @store.connect 0,self,2
+ @fin = GridFinished.new
+ @fin.connect 0,self,3
+ @bg,@bgs,@fg = "#609068","#0080ff","#ff80ff"
+ @bghi = highlight(@bg,1.25) # "#80C891" # highlighted @bg
+ #@bghihi = highlight(@bghi,1.5) # very highlighted @bg
+ @cellsy,@cellsx = 16,48
+ @i,@j = nil,nil # highlighted cell dex
+ send_in 0, grid
+ end
+ def _0_cell_size(sy,sx) @cellsy,@cellsx=sy,sx; update end
+ def _0_float(*a) @store.send_in 1,:float,*a; @store.send_in 0; update end
+ def _0_list (*a) @store.send_in 1, :list,*a; @store.send_in 0; update end
+ def _0_grid (*a) @store.send_in 1, :grid,*a; @fin.send_in 0, :grid,*a end
+ def _3_bang; @store.send_in 0; update end
+ def edit_start(i,j)
+ edit_end if @i
+ @i,@j=i,j
+ GridFlow.gui %{
+ set canvas #{canvas}
+ $canvas itemconfigure #{@rsym}CELL_#{@i}_#{@j} -fill #{@bghi}
+ }
+ end
+ def edit_end
+ GridFlow.gui %{
+ set canvas #{canvas}
+ $canvas itemconfigure #{@rsym}CELL_#{@i}_#{@j} -fill #{@bg}
+ }
+ unfocus @can
+ end
+ def _2_rgrid_begin
+ @data = []
+ @dim = inlet_dim 2
+ @nt = inlet_nt 2
+ post "_2_rgrid_begin: dim=#{@dim.inspect} nt=#{@nt.inspect}"
+ send_out_grid_begin 0, @dim, @nt
+ end
+ def _2_rgrid_flow data
+ ps = GridFlow.packstring_for_nt @nt
+ @data[@data.length,0] = data.unpack(ps)
+ post "_2_rgrid_flow: data=#{@data.inspect}"
+ send_out_grid_flow 0, data
+ end
+ def _2_rgrid_end
+ post "_2_rgrid_end"
+ end
+ def pd_click(can,x,y,shift,alt,dbl,doit)
+ post "pd_click: %s", [can,x,y,shift,alt,dbl,doit].inspect
+ return 0 if not doit!=0
+ i = (y-@y-1)/@cellsy
+ j = (x-@x-1)/@cellsx
+ post "%d,%d", i,j
+ ny = @dim[0] || 1
+ nx = @dim[1] || 1
+ if (0...ny)===i and (0...nx)===j then
+ focus @can,x,y
+ edit_start i,j
+ end
+ return 0
+ end
+ def pd_key(key)
+ post "pd_key: %s", [key].inspect
+ if key==0 then unfocus @can; return end
+ end
+ def pd_motion(dx,dy)
+ post "pd_motion: %s", [dx,dy].inspect
+ ny = @dim[0] || 1
+ nx = @dim[1] || 1
+ k = @i*nx+@j
+ post "@data[#{k}]=#{@data[k]} before"
+ @data[k]-=dy
+ @store.send_in 1, :put_at, [@i,@j]
+ @store.send_in 1, @data[k]
+ @store.send_in 0
+ post "@data[#{k}]=#{@data[k]} after"
+ update
+ end
+ def pd_show(can)
+ super
+ return if not can
+ ny = @dim[0] || 1
+ nx = @dim[1] || 1
+ @sy = 2+@cellsy*ny
+ @sx = 2+@cellsx*nx
+ g = %{
+ set canvas #{canvas}
+ $canvas delete #{@rsym} #{@rsym}CELL
+ $canvas create rectangle #{@x} #{@y} #{@x+@sx} #{@y+@sy} \
+ -fill #{@bg} -tags #{@rsym} -outline #{outline}
+ }
+ ny.times {|i|
+ nx.times {|j|
+ y1 = @y+1+i*@cellsy; y2 = y1+@cellsy
+ x1 = @x+1+j*@cellsx; x2 = x1+@cellsx
+ v = @data[i*nx+j]
+ g << %{
+ $canvas create rectangle #{x1} #{y1} #{x2} #{y2} -fill #{@bg} \
+ -tags {#{@rsym}CELL #{@rsym}CELL_#{i}_#{j}} -outline #{outline}
+ $canvas create text #{x2-4} #{y1+2} -text "#{v}" -anchor ne -fill #ffffff \
+ -tags {#{@rsym}CELL_#{i}_#{j}_T}
+ }
+ }
+ }
+ GridFlow.gui g
+ end
+ install "#edit", 2, 1
+ install_rgrid 2, true
+ gui_enable if GridFlow.bridge_name =~ /puredata/
+end
+
+class Peephole < FPatcher; include Gooey
+ @fobjects = ["#dim","#export_list","#downscale_by 1 smoothly","#out","#scale_by 1",
+ proc{Demux.new(2)}]
+ @wires = [-1,0,0,0, 0,0,1,0, -1,0,5,0, 2,0,3,0, 4,0,3,0, 5,0,2,0, 5,1,4,0, 3,0,-1,0]
+ def initialize(sy=32,sx=32,*args)
+ super
+ @fobjects[1].connect 0,self,2
+ post "Peephole#initialize: #{sx} #{sy} #{args.inspect}"
+ @scale = 1
+ @down = false
+ @sy,@sx = sy,sx # size of the widget
+ @fy,@fx = 0,0 # size of last frame after downscale
+ @bg,@bgs = "#A07467","#00ff80"
+ end
+ def pd_show(can)
+ super
+ return if not can
+ if not @open
+ GridFlow.gui %{
+ pd \"#{@rsym} open [eval list [winfo id #{@canvas}]] 1;\n\";
+ }
+ @open=true
+ end
+ # round-trip to ensure this is done after the open
+ GridFlow.gui %{
+ pd \"#{@rsym} set_geometry #{@y} #{@x} #{@sy} #{@sx};\n\";
+ }
+ GridFlow.gui %{
+ set canvas #{canvas}
+ $canvas delete #{@rsym}
+ $canvas create rectangle #{@x} #{@y} #{@x+@sx} #{@y+@sy} \
+ -fill #{@bg} -tags #{@rsym} -outline #{outline}
+ }
+ set_geometry_for_real_now
+ end
+ def set_geometry_for_real_now
+ @fy,@fx=@sy,@sx if @fy<1 or @fx<1
+ @down = (@fx>@sx or @fy>@sx)
+ if @down then
+ @scale = [(@fy+@sy-1)/@sy,(@fx+@sx-1)/@sx].max
+ @scale=1 if @scale<1 # what???
+ @fobjects[2].send_in 1, @scale
+ sy2 = @fy/@scale
+ sx2 = @fx/@scale
+ else
+ @scale = [@sy/@fy,@sx/@fx].min
+ @fobjects[4].send_in 1, @scale
+ sy2 = @fy*@scale
+ sx2 = @fx*@scale
+ end
+ begin
+ @fobjects[5].send_in 1, (if @down then 0 else 1 end)
+ x2=@y+(@sy-sy2)/2
+ y2=@x+(@sx-sx2)/2
+ @fobjects[3].send_in 0, :set_geometry,
+ x2, y2, sy2, sx2
+ rescue StandardError => e
+ post "peeperr: %s", e.inspect
+ end
+ post "set_geometry_for_real_now (%d,%d) (%d,%d) (%d,%d) (%d,%d) (%d,%d)",
+ @x+1,@y+1,@sx,@sy,@fx,@fy,sx2,sy2,x2,y2
+ end
+ def _0_open(wid,use_subwindow)
+ post "%s", [wid,use_subwindow].inspect
+ @use_subwindow = use_subwindow==0 ? false : true
+ if @use_subwindow then
+ @fobjects[3].send_in 0, :open,:x11,:here,:embed_by_id,wid
+ end
+ end
+ def _0_set_geometry(y,x,sy,sx)
+ @sy,@sx = sy,sx
+ @y,@x = y,x
+ set_geometry_for_real_now
+ end
+ def _0_fall_thru(flag) # never worked ?
+ post "fall_thru: #{flag}"
+ @fobjects[3].send_in 0, :fall_thru, flag
+ end
+ # note: the numbering here is a FPatcher gimmick... -1,0 goes to _1_.
+ def _1_position(y,x,b)
+ s=@scale
+ if @down then y*=s;x*=s else y*=s;x*=s end
+ send_out 0,:position,y,x,b
+ end
+ def _2_list(sy,sx,chans)
+ @fy,@fx = sy,sx
+ set_geometry_for_real_now
+ end
+ def _0_paint()
+ post "paint()"
+ @fobjects[3].send_in 0, "draw"
+ end
+ def delete
+ post "deleting peephole"
+ GridFlow.gui %{ #{canvas} delete #{@rsym} \n}
+ @fobjects[3].send_in 0, :close
+ super
+ end
+ def method_missing(s,*a)
+ #post "%s: %s", s.to_s, a.inspect
+ super rescue NameError
+ end
+
+ install "#peephole", 1, 1
+ gui_enable if GridFlow.bridge_name =~ /puredata/
+ #GridFlow.addtomenu "#peephole" # was this IMPD-specific ?
+end
+
+#-------- fClasses for: Hardware
+
+if const_defined? :USB
+
+class<<USB
+ attr_reader :busses
+end
+
+class DelcomUSB < GridFlow::FObject
+ Vendor,Product=0x0FC5,0x1222
+ def self.find
+ r=[]
+ USB.busses.each {|dir,bus|
+ bus.each {|dev|
+ r<<dev if dev.idVendor==Vendor and dev.idProduct==Product
+ }
+ }
+ r
+ end
+ def initialize #(bus=nil,dev=nil)
+ r=DelcomUSB.find
+ raise "no such device" if r.length<1
+ raise "#{r.length} such devices (which one???)" if r.length>1
+ @usb=USB.new(r[0])
+ if_num=nil
+ r[0].config.each {|config|
+ config.interface.each {|interface|
+ if_num = interface.bInterfaceNumber
+ }
+ }
+ # post "Interface # %i\n", if_num
+ @usb.set_configuration 1
+ @usb.claim_interface if_num
+ @usb.set_altinterface 0 rescue ArgumentError
+ end
+ # libdelcom had this:
+ # uint8 recipient, deviceModel, major, minor, dataL, dataM;
+ # uint16 length; uint8[8] extension;
+ def _0_send_command(major,minor,dataL,dataM,extension="\0\0\0\0\0\0\0\0")
+ raise "closed" if not @usb
+ raise "extension.length!=8" if extension.length!=8
+ @usb.control_msg(
+ 0x000000c8, 0x00000012,
+ minor*0x100+major,
+ dataM*0x100+dataL,
+ extension, 5000)
+ end
+ def delete; @usb.close; end
+ install "delcomusb", 1, 1
+end
+
+# Klippeltronics
+FObject.subclass("multio",1,1) {
+ Vendor,Product=0xDEAD,0xBEEF
+ def self.find
+ r=[]
+ USB.busses.each {|dir,bus|
+ bus.each {|dev|
+ post "dir=%s, vendor=%x, product=%x",
+ dir, dev.idVendor, dev.idProduct
+ r<<dev if dev.idVendor==Vendor and dev.idProduct==Product
+ }
+ }
+ r
+ end
+ def initialize
+ r=self.class.find
+ raise "no such device" if r.length<1
+ raise "#{r.length} such devices (which one???)" if r.length>1
+ $iobox=@usb=USB.new(r[0])
+ if_num=nil
+ r[0].config.each {|config|
+ config.interface.each {|interface|
+ #post "interface=%s", interface.to_s
+ if_num = interface.bInterfaceNumber
+ }
+ }
+ # post "Interface # %i\n", if_num
+ # @usb.set_configuration 0
+ @usb.claim_interface if_num
+ @usb.set_altinterface 0 rescue ArgumentError
+ end
+ #@usb.control_msg(0b10100001,0x01,0,0,"",1000)
+ #@usb.control_msg(0b10100001,0x01,0,1," ",0)
+ def delete; @usb.close; end
+}
+end # if const_defined? :USB
+
+# requires Ruby 1.8.0 because of bug in Ruby 1.6.x
+FObject.subclass("joystick_port",0,1) {
+ def initialize(port)
+ raise "sorry, requires Ruby 1.8" if RUBY_VERSION<"1.8"
+ @f = File.open(port.to_s,"r+")
+ @status = nil
+ @clock = Clock.new self
+ @clock.delay 0
+ @f.nonblock=true
+ end
+ def delete; @clock.unset; @f.close end
+ def call
+ loop{
+ begin
+ event = @f.read(8)
+ rescue Errno::EAGAIN
+ @clock.delay 0
+ return
+ end
+ return if not event
+ return if event.length<8
+ send_out 0, *event.unpack("IsCC")
+ }
+ end
+}
+
+# plotter control (HPGL)
+FObject.subclass("plotter_control",1,1) {
+ def puts(x)
+ x<<"\n"
+ x.each_byte {|b| send_out 0, b }
+ send_out 0
+ end
+ def _0_pu; puts "PU;" end
+ def _0_pd; puts "PD;" end
+ def _0_pa x,y; puts "PA#{x},#{y};" end
+ def _0_sp c; puts "SP#{c};"; end
+ def _0_ip(*v) puts "IP#{v.join','};" end
+ def _0_other(command,*v) puts "#{command.to_s.upcase}#{v.join','};" end
+ def _0_print(*text) puts "LB#{text.join(' ')}\003;" end
+ def _0_print_from_ascii(*codes)
+ _0_print codes.map{|code| code.chr }.join("")
+ end
+}
+
+(begin require "linux/ParallelPort"; true; rescue LoadError; false end) and
+FObject.subclass("parallel_port",1,3) {
+ def initialize(port,manually=0)
+ @f = File.open(port.to_s,"r+")
+ @f.extend Linux::ParallelPort
+ @status = nil
+ @flags = nil
+ @manually = manually!=0
+ @clock = (if @manually then nil else Clock.new self end)
+ @clock.delay 0 if @clock
+ end
+ def delete; @clock.unset unless @manually; @f.close end
+ def _0_int(x) @f.write x.to_i.chr; @f.flush end
+ alias _0_float _0_int
+ def call
+ flags = @f.port_flags
+ send_out 2, flags if @flags != flags
+ @flags = flags
+ status = @f.port_status
+ send_out 1, status if @status != status
+ @status = status
+ @clock.delay 20 if @clock
+ end
+ def _0_bang
+ @status = @flags = nil
+ call
+ end
+ # outlet 0 reserved (future use)
+}
+
+(begin require "linux/SoundMixer"; true; rescue LoadError; false end) and
+FObject.subclass("SoundMixer",1,1) {
+ # BUG? i may have the channels (left,right) backwards
+ def initialize(filename)
+ super
+ @file = File.open filename.to_s, 0
+ @file.extend Linux::SoundMixer
+ $sm = self
+ end
+ @@vars = Linux::SoundMixer.instance_methods.grep(/=/)
+ @@vars_h = {}
+ @@vars.each {|attr|
+ attr.chop!
+ eval %{ def _0_#{attr}(x) @file.#{attr} = x[0]*256+x[1] end }
+ @@vars_h[attr]=true
+ }
+ def _0_get(sel=nil)
+ if sel then
+ sels=sel.to_s
+ sel=sels.intern
+ raise if not @@vars_h.include? sel.to_s
+ begin
+ x = @file.send sel
+ send_out 0, sel, "(".intern, (x>>8)&255, x&255, ")".intern
+ rescue
+ send_out 0, sel, "(".intern, -1, -1, ")".intern
+ end
+ else
+ @@vars.each {|var| _0_get var }
+ end
+ end
+}
+
+# experimental
+FObject.subclass("rubyarray",2,1) {
+ def initialize() @a=[]; @i=0; end
+ def _0_float i; @i=i; send_out 0, *@a[@i]; end
+ def _1_list(*l) @a[@i]=l; end
+ def _0_save(filename,format=nil)
+ f=File.open(filename.to_s,"w")
+ if format then
+ @a.each {|x| f.puts(format.to_s%x) }
+ else
+ @a.each {|x| f.puts(x.join(",")) }
+ end
+ f.close
+ end
+ def _0_load(filename)
+ f=File.open(filename.to_s,"r")
+ @a.clear
+ f.each {|x| @a.push x.split(",").map {|y| Float(y) rescue y.intern }}
+ f.close
+ end
+}
+
+end # module GridFlow
diff --git a/externals/gridflow/base/flow_objects_for_image.c b/externals/gridflow/base/flow_objects_for_image.c
new file mode 100644
index 00000000..2041c3e1
--- /dev/null
+++ b/externals/gridflow/base/flow_objects_for_image.c
@@ -0,0 +1,618 @@
+/*
+ $Id: flow_objects_for_image.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <math.h>
+#include "grid.h.fcs"
+
+static void expect_picture (P<Dim> d) {
+ if (d->n!=3) RAISE("(height,width,chans) dimensions please");}
+static void expect_rgb_picture (P<Dim> d) {
+ expect_picture(d);
+ if (d->get(2)!=3) RAISE("(red,green,blue) channels please");}
+static void expect_rgba_picture (P<Dim> d) {
+ expect_picture(d);
+ if (d->get(2)!=4) RAISE("(red,green,blue,alpha) channels please");}
+static void expect_max_one_dim (P<Dim> d) {
+ if (d->n>1) { RAISE("expecting Dim[] or Dim[n], got %s",d->to_s()); }}
+
+//****************************************************************
+//{ Dim[A,B,*Cs]<T>,Dim[D,E]<T> -> Dim[A,B,*Cs]<T> }
+
+static void expect_convolution_matrix (P<Dim> d) {
+ if (d->n != 2) RAISE("only exactly two dimensions allowed for now (got %d)",
+ d->n);
+}
+
+// entry in a compiled convolution kernel
+struct PlanEntry { int y,x; bool neutral; };
+
+\class GridConvolve < GridObject
+struct GridConvolve : GridObject {
+ \attr Numop *op_para;
+ \attr Numop *op_fold;
+ \attr PtrGrid seed;
+ \attr PtrGrid b;
+ PtrGrid a;
+ int plann;
+ PlanEntry *plan; //Pt?
+ int margx,margy; // margins
+ GridConvolve () : plan(0) { b.constrain(expect_convolution_matrix); plan=0; }
+ \decl void initialize (Grid *r=0);
+ \decl void _0_op (Numop *op);
+ \decl void _0_fold (Numop *op);
+ \decl void _0_seed (Grid *seed);
+ \grin 0
+ \grin 1
+ template <class T> void copy_row (Pt<T> buf, int sx, int y, int x);
+ template <class T> void make_plan (T bogus);
+ ~GridConvolve () {if (plan) delete[] plan;}
+};
+
+template <class T> void GridConvolve::copy_row (Pt<T> buf, int sx, int y, int x) {
+ int day = a->dim->get(0), dax = a->dim->get(1), dac = a->dim->prod(2);
+ y=mod(y,day); x=mod(x,dax);
+ Pt<T> ap = (Pt<T>)*a + y*dax*dac;
+ while (sx) {
+ int sx1 = min(sx,dax-x);
+ COPY(buf,ap+x*dac,sx1*dac);
+ x=0;
+ buf += sx1*dac;
+ sx -= sx1;
+ }
+}
+
+static Numop *OP(Ruby x) {return FIX2PTR(Numop,rb_hash_aref(op_dict,x));}
+
+template <class T> void GridConvolve::make_plan (T bogus) {
+ P<Dim> da = a->dim, db = b->dim;
+ int dby = db->get(0);
+ int dbx = db->get(1);
+ if (plan) delete[] plan;
+ plan = new PlanEntry[dbx*dby];
+ int i=0;
+ for (int y=0; y<dby; y++) {
+ for (int x=0; x<dbx; x++) {
+ T rh = ((Pt<T>)*b)[y*dbx+x];
+ bool neutral = op_para->on(rh)->is_neutral(rh,at_right);
+ bool absorbent = op_para->on(rh)->is_absorbent(rh,at_right);
+ STACK_ARRAY(T,foo,1);
+ if (absorbent) {
+ foo[0] = 0;
+ op_para->map(1,foo,rh);
+ absorbent = op_fold->on(rh)->is_neutral(foo[0],at_right);
+ }
+ if (absorbent) continue;
+ plan[i].y = y;
+ plan[i].x = x;
+ plan[i].neutral = neutral;
+ i++;
+ }
+ }
+ plann = i;
+}
+
+GRID_INLET(GridConvolve,0) {
+ SAME_TYPE(in,b);
+ SAME_TYPE(in,seed);
+ P<Dim> da = in->dim, db = b->dim;
+ if (!db) RAISE("right inlet has no grid");
+ if (!seed) RAISE("seed missing");
+ if (db->n != 2) RAISE("right grid must have two dimensions");
+ if (da->n < 2) RAISE("left grid has less than two dimensions");
+ if (seed->dim->n != 0) RAISE("seed must be scalar");
+ if (da->get(0) < db->get(0)) RAISE("grid too small (y): %d < %d", da->get(0), db->get(0));
+ if (da->get(1) < db->get(1)) RAISE("grid too small (x): %d < %d", da->get(1), db->get(1));
+ margy = (db->get(0)-1)/2;
+ margx = (db->get(1)-1)/2;
+ a=new Grid(in->dim,in->nt);
+ out=new GridOutlet(this,0,da,in->nt);
+} GRID_FLOW {
+ COPY((Pt<T>)*a+in->dex, data, n);
+} GRID_FINISH {
+ Numop *op_put = OP(SYM(put));
+ make_plan((T)0);
+ int dbx = b->dim->get(1);
+ int day = a->dim->get(0);
+ int n = a->dim->prod(1);
+ int sx = a->dim->get(1)+dbx-1;
+ int n2 = sx*a->dim->prod(2);
+ STACK_ARRAY(T,buf,n);
+ STACK_ARRAY(T,buf2,n2);
+ T orh=0;
+ for (int iy=0; iy<day; iy++) {
+ op_put->map(n,buf,*(T *)*seed);
+ for (int i=0; i<plann; i++) {
+ int jy = plan[i].y;
+ int jx = plan[i].x;
+ T rh = ((Pt<T>)*b)[jy*dbx+jx];
+ if (i==0 || plan[i].y!=plan[i-1].y || orh!=rh) {
+ copy_row(buf2,sx,iy+jy-margy,-margx);
+ if (!plan[i].neutral) op_para->map(n2,buf2,rh);
+ }
+ op_fold->zip(n,buf,buf2+jx*a->dim->prod(2));
+ orh=rh;
+ }
+ out->send(n,buf);
+ }
+ a=0;
+} GRID_END
+
+GRID_INPUT(GridConvolve,1,b) {} GRID_END
+
+\def void _0_op (Numop *op ) { this->op_para=op; }
+\def void _0_fold (Numop *op ) { this->op_fold=op; }
+\def void _0_seed (Grid *seed) { this->seed=seed; }
+
+\def void initialize (Grid *r) {
+ rb_call_super(argc,argv);
+ this->op_para = op_mul;
+ this->op_fold = op_add;
+ this->seed = new Grid(new Dim(),int32_e,true);
+ this->b= r ? r : new Grid(new Dim(1,1),int32_e,true);
+}
+
+\classinfo { IEVAL(rself,"install '#convolve',2,1"); }
+\end class GridConvolve
+
+/* ---------------------------------------------------------------- */
+/* "#scale_by" does quick scaling of pictures by integer factors */
+/*{ Dim[A,B,3]<T> -> Dim[C,D,3]<T> }*/
+\class GridScaleBy < GridObject
+struct GridScaleBy : GridObject {
+ \attr PtrGrid scale; // integer scale factor
+ int scaley;
+ int scalex;
+ \decl void initialize (Grid *factor=0);
+ \grin 0
+ \grin 1
+ void prepare_scale_factor () {
+ scaley = ((Pt<int32>)*scale)[0];
+ scalex = ((Pt<int32>)*scale)[scale->dim->prod()==1 ? 0 : 1];
+ if (scaley<1) scaley=2;
+ if (scalex<1) scalex=2;
+ }
+};
+
+GRID_INLET(GridScaleBy,0) {
+ P<Dim> a = in->dim;
+ expect_picture(a);
+ out=new GridOutlet(this,0,new Dim(a->get(0)*scaley,a->get(1)*scalex,a->get(2)),in->nt);
+ in->set_factor(a->get(1)*a->get(2));
+} GRID_FLOW {
+ int rowsize = in->dim->prod(1);
+ STACK_ARRAY(T,buf,rowsize*scalex);
+ int chans = in->dim->get(2);
+ #define Z(z) buf[p+z]=data[i+z]
+ for (; n>0; data+=rowsize, n-=rowsize) {
+ int p=0;
+ #define LOOP(z) \
+ for (int i=0; i<rowsize; i+=z) \
+ for (int k=0; k<scalex; k++, p+=3)
+ switch (chans) {
+ case 3: LOOP(3) {Z(0);Z(1);Z(2);} break;
+ case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break;
+ default: LOOP(chans) {for (int c=0; c<chans; c++) Z(c);}
+ }
+ #undef LOOP
+ for (int j=0; j<scaley; j++) out->send(rowsize*scalex,buf);
+ }
+ #undef Z
+} GRID_END
+
+static void expect_scale_factor (P<Dim> dim) {
+ if (dim->prod()!=1 && dim->prod()!=2)
+ RAISE("expecting only one or two numbers");
+}
+
+GRID_INPUT(GridScaleBy,1,scale) { prepare_scale_factor(); } GRID_END
+
+\def void initialize (Grid *factor) {
+ scale.constrain(expect_scale_factor);
+ rb_call_super(argc,argv);
+ scale=new Grid(INT2NUM(2));
+ if (factor) scale=factor;
+ prepare_scale_factor();
+}
+
+\classinfo { IEVAL(rself,"install '#scale_by',2,1"); }
+\end class GridScaleBy
+
+// ----------------------------------------------------------------
+//{ Dim[A,B,3]<T> -> Dim[C,D,3]<T> }
+\class GridDownscaleBy < GridObject
+struct GridDownscaleBy : GridObject {
+ \attr PtrGrid scale;
+ \attr bool smoothly;
+ int scaley;
+ int scalex;
+ PtrGrid temp;
+ \decl void initialize (Grid *factor=0, Symbol option=Qnil);
+ \grin 0
+ \grin 1
+ void prepare_scale_factor () {
+ scaley = ((Pt<int32>)*scale)[0];
+ scalex = ((Pt<int32>)*scale)[scale->dim->prod()==1 ? 0 : 1];
+ if (scaley<1) scaley=2;
+ if (scalex<1) scalex=2;
+ }
+};
+
+GRID_INLET(GridDownscaleBy,0) {
+
+ P<Dim> a = in->dim;
+ if (a->n!=3) RAISE("(height,width,chans) please");
+ out=new GridOutlet(this,0,new Dim(a->get(0)/scaley,a->get(1)/scalex,a->get(2)),in->nt);
+ in->set_factor(a->get(1)*a->get(2));
+ // i don't remember why two rows instead of just one.
+ temp=new Grid(new Dim(2,in->dim->get(1)/scalex,in->dim->get(2)),in->nt);
+} GRID_FLOW {
+ int rowsize = in->dim->prod(1);
+ int rowsize2 = temp->dim->prod(1);
+ Pt<T> buf = (Pt<T>)*temp; //!@#$ maybe should be something else than T ?
+ int xinc = in->dim->get(2)*scalex;
+ int y = in->dex / rowsize;
+ int chans=in->dim->get(2);
+ #define Z(z) buf[p+z]+=data[i+z]
+ if (smoothly) {
+ while (n>0) {
+ if (y%scaley==0) CLEAR(buf,rowsize2);
+ #define LOOP(z) \
+ for (int i=0,p=0; p<rowsize2; p+=z) \
+ for (int j=0; j<scalex; j++,i+=z)
+ switch (chans) {
+ case 1: LOOP(1) {Z(0);} break;
+ case 2: LOOP(2) {Z(0);Z(1);} break;
+ case 3: LOOP(3) {Z(0);Z(1);Z(2);} break;
+ case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break;
+ default:LOOP(chans) {for (int k=0; k<chans; k++) Z(k);} break;
+ }
+ #undef LOOP
+ y++;
+ if (y%scaley==0 && out->dim) {
+ op_div->map(rowsize2,buf,(T)(scalex*scaley));
+ out->send(rowsize2,buf);
+ CLEAR(buf,rowsize2);
+ }
+ data+=rowsize;
+ n-=rowsize;
+ }
+ #undef Z
+ } else {
+ #define Z(z) buf[p+z]=data[i+z]
+ for (; n>0 && out->dim; data+=rowsize, n-=rowsize,y++) {
+ if (y%scaley!=0) continue;
+ #define LOOP(z) for (int i=0,p=0; p<rowsize2; i+=xinc, p+=z)
+ switch(in->dim->get(2)) {
+ case 1: LOOP(1) {Z(0);} break;
+ case 2: LOOP(2) {Z(0);Z(1);} break;
+ case 3: LOOP(3) {Z(0);Z(1);Z(2);} break;
+ case 4: LOOP(4) {Z(0);Z(1);Z(2);Z(3);} break;
+ default:LOOP(chans) {for (int k=0; k<chans; k++) Z(k);}break;
+ }
+ #undef LOOP
+ out->send(rowsize2,buf);
+ }
+ }
+ #undef Z
+} GRID_END
+
+GRID_INPUT(GridDownscaleBy,1,scale) { prepare_scale_factor(); } GRID_END
+
+\def void initialize (Grid *factor, Symbol option) {
+ scale.constrain(expect_scale_factor);
+ rb_call_super(argc,argv);
+ scale=new Grid(INT2NUM(2));
+ if (factor) scale=factor;
+ prepare_scale_factor();
+ smoothly = option==SYM(smoothly);
+}
+
+\classinfo { IEVAL(rself,"install '#downscale_by',2,1"); }
+\end class GridDownscaleBy
+
+//****************************************************************
+\class GridLayer < GridObject
+struct GridLayer : GridObject {
+ PtrGrid r;
+ GridLayer() { r.constrain(expect_rgb_picture); }
+ \grin 0 int
+ \grin 1 int
+};
+
+GRID_INLET(GridLayer,0) {
+ NOTEMPTY(r);
+ SAME_TYPE(in,r);
+ P<Dim> a = in->dim;
+ expect_rgba_picture(a);
+ if (a->get(1)!=r->dim->get(1)) RAISE("same width please");
+ if (a->get(0)!=r->dim->get(0)) RAISE("same height please");
+ in->set_factor(a->prod(2));
+ out=new GridOutlet(this,0,r->dim);
+} GRID_FLOW {
+ Pt<T> rr = ((Pt<T>)*r) + in->dex*3/4;
+ STACK_ARRAY(T,foo,n*3/4);
+#define COMPUTE_ALPHA(c,a) \
+ foo[j+c] = (data[i+c]*data[i+a] + rr[j+c]*(256-data[i+a])) >> 8
+ for (int i=0,j=0; i<n; i+=4,j+=3) {
+ COMPUTE_ALPHA(0,3);
+ COMPUTE_ALPHA(1,3);
+ COMPUTE_ALPHA(2,3);
+ }
+#undef COMPUTE_ALPHA
+ out->send(n*3/4,foo);
+} GRID_END
+
+GRID_INPUT(GridLayer,1,r) {} GRID_END
+
+\classinfo { IEVAL(rself,"install '#layer',2,1"); }
+\end class GridLayer
+
+// ****************************************************************
+// pad1,pad2 only are there for 32-byte alignment
+struct Line { int32 y1,x1,y2,x2,x,m,pad1,pad2; };
+
+static void expect_polygon (P<Dim> d) {
+ if (d->n!=2 || d->get(1)!=2) RAISE("expecting Dim[n,2] polygon");
+}
+
+\class DrawPolygon < GridObject
+struct DrawPolygon : GridObject {
+ \attr Numop *op;
+ \attr PtrGrid color;
+ \attr PtrGrid polygon;
+ PtrGrid color2;
+ PtrGrid lines;
+ int lines_start;
+ int lines_stop;
+ DrawPolygon() {
+ color.constrain(expect_max_one_dim);
+ polygon.constrain(expect_polygon);
+ }
+ \decl void initialize (Numop *op, Grid *color=0, Grid *polygon=0);
+ \grin 0
+ \grin 1
+ \grin 2 int32
+ void init_lines();
+
+};
+
+void DrawPolygon::init_lines () {
+ int nl = polygon->dim->get(0);
+ lines=new Grid(new Dim(nl,8), int32_e);
+ Pt<Line> ld = Pt<Line>((Line *)(int32 *)*lines,nl);
+ Pt<int32> pd = *polygon;
+ for (int i=0,j=0; i<nl; i++) {
+ ld[i].y1 = pd[j+0];
+ ld[i].x1 = pd[j+1];
+ j=(j+2)%(2*nl);
+ ld[i].y2 = pd[j+0];
+ ld[i].x2 = pd[j+1];
+ if (ld[i].y1>ld[i].y2) memswap(Pt<int32>(ld+i)+0,Pt<int32>(ld+i)+2,2);
+ }
+}
+
+static int order_by_starting_scanline (const void *a, const void *b) {
+ return ((Line *)a)->y1 - ((Line *)b)->y1;
+}
+
+static int order_by_column (const void *a, const void *b) {
+ return ((Line *)a)->x - ((Line *)b)->x;
+}
+
+GRID_INLET(DrawPolygon,0) {
+ NOTEMPTY(color);
+ NOTEMPTY(polygon);
+ NOTEMPTY(lines);
+ SAME_TYPE(in,color);
+ if (in->dim->n!=3) RAISE("expecting 3 dimensions");
+ if (in->dim->get(2)!=color->dim->get(0))
+ RAISE("image does not have same number of channels as stored color");
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ lines_start = lines_stop = 0;
+ in->set_factor(in->dim->get(1)*in->dim->get(2));
+ int nl = polygon->dim->get(0);
+ qsort((int32 *)*lines,nl,sizeof(Line),order_by_starting_scanline);
+ int cn = color->dim->prod();
+ color2=new Grid(new Dim(cn*16), color->nt);
+ for (int i=0; i<16; i++) COPY((Pt<T>)*color2+cn*i,(Pt<T>)*color,cn);
+} GRID_FLOW {
+ int nl = polygon->dim->get(0);
+ Pt<Line> ld = Pt<Line>((Line *)(int32 *)*lines,nl);
+ int f = in->factor();
+ int y = in->dex/f;
+ int cn = color->dim->prod();
+ Pt<T> cd = (Pt<T>)*color2;
+
+ while (n) {
+ while (lines_stop != nl && ld[lines_stop].y1<=y) lines_stop++;
+ for (int i=lines_start; i<lines_stop; i++) {
+ if (ld[i].y2<=y) {
+ memswap(ld+i,ld+lines_start,1);
+ lines_start++;
+ }
+ }
+ if (lines_start == lines_stop) {
+ out->send(f,data);
+ } else {
+ int32 xl = in->dim->get(1);
+ Pt<T> data2 = ARRAY_NEW(T,f);
+ COPY(data2,data,f);
+ for (int i=lines_start; i<lines_stop; i++) {
+ Line &l = ld[i];
+ l.x = l.x1 + (y-l.y1)*(l.x2-l.x1+1)/(l.y2-l.y1+1);
+ }
+ qsort(ld+lines_start,lines_stop-lines_start,
+ sizeof(Line),order_by_column);
+ for (int i=lines_start; i<lines_stop-1; i+=2) {
+ int xs = max(ld[i].x,(int32)0), xe = min(ld[i+1].x,xl);
+ if (xs>=xe) continue; /* !@#$ WHAT? */
+ while (xe-xs>=16) { op->zip(16*cn,data2+cn*xs,cd); xs+=16; }
+ op->zip((xe-xs)*cn,data2+cn*xs,cd);
+ }
+ out->give(f,data2);
+ }
+ n-=f;
+ data+=f;
+ y++;
+ }
+} GRID_END
+
+GRID_INPUT(DrawPolygon,1,color) {} GRID_END
+GRID_INPUT(DrawPolygon,2,polygon) {init_lines();} GRID_END
+
+\def void initialize (Numop *op, Grid *color, Grid *polygon) {
+ rb_call_super(argc,argv);
+ this->op = op;
+ if (color) this->color=color;
+ if (polygon) { this->polygon=polygon; init_lines(); }
+}
+
+\classinfo { IEVAL(rself,"install '#draw_polygon',3,1"); }
+\end class DrawPolygon
+
+//****************************************************************
+static void expect_position(P<Dim> d) {
+ if (d->n!=1) RAISE("position should have 1 dimension, not %d", d->n);
+ if (d->v[0]!=2) RAISE("position dim 0 should have 2 elements, not %d", d->v[0]);
+}
+
+\class DrawImage < GridObject
+struct DrawImage : GridObject {
+ \attr Numop *op;
+ \attr PtrGrid image;
+ \attr PtrGrid position;
+ \attr bool alpha;
+ \attr bool tile;
+
+ DrawImage() : alpha(false), tile(false) {
+ position.constrain(expect_position);
+ image.constrain(expect_picture);
+ }
+
+ \decl void initialize (Numop *op, Grid *image=0, Grid *position=0);
+ \decl void _0_alpha (bool v=true);
+ \decl void _0_tile (bool v=true);
+ \grin 0
+ \grin 1
+ \grin 2 int32
+ // draw row # ry of right image in row buffer buf, starting at xs
+ // overflow on both sides has to be handled automatically by this method
+ template <class T> void draw_segment(Pt<T> obuf, Pt<T> ibuf, int ry, int x0);
+};
+
+#define COMPUTE_ALPHA(c,a) obuf[j+(c)] = ibuf[j+(c)] + (rbuf[a])*(obuf[j+(c)]-ibuf[j+(c)])/256;
+#define COMPUTE_ALPHA4(b) \
+ COMPUTE_ALPHA(b+0,b+3); \
+ COMPUTE_ALPHA(b+1,b+3); \
+ COMPUTE_ALPHA(b+2,b+3); \
+ obuf[b+3] = rbuf[b+3] + (255-rbuf[b+3])*(ibuf[j+b+3])/256;
+
+template <class T> void DrawImage::draw_segment(Pt<T> obuf, Pt<T> ibuf, int ry, int x0) {
+ if (ry<0 || ry>=image->dim->get(0)) return; // outside of image
+ int sx = in[0]->dim->get(1), rsx = image->dim->get(1);
+ int sc = in[0]->dim->get(2), rsc = image->dim->get(2);
+ Pt<T> rbuf = (Pt<T>)*image + ry*rsx*rsc;
+ if (x0>sx || x0<=-rsx) return; // outside of buffer
+ int n=rsx;
+ if (x0+n>sx) n=sx-x0;
+ if (x0<0) { rbuf-=rsc*x0; n+=x0; x0=0; }
+ if (alpha && rsc==4 && sc==3) { // RGB by RGBA //!@#$ optimise
+ int j=sc*x0;
+ for (; n; n--, rbuf+=4, j+=3) {
+ op->zip(sc,obuf+j,rbuf); COMPUTE_ALPHA(0,3); COMPUTE_ALPHA(1,3); COMPUTE_ALPHA(2,3);
+ }
+ } else if (alpha && rsc==4 && sc==4) { // RGBA by RGBA
+ op->zip(n*rsc,obuf+x0*rsc,rbuf);
+ int j=sc*x0;
+ for (; n>=4; n-=4, rbuf+=16, j+=16) {
+ COMPUTE_ALPHA4(0);COMPUTE_ALPHA4(4);
+ COMPUTE_ALPHA4(8);COMPUTE_ALPHA4(12);
+ }
+ for (; n; n--, rbuf+=4, j+=4) {
+ COMPUTE_ALPHA4(0);
+ }
+ } else { // RGB by RGB, etc
+ op->zip(n*rsc,obuf+sc*x0,rbuf);
+ }
+}
+
+GRID_INLET(DrawImage,0) {
+ NOTEMPTY(image);
+ NOTEMPTY(position);
+ SAME_TYPE(in,image);
+ if (in->dim->n!=3) RAISE("expecting 3 dimensions");
+ int lchan = in->dim->get(2);
+ int rchan = image->dim->get(2);
+ if (alpha && rchan!=4) {
+ RAISE("alpha mode works only with 4 channels in right_hand");
+ }
+ if (lchan != rchan-(alpha?1:0) && lchan != rchan) {
+ RAISE("right_hand has %d channels, alpha=%d, left_hand has %d, expecting %d or %d",
+ rchan, alpha?1:0, lchan, rchan-(alpha?1:0), rchan);
+ }
+ out=new GridOutlet(this,0,in->dim,in->nt);
+ in->set_factor(in->dim->get(1)*in->dim->get(2));
+} GRID_FLOW {
+ int f = in->factor();
+ int y = in->dex/f;
+ if (position->nt != int32_e) RAISE("position has to be int32");
+ int py = ((int32*)*position)[0], rsy = image->dim->v[0], sy=in->dim->get(0);
+ int px = ((int32*)*position)[1], rsx = image->dim->v[1], sx=in->dim->get(1);
+ for (; n; y++, n-=f, data+=f) {
+ int ty = div2(y-py,rsy);
+ if (tile || ty==0) {
+ Pt<T> data2 = ARRAY_NEW(T,f);
+ COPY(data2,data,f);
+ if (tile) {
+ for (int x=px-div2(px+rsx-1,rsx)*rsx; x<sx; x+=rsx) {
+ draw_segment(data2,data,mod(y-py,rsy),x);
+ }
+ } else {
+ draw_segment(data2,data,y-py,px);
+ }
+ out->give(f,data2);
+ } else {
+ out->send(f,data);
+ }
+ }
+} GRID_END
+
+GRID_INPUT(DrawImage,1,image) {} GRID_END
+GRID_INPUT(DrawImage,2,position) {} GRID_END
+\def void _0_alpha (bool v=true) { alpha = v; gfpost("ALPHA=%d",v); }
+\def void _0_tile (bool v=true) { tile = v; }
+
+\def void initialize (Numop *op, Grid *image, Grid *position) {
+ rb_call_super(argc,argv);
+ this->op = op;
+ if (image) this->image=image;
+ if (position) this->position=position;
+ else this->position=new Grid(new Dim(2),int32_e,true);
+}
+
+\classinfo { IEVAL(rself,"install '#draw_image',3,1"); }
+\end class DrawImage
+
+void startup_flow_objects_for_image () {
+ \startall
+}
diff --git a/externals/gridflow/base/flow_objects_for_matrix.c b/externals/gridflow/base/flow_objects_for_matrix.c
new file mode 100644
index 00000000..d245a10f
--- /dev/null
+++ b/externals/gridflow/base/flow_objects_for_matrix.c
@@ -0,0 +1,77 @@
+/*
+ $Id: flow_objects_for_matrix.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <math.h>
+#include "grid.h.fcs"
+
+// produce an upper triangular matrix with ones on the diagonal
+// will also affect any additional columns using the same row-operations
+
+void expect_complete_matrix (P<Dim> d) {
+ if (d->n!=2) RAISE("bletch");
+ if (d->get(0)>d->get(1)) RAISE("argh");
+}
+
+\class GridMatrixSolve < GridObject
+struct GridMatrixSolve : GridObject {
+ Numop *op_sub;
+ Numop *op_mul;
+ Numop *op_div;
+ PtrGrid matrix;
+ GridMatrixSolve() {
+ matrix.constrain(expect_complete_matrix);
+ }
+ \decl void initialize ();
+ \grin 0 float
+};
+
+GRID_INPUT(GridMatrixSolve,0,matrix) {
+ int n = matrix->dim->get(0); // # rows
+ int m = matrix->dim->get(1); // # columns
+ Pt<T> mat = (Pt<T>)*matrix;
+ for (int j=0; j<n; j++) {
+ op_div->map(m,mat+j*m,mat[j*m+j]);
+ for (int i=j+1; i<n; i++) {
+ STACK_ARRAY(T,row,m);
+ COPY(row,mat+j,m);
+ op_mul->map(m,row,mat[i*m+j]);
+ op_sub->zip(m,mat+i*m,row);
+ }
+ }
+ GridOutlet out(this,0,matrix->dim);
+ out.send(n*m,mat);
+} GRID_END
+
+\def void initialize () {
+ rb_call_super(argc,argv);
+ this->op_sub = op_sub;
+ this->op_mul = op_mul;
+ this->op_div = op_div;
+}
+
+\classinfo { IEVAL(rself,"install '#matrix_solve',1,1"); }
+\end class
+
+void startup_flow_objects_for_matrix () {
+ \startall
+}
diff --git a/externals/gridflow/base/grid.c b/externals/gridflow/base/grid.c
new file mode 100644
index 00000000..877a460f
--- /dev/null
+++ b/externals/gridflow/base/grid.c
@@ -0,0 +1,604 @@
+/*
+ $Id: grid.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "grid.h.fcs"
+#include <ctype.h>
+
+/* copied from bridge/puredata.c (sorry: linkage issue) */
+struct Pointer : CObject { void *p; Pointer(void *_p) : p(_p) {}};
+Ruby Pointer_s_noo (void *ptr) {
+ return Data_Wrap_Struct(EVAL("GridFlow::Pointer"), 0, 0, new Pointer(ptr));}
+static void *Pointer_gut (Ruby rself) {DGS(Pointer); return self->p;}
+
+//#define TRACE fprintf(stderr,"%s %s [%s:%d]\n",INFO(parent),__PRETTY_FUNCTION__,__FILE__,__LINE__);assert(this);
+#define TRACE assert(this);
+
+#define CHECK_TYPE(d) \
+ if (NumberTypeE_type_of(d)!=this->nt) RAISE("%s(%s): " \
+ "type mismatch during transmission (got %s expecting %s)", \
+ INFO(parent), \
+ __PRETTY_FUNCTION__, \
+ number_type_table[NumberTypeE_type_of(d)].name, \
+ number_type_table[this->nt].name);
+
+#define CHECK_BUSY(s) \
+ if (!dim) RAISE("%s: " #s " not busy",INFO(parent));
+
+#define CHECK_ALIGN(d) \
+ {int bytes = number_type_table[nt].size/8; \
+ int align = ((long)(void*)d)%bytes; \
+ if (align) {L;gfpost("%s(%s): Alignment Warning: %p is not %d-aligned: %d", \
+ INFO(parent), __PRETTY_FUNCTION__, (void*)d,bytes,align);}}
+
+#define CHECK_ALIGN2(d,nt) \
+ {int bytes = number_type_table[nt].size/8; \
+ int align = ((long)(void*)d)%bytes; \
+ if (align) {L;gfpost("Alignment Warning: %p is not %d-aligned: %d", \
+ (void*)d,bytes,align);}}
+
+// **************** Grid ******************************************
+
+#define FOO(S) static inline void NUM(Ruby x, S &y) {y=convert(x,(int32*)0);}
+EACH_INT_TYPE(FOO)
+#undef FOO
+
+#define FOO(S) \
+static inline void NUM(Ruby x, S &y) { \
+ if (TYPE(x)==T_FLOAT) y = RFLOAT(x)->value; \
+ else if (INTEGER_P(x)) y = convert(x,(S*)0); \
+ else RAISE("expected Float (or at least Integer)");}
+EACH_FLOAT_TYPE(FOO)
+#undef FOO
+
+void Grid::init_from_ruby_list(int n, Ruby *a, NumberTypeE nt) {
+ Ruby delim = SYM(#);
+ for (int i=0; i<n; i++) {
+ if (a[i] == delim) {
+ STACK_ARRAY(int32,v,i);
+ if (i!=0 && TYPE(a[i-1])==T_SYMBOL) nt=NumberTypeE_find(a[--i]);
+ for (int j=0; j<i; j++) v[j] = convert(a[j],(int32*)0);
+ init(new Dim(i,v),nt);
+ CHECK_ALIGN2(this->data,nt);
+ if (a[i] != delim) i++;
+ i++; a+=i; n-=i;
+ goto fill;
+ }
+ }
+ if (n!=0 && TYPE(a[0])==T_SYMBOL) {
+ nt = NumberTypeE_find(a[0]);
+ a++, n--;
+ }
+ init(new Dim(n),nt);
+ CHECK_ALIGN2(this->data,nt);
+ fill:
+ int nn = dim->prod();
+ n = min(n,nn);
+#define FOO(type) { \
+ Pt<type> p = (Pt<type>)*this; \
+ if (n==0) CLEAR(p,nn); \
+ else { \
+ for (int i=0; i<n; i++) NUM(a[i],p[i]); \
+ for (int i=n; i<nn; i+=n) COPY(p+i,p,min(n,nn-i)); }}
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+}
+
+void Grid::init_from_ruby(Ruby x) {
+ if (TYPE(x)==T_ARRAY) {
+ init_from_ruby_list(rb_ary_len(x),rb_ary_ptr(x));
+ } else if (INTEGER_P(x) || FLOAT_P(x)) {
+ init(new Dim(),int32_e);
+ CHECK_ALIGN2(this->data,nt);
+ ((Pt<int32>)*this)[0] = INT(x);
+ } else {
+ rb_funcall(
+ EVAL("proc{|x| raise \"can't convert to grid: #{x.inspect}\"}"),
+ SI(call),1,x);
+ }
+}
+
+// **************** GridInlet *************************************
+
+// must be set before the end of GRID_BEGIN phase, and so cannot be changed
+// afterwards. This is to allow some optimisations. Anyway there is no good reason
+// why this would be changed afterwards.
+void GridInlet::set_factor(int factor) {
+ if(!dim) RAISE("huh?");
+ if(factor<=0) RAISE("%s: factor=%d should be >= 1",INFO(parent),factor);
+ if (dim->prod() && dim->prod() % factor)
+ RAISE("%s: set_factor: expecting divisor",INFO(parent));
+ if (factor > 1) {
+ buf=new Grid(new Dim(factor), nt);
+ bufi=0;
+ } else {
+ buf=0;
+ }
+}
+
+static Ruby GridInlet_begin_1(GridInlet *self) {
+#define FOO(T) self->gh->flow(self,-1,Pt<T>()); break;
+ TYPESWITCH(self->nt,FOO,)
+#undef FOO
+ return Qnil;
+}
+
+static Ruby GridInlet_begin_2(GridInlet *self) {
+ self->dim = 0; // hack
+ return (Ruby) 0;
+}
+
+bool GridInlet::supports_type(NumberTypeE nt) {
+#define FOO(T) return !! gh->flow_##T;
+ TYPESWITCH(nt,FOO,return false)
+#undef FOO
+}
+
+Ruby GridInlet::begin(int argc, Ruby *argv) {TRACE;
+ if (!argc) return PTR2FIX(this);
+ GridOutlet *back_out = (GridOutlet *) Pointer_gut(argv[0]);
+ nt = (NumberTypeE) INT(argv[1]);
+ argc-=2, argv+=2;
+ PROF(parent) {
+ if (dim) {
+ gfpost("%s: grid inlet conflict; aborting %s in favour of %s",
+ INFO(parent), INFO(sender), INFO(back_out->parent));
+ abort();
+ }
+ sender = back_out->parent;
+ if ((int)nt<0 || (int)nt>=(int)number_type_table_end)
+ RAISE("%s: inlet: unknown number type",INFO(parent));
+ if (!supports_type(nt))
+ RAISE("%s: number type %s not supported here",
+ INFO(parent), number_type_table[nt].name);
+ STACK_ARRAY(int32,v,argc);
+ for (int i=0; i<argc; i++) v[i] = NUM2INT(argv[i]);
+ P<Dim> dim = this->dim = new Dim(argc,v);
+ dex=0;
+ buf=0;
+ int r = rb_ensure(
+ (RMethod)GridInlet_begin_1,(Ruby)this,
+ (RMethod)GridInlet_begin_2,(Ruby)this);
+ if (!r) {abort(); goto hell;}
+ this->dim = dim;
+ back_out->callback(this);
+ hell:;} // PROF
+ return Qnil;
+}
+
+template <class T> void GridInlet::flow(int mode, int n, Pt<T> data) {TRACE;
+ CHECK_BUSY(inlet);
+ CHECK_TYPE(*data);
+ CHECK_ALIGN(data);
+ PROF(parent) {
+ if (this->mode==0) {dex += n; return;} // ignore data
+ if (n==0) return; // no data
+ switch(mode) {
+ case 4:{
+ int d = dex + bufi;
+ if (d+n > dim->prod()) {
+ gfpost("grid input overflow: %d of %d from [%s] to [%s]",
+ d+n, dim->prod(), INFO(sender), 0);
+ n = dim->prod() - d;
+ if (n<=0) return;
+ }
+ int bufn = factor();
+ if (buf && bufi) {
+ Pt<T> bufd = (Pt<T>)*buf;
+ int k = min(n,bufn-bufi);
+ COPY(bufd+bufi,data,k);
+ bufi+=k; data+=k; n-=k;
+ if (bufi==bufn) {
+ int newdex = dex+bufn;
+ if (this->mode==6) {
+ Pt<T> data2 = ARRAY_NEW(T,bufn);
+ COPY(data2,bufd,bufn);
+ CHECK_ALIGN(data2);
+ gh->flow(this,bufn,data2);
+ } else {
+ CHECK_ALIGN(bufd);
+ gh->flow(this,bufn,bufd);
+ }
+ dex = newdex;
+ bufi = 0;
+ }
+ }
+ int m = (n/bufn)*bufn;
+ if (m) {
+ int newdex = dex + m;
+ if (this->mode==6) {
+ Pt<T> data2 = ARRAY_NEW(T,m);
+ COPY(data2,data,m);
+ CHECK_ALIGN(data2);
+ gh->flow(this,m,data2);
+ } else {
+ gh->flow(this,m,data);
+ }
+ dex = newdex;
+ }
+ data += m;
+ n -= m;
+ if (buf && n>0) COPY((Pt<T>)*buf+bufi,data,n), bufi+=n;
+ }break;
+ case 6:{
+ assert(!buf);
+ int newdex = dex + n;
+ gh->flow(this,n,data);
+ if (this->mode==4) delete[] (T *)data;
+ dex = newdex;
+ }break;
+ case 0: break; // ignore data
+ default: RAISE("%s: unknown inlet mode",INFO(parent));
+ }} // PROF
+}
+
+void GridInlet::end() {TRACE;
+ assert(this);
+ if (!dim) RAISE("%s: inlet not busy",INFO(parent));
+ if (dim->prod() != dex) {
+ gfpost("incomplete grid: %d of %d from [%s] to [%s]",
+ dex, dim->prod(), INFO(sender), INFO(parent));
+ }
+ PROF(parent) {
+#define FOO(T) gh->flow(this,-2,Pt<T>());
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+ } // PROF
+ dim=0;
+ buf=0;
+ dex=0;
+}
+
+template <class T> void GridInlet::from_grid2(Grid *g, T foo) {TRACE;
+ nt = g->nt;
+ dim = g->dim;
+ int n = g->dim->prod();
+ gh->flow(this,-1,Pt<T>());
+ if (n>0 && this->mode!=0) {
+ Pt<T> data = (Pt<T>)*g;
+ CHECK_ALIGN(data);
+ int size = g->dim->prod();
+ if (this->mode==6) {
+ Pt<T> d = data;
+ data = ARRAY_NEW(T,size); // problem with int64,float64 here.
+ COPY(data,d,size);
+ CHECK_ALIGN(data);
+ gh->flow(this,n,data);
+ } else {
+ int ntsz = number_type_table[nt].size;
+ int m = GridOutlet::MAX_PACKET_SIZE/*/ntsz*//factor();
+ if (!m) m++;
+ m *= factor();
+ while (n) {
+ if (m>n) m=n;
+ CHECK_ALIGN(data);
+ gh->flow(this,m,data);
+ data+=m; n-=m; dex+=m;
+ }
+ }
+ }
+ gh->flow(this,-2,Pt<T>());
+ //!@#$ add error handling.
+ // rescue; abort(); end ???
+ dim = 0;
+ dex = 0;
+}
+
+void GridInlet::from_grid(Grid *g) {TRACE;
+ if (!supports_type(g->nt))
+ RAISE("%s: number type %s not supported here",
+ INFO(parent), number_type_table[g->nt].name);
+#define FOO(T) from_grid2(g,(T)0);
+ TYPESWITCH(g->nt,FOO,)
+#undef FOO
+}
+
+/* **************** GridOutlet ************************************ */
+
+void GridOutlet::begin(int woutlet, P<Dim> dim, NumberTypeE nt) {TRACE;
+ int n = dim->count();
+ this->nt = nt;
+ this->dim = dim;
+ Ruby a[n+4];
+ a[0] = INT2NUM(woutlet);
+ a[1] = bsym._grid;
+ a[2] = Pointer_s_noo(this);
+ a[3] = INT2NUM(nt);
+ for(int i=0; i<n; i++) a[4+i] = INT2NUM(dim->get(i));
+ parent->send_out(COUNT(a),a);
+ frozen=true;
+ if (!dim->prod()) {end(); return;}
+ int32 lcm_factor = 1;
+ for (uint32 i=0; i<inlets.size(); i++) lcm_factor = lcm(lcm_factor,inlets[i]->factor());
+ if (nt != buf->nt) {
+ // biggest packet size divisible by lcm_factor
+ int32 v = (MAX_PACKET_SIZE/lcm_factor)*lcm_factor;
+ if (v==0) v=MAX_PACKET_SIZE; // factor too big. don't have a choice.
+ buf=new Grid(new Dim(v),nt);
+ }
+}
+
+// send modifies dex; send_direct doesn't
+template <class T>
+void GridOutlet::send_direct(int n, Pt<T> data) {TRACE;
+ assert(data); assert(frozen);
+ CHECK_BUSY(outlet); CHECK_TYPE(*data); CHECK_ALIGN(data);
+ for (; n>0; ) {
+ int pn = min(n,MAX_PACKET_SIZE);
+ for (uint32 i=0; i<inlets.size(); i++) inlets[i]->flow(4,pn,data);
+ data+=pn, n-=pn;
+ }
+}
+
+void GridOutlet::flush() {TRACE;
+ if (!bufi) return;
+#define FOO(T) send_direct(bufi,(Pt<T>)*buf);
+ TYPESWITCH(buf->nt,FOO,)
+#undef FOO
+ bufi = 0;
+}
+
+template <class T, class S>
+static void convert_number_type(int n, Pt<T> out, Pt<S> in) {
+ for (int i=0; i<n; i++) out[i]=(T)in[i];
+}
+
+//!@#$ buffering in outlet still is 8x faster...?
+//!@#$ should use BitPacking for conversion...?
+// send modifies dex; send_direct doesn't
+template <class T>
+void GridOutlet::send(int n, Pt<T> data) {TRACE;
+ assert(data); assert(frozen);
+ if (!n) return;
+ CHECK_BUSY(outlet); CHECK_ALIGN(data);
+ if (NumberTypeE_type_of(*data)!=nt) {
+ int bs = MAX_PACKET_SIZE;
+#define FOO(T) { \
+ STACK_ARRAY(T,data2,bs); \
+ for (;n>=bs;n-=bs,data+=bs) { \
+ convert_number_type(bs,data2,data); send(bs,data2);} \
+ convert_number_type(n,data2,data); \
+ send(n,data2); }
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+ } else {
+ dex += n;
+ assert(dex <= dim->prod());
+ if (n > MIN_PACKET_SIZE || bufi + n > MAX_PACKET_SIZE) flush();
+ if (n > MIN_PACKET_SIZE) {
+ send_direct(n,data);
+ } else {
+ COPY((Pt<T>)*buf+bufi,data,n);
+ bufi += n;
+ }
+ if (dex==dim->prod()) end();
+ }
+}
+
+template <class T>
+void GridOutlet::give(int n, Pt<T> data) {TRACE;
+ assert(data); CHECK_BUSY(outlet); assert(frozen);
+ assert(dex+n <= dim->prod()); CHECK_ALIGN(data);
+ if (NumberTypeE_type_of(*data)!=nt) {
+ send(n,data);
+ delete[] (T *)data;
+ return;
+ }
+ if (inlets.size()==1 && inlets[0]->mode == 6) {
+ // this is the copyless buffer passing
+ flush();
+ inlets[0]->flow(6,n,data);
+ dex += n;
+ } else {
+ flush();
+ send_direct(n,data);
+ dex += n;
+ delete[] (T *)data;
+ }
+ if (dex==dim->prod()) end();
+}
+
+void GridOutlet::callback(GridInlet *in) {TRACE;
+ CHECK_BUSY(outlet); assert(!frozen);
+ int mode = in->mode;
+ assert(mode==6 || mode==4 || mode==0);
+ inlets.push_back(in);
+}
+
+\class GridObject < FObject
+
+//!@#$ does not handle types properly
+//!@#$ most possibly a big hack
+template <class T>
+void GridObject_r_flow(GridInlet *in, int n, Pt<T> data) {
+ GridObject *self = in->parent;
+ uint32 i;
+ for (i=0; i<self->in.size(); i++) if (in==self->in[i].p) break;
+ if (i==self->in.size()) RAISE("inlet not found?");
+ if (n==-1) {
+ rb_funcall(self->rself,SI(send_in),2,INT2NUM(i),SYM(rgrid_begin));
+ } else if (n>=0) {
+ Ruby buf = rb_str_new((char *)((uint8 *)data),n*sizeof(T));
+ rb_funcall(self->rself,SI(send_in),3,INT2NUM(i),SYM(rgrid_flow),buf);
+ } else {
+ rb_funcall(self->rself,SI(send_in),2,INT2NUM(i),SYM(rgrid_end));
+ }
+}
+
+\def Symbol inlet_nt (int inln) {
+ if (inln<0 || inln>=(int)in.size()) RAISE("bad inlet number");
+ P<GridInlet> inl = in[inln];
+ if (!inl) RAISE("no such inlet #%d",inln);
+ if (!inl->dim) return Qnil;
+ return number_type_table[inl->nt].sym;
+}
+
+\def Array inlet_dim (int inln) {
+ if (inln<0 || inln>=(int)in.size()) RAISE("bad inlet number");
+ P<GridInlet> inl = in[inln];
+ if (!inl) RAISE("no such inlet #%d",inln);
+ if (!inl->dim) return Qnil;
+ int n=inl->dim->count();
+ Ruby a = rb_ary_new2(n);
+ for(int i=0; i<n; i++) rb_ary_push(a,INT2NUM(inl->dim->v[i]));
+ return a;
+}
+
+\def void inlet_set_factor (int inln, int factor) {
+ if (inln<0 || inln>=(int)in.size()) RAISE("bad inlet number");
+ P<GridInlet> inl = in[inln];
+ if (!inl) RAISE("no such inlet #%d",inln);
+ if (!inl->dim) RAISE("inlet #%d not active",inln);
+ inl->set_factor(factor);
+}
+
+\def void send_out_grid_begin (int outlet, Array buf, NumberTypeE nt=int32_e) {
+ if (outlet<0) RAISE("bad outlet number");
+ int n = rb_ary_len(buf);
+ Ruby *p = rb_ary_ptr(buf);
+ STACK_ARRAY(int32,v,n);
+ for (int i=0; i<n; i++) v[i] = convert(p[i],(int32*)0);
+ out = new GridOutlet(this,outlet,new Dim(n,v),nt); // valgrind says leak?
+}
+
+template <class T>
+void send_out_grid_flow_2(P<GridOutlet> go, Ruby s, T bogus) {
+ int n = rb_str_len(s) / sizeof(T);
+ Pt<T> p = rb_str_pt(s,T);
+ go->send(n,p);
+}
+
+\def void send_out_grid_flow (int outlet, String buf, NumberTypeE nt=int32_e) {
+ if (outlet<0) RAISE("bad outlet number");
+#define FOO(T) send_out_grid_flow_2(out,argv[1],(T)0);
+ TYPESWITCH(nt,FOO,)
+#undef FOO
+}
+
+// install_rgrid(Integer inlet, Boolean multi_type? = true)
+static Ruby GridObject_s_install_rgrid(int argc, Ruby *argv, Ruby rself) {
+ if (argc<1 || argc>2) RAISE("er...");
+ IEVAL(rself,"@handlers||=[]");
+ Ruby handlers = rb_ivar_get(rself,SI(@handlers));
+ GridHandler *gh = new GridHandler;
+ bool mt = argc>1 ? argv[1]==Qtrue : 0; /* multi_type? */
+ if (mt) {
+#define FOO(S) gh->flow_##S = GridObject_r_flow;
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ } else {
+#define FOO(S) gh->flow_##S = 0;
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ }
+ gh->flow_int32 = GridObject_r_flow;
+ //IEVAL(rself,"self.class_eval { def _0_grid(*a) ___grid(0,*a) end }");
+ rb_funcall(handlers,SI([]=),2,INT2NUM(INT(argv[0])),PTR2FIX(gh));
+ return Qnil;
+}
+
+static Ruby GridObject_s_instance_methods(int argc, Ruby *argv, Ruby rself) {
+ static const char *names[] = {"grid","list","float"};
+ Ruby list = rb_class_instance_methods(argc,argv,rself);
+ Ruby handlers = rb_ivar_get(rself,SI(@handlers));
+ if (handlers==Qnil) return list;
+ for (int i=0; i<rb_ary_len(handlers); i++) {
+ Ruby ghp = rb_ary_ptr(handlers)[i];
+ if (ghp==Qnil) continue;
+ GridHandler *gh = FIX2PTR(GridHandler,ghp);
+ char buf[256];
+ for (int j=0; j<COUNT(names); j++) {
+ sprintf(buf,"_%d_%s",i,names[j]);
+ rb_ary_push(list,rb_str_new2(buf));
+ }
+ }
+ return list;
+}
+
+// this does auto-conversion of list/float to grid
+// this also (will) do grid inputs for ruby stuff.
+\def Ruby method_missing (...) {
+ {
+ if (argc<1) RAISE("not enough arguments");
+ if (!SYMBOL_P(argv[0])) RAISE("expected symbol");
+ const char *name = rb_sym_name(argv[0]);
+ char *endp;
+ if (*name++!='_') goto hell;
+ int i = strtol(name,&endp,10);
+ if (name==endp) goto hell;
+ if (*endp++!='_') goto hell;
+ if (strcmp(endp,"grid")==0) {
+ Ruby handlers = rb_ivar_get(rb_obj_class(rself),SI(@handlers));
+ if (TYPE(handlers)!=T_ARRAY) {
+ rb_p(handlers);
+ RAISE("gridhandler-list missing (maybe forgot install_rgrid ?)"
+ " while trying to receive on inlet %d",i);
+ }
+ if (i>=rb_ary_len(handlers)) RAISE("BORK");
+ GridHandler *gh = FIX2PTR(GridHandler, rb_ary_ptr(handlers)[i]);
+ if (in.size()<=(uint32)i) in.resize(i+1);
+ if (!in[i]) in[i]=new GridInlet((GridObject *)this,gh);
+ return in[i]->begin(argc-1,argv+1);
+ }
+ // we call the grid method recursively to ask it its GridInlet*
+ // don't do this before checking the missing method is exactly that =)
+ char foo[42];
+ sprintf(foo,"_%d_grid",i);
+ P<GridInlet> inl = FIX2PTR(GridInlet,rb_funcall(rself,rb_intern(foo),0));
+ if (strcmp(endp,"list" )==0) return inl->from_ruby_list(argc-1,argv+1), Qnil;
+ if (strcmp(endp,"float")==0) return inl->from_ruby (argc-1,argv+1), Qnil;
+ }
+ hell: return rb_call_super(argc,argv);
+}
+
+\classinfo {
+ IEVAL(rself,"install 'GridObject',0,0");
+ // define in Ruby-metaclass
+ rb_define_singleton_method(rself,"instance_methods",(RMethod)GridObject_s_instance_methods,-1);
+ rb_define_singleton_method(rself,"install_rgrid",(RMethod)GridObject_s_install_rgrid,-1);
+ rb_enable_super(rb_singleton_class(rself),"instance_methods");
+}
+\end class GridObject
+
+Ruby cGridObject;
+
+void startup_grid () {
+ \startall
+ cGridObject = rb_const_get(mGridFlow,SI(GridObject));
+}
+
+// never call this. this is a hack to make some things work.
+// i'm trying to circumvent either a bug in the compiler or i don't have a clue. :-(
+void make_gimmick () {
+ GridOutlet foo(0,0,0);
+#define FOO(S) foo.give(0,Pt<S>());
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+}
+
diff --git a/externals/gridflow/base/grid.h b/externals/gridflow/base/grid.h
new file mode 100644
index 00000000..4aaf2aa3
--- /dev/null
+++ b/externals/gridflow/base/grid.h
@@ -0,0 +1,1109 @@
+/*
+ $Id: grid.h,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __GF_GRID_H
+#define __GF_GRID_H
+
+// current version number as string literal
+#define GF_VERSION "0.8.0"
+#define GF_COMPILE_TIME __DATE__ ", " __TIME__
+
+#include <new>
+#include <vector>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <math.h>
+
+extern "C" {
+#include <ruby.h>
+#include <rubyio.h>
+#include <version.h>
+};
+
+#ifndef RUBY_H
+#error "Can't do anything without ruby.h"
+#endif
+
+#include "../config.h"
+
+#ifdef __WIN32__
+#define INT winINT
+#define random rand
+#undef send
+#undef close
+#define sigjmp_buf jmp_buf
+#define siglongjmp longjmp
+#endif
+
+#define BUG(s,args...) {fprintf(stderr,s "\nat: %s\n",args,__PRETTY_FUNCTION__); ::raise(11);}
+
+// !@#$ what am I going to do about this? should this be changed?
+// should I wrap all of the Ruby API for C++-style convenience?
+typedef VALUE Ruby;
+// typedef struct Ruby { VALUE x };
+
+#ifdef IS_BRIDGE
+#define RAISE(args...) rb_raise(rb_eArgError,args)
+#else
+#define RAISE(args...) rb_raise0(__FILE__,__LINE__,__PRETTY_FUNCTION__,rb_eArgError,args)
+#endif
+
+// avoid ruby warning
+#ifndef rb_enable_super
+#define rb_enable_super(a,b) \
+ if (RUBY_RELEASE_CODE < 20030716) rb_enable_super(a,b)
+#endif
+
+/* undocumented function from Ruby that is one thing we need to fix a very elusive bug
+that manifests itself when embedding ruby inside a plugin of another app. This exists
+for all versions of Ruby up to now, and I don't know when it gets fixed. */
+extern "C" void Init_stack(VALUE *addr);
+
+extern "C" {
+void rb_raise0(
+const char *file, int line, const char *func, VALUE exc, const char *fmt, ...)
+__attribute__ ((noreturn));
+};
+#define L fprintf(stderr,"%s:%d in %s\n",__FILE__,__LINE__,__PRETTY_FUNCTION__);
+#define SI(_sym_) (rb_intern(#_sym_))
+#define SYM(_sym_) (ID2SYM(SI(_sym_)))
+#define DGS(_class_) \
+ _class_ *self; \
+ Data_Get_Struct(rself,_class_,self); \
+ self->check_magic();
+
+// returns the size of a statically defined array
+// warning: does not work with STACK_ARRAY()
+#define COUNT(_array_) ((int)(sizeof(_array_) / sizeof((_array_)[0])))
+
+static inline long rb_str_len(Ruby s) {return RSTRING(s)->len;}
+static inline char *rb_str_ptr(Ruby s) {return RSTRING(s)->ptr;}
+static inline long rb_ary_len(Ruby s) {return RARRAY(s)->len;}
+static inline Ruby *rb_ary_ptr(Ruby s) {return RARRAY(s)->ptr;}
+static inline const char *rb_sym_name(Ruby sym) {return rb_id2name(SYM2ID(sym));}
+#define rb_str_pt(s,t) Pt<t>((t*)rb_str_ptr(s),rb_str_len(s))
+
+// shorthands
+#define IEVAL(_self_,s) rb_funcall(_self_,SI(instance_eval),1,rb_str_new2(s))
+#define EVAL(s) rb_eval_string(s)
+#define rassert(_p_) if (!(_p_)) RAISE(#_p_);
+
+// because of older versions of Ruby (1.6.?)
+#define rb_obj_class(o) rb_funcall((o),SI(class),0)
+
+#define WATCH(n,ar) { \
+ char foo[16*1024], *p=foo; \
+ p += sprintf(p,"%s: ",#ar); \
+ for (int q=0; q<n; q++) p += sprintf(p,"%lld ",(long long)ar[q]); \
+ gfpost("%s",foo); \
+}
+
+// we're gonna override assert, so load it first, to avoid conflicts
+#include <assert.h>
+
+#undef assert
+#define assert(_expr_) \
+ if (!(_expr_)) { \
+ fprintf(stderr, "%s:%d: assertion failed: %s is false\n", \
+ __FILE__, __LINE__, #_expr_); \
+ ::abort(); }
+
+// disabling assertion checking?
+#ifndef HAVE_DEBUG
+#undef assert
+#define assert(_foo_)
+#endif
+
+#ifdef HAVE_TSC_PROFILING
+#define HAVE_PROFILING
+#endif
+
+#ifdef HAVE_PROFILING
+#define PROF(_self_) for (GFStackMarker gf_marker(_self_);gf_marker.once();)
+#else
+#define PROF(_self_)
+#endif // HAVE_PROFILING
+
+static inline Ruby PTR2FIX (const void *ptr) {
+ long p = (long)ptr;
+ if ((p&3)!=0) BUG("unaligned pointer: %08x\n",(long)(ptr));
+ return LONG2NUM(p>>2);
+}
+#define FIX2PTR(type,ruby) ((type *)(TO(long,ruby)<<2))
+
+//****************************************************************
+
+/* int32 was long before, now int, because of amd64 */
+typedef char int8; typedef unsigned char uint8;
+typedef short int16; typedef unsigned short uint16;
+typedef int int32; typedef unsigned int uint32;
+typedef long long int64;typedef unsigned long long uint64;
+typedef float float32;
+typedef double float64;
+
+// three-way comparison (T is assumed Comparable)
+template <class T> static inline T cmp(T a, T b) { return a<b ? -1 : a>b; }
+
+// a remainder function such that div2(a,b)*b+mod(a,b) = a and for which
+// mod(a,b) is in [0;b) or (b;0]. in contrast to C-language builtin a%b,
+// this one has uniform behaviour around zero.
+static inline int mod(int a, int b) {
+int c=a%b; c+=b&-(c&&(a<0)^(b<0)); return c;}
+
+// counterpart of mod(a,b), just like a/b and a%b are counterparts
+static inline int div2(int a, int b) {
+int c=a<0; return (a/b)-(c&&!!(a%b));}
+
+static inline int32 gf_abs( int32 a) { return a>0?a:-a; }
+static inline int64 gf_abs( int64 a) { return a>0?a:-a; }
+static inline float32 gf_abs(float32 a) { return fabs(a); }
+static inline float64 gf_abs(float64 a) { return fabs(a); }
+
+// integer powers in log(b) time. T is assumed Integer
+template <class T> static inline T ipow(T a, T b) {
+ for(T r=1;;) {if (b&1) r*=a; b>>=1; if (!b) return r; a*=a;}
+}
+
+// kludge
+static inline float32 ipow(float32 a, float32 b) { return pow(a,b); }
+static inline float64 ipow(float64 a, float64 b) { return pow(a,b); }
+
+#undef min
+#undef max
+// minimum/maximum functions; T is assumed to be Comparable
+template <class T> static inline T min(T a, T b) { return a<b?a:b; }
+template <class T> static inline T max(T a, T b) { return a>b?a:b; }
+//template <class T> inline T min(T a, T b) { T c = (a-b)>>31; return (a&c)|(b&~c); }
+//template <class T> inline T max(T a, T b) { T c = (a-b)>>31; return (a&c)|(b&~c); }
+
+// greatest common divisor, by euclid's algorithm
+// this runs in log(a+b) number operations
+template <class T> static T gcd (T a, T b) {
+ while (b) {T c=mod(a,b); a=b; b=c;}
+ return a;
+}
+
+// greatest common divisor, the binary algorithm. haven't tried yet.
+template <class T> static T gcd2 (T a, T b) {
+ int s=0;
+ while ((a|b)&1==0) { a>>=1; b>>=1; s++; }
+ while (a) {
+ if (a&1==0) a>>=1;
+ else if (b&1==0) b>>=1;
+ else {T t=abs(a-b); if (a<b) b=t; else a=t;}
+ }
+ return b<<s;
+}
+
+// least common multiple; this runs in log(a+b) like gcd.
+template <class T> static inline T lcm (T a, T b) {return a*b/gcd(a,b);}
+
+// returns the position (0..31) of highest bit set in a word, or 0 if none.
+#define FOO(N) if ((x>>N)&(((typeof(x))1<<N)-1)) { x>>=N; i+=N; }
+static int highest_bit(uint8 x) {int i=0; FOO(4)FOO(2)FOO(1)return i;}
+static int highest_bit(uint16 x) {int i=0; FOO(8)FOO(4)FOO(2)FOO(1)return i;}
+static int highest_bit(uint32 x) {int i=0; FOO(16)FOO(8)FOO(4)FOO(2)FOO(1)return i;}
+static int highest_bit(uint64 x) {int i=0;FOO(32)FOO(16)FOO(8)FOO(4)FOO(2)FOO(1)return i;}
+#undef FOO
+// returns the position (0..31) of lowest bit set in a word, or 0 if none.
+template <class T> static int lowest_bit(T n) { return highest_bit((~n+1)&n); }
+
+static double drand() { return 1.0*rand()/(RAND_MAX+1.0); }
+
+// is little-endian
+static inline bool is_le() {int x=1; return ((char *)&x)[0];}
+
+#if defined(HAVE_PENTIUM)
+static inline uint64 rdtsc() {
+ uint64 x; __asm__ volatile (".byte 0x0f, 0x31":"=A"(x)); return x;
+}
+#elif defined(HAVE_PPC)
+static inline uint64 rdtsc() {
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+/* see AbsoluteToNanoseconds(), Microseconds()
+http://www.simdtech.org/apps/group_public/email/altivec/msg01956.html
+and mach_absolute_time() and mach_timebase_info(&info),
+then ns=(time*info.numer)/info.denom;
+and the timebase_info is constant
+(double)mach_absolute_time*gTimeBaseToNS*/
+return 0;
+}
+#else
+static inline uint64 rdtsc() {return 0;}
+#endif
+
+#ifdef HAVE_LITE
+#define EACH_INT_TYPE(MACRO) MACRO(uint8) MACRO(int16) MACRO(int32)
+#define EACH_FLOAT_TYPE(MACRO)
+#else
+#define EACH_INT_TYPE(MACRO) MACRO(uint8) MACRO(int16) MACRO(int32) MACRO(int64)
+#define EACH_FLOAT_TYPE(MACRO) MACRO(float32) MACRO(float64)
+#endif
+#define EACH_NUMBER_TYPE(MACRO) EACH_INT_TYPE(MACRO) EACH_FLOAT_TYPE(MACRO)
+
+// note: loop unrolling macros assume N!=0
+// btw this may cause alignment problems when 8 does not divide N
+#define UNROLL_8(MACRO,N,PTR,ARGS...) \
+ int n__=(-N)&7; PTR-=n__; N+=n__; \
+ switch (n__) { start: \
+ case 0:MACRO(0); case 1:MACRO(1); case 2:MACRO(2); case 3:MACRO(3); \
+ case 4:MACRO(4); case 5:MACRO(5); case 6:MACRO(6); case 7:MACRO(7); \
+ PTR+=8; N-=8; ARGS; if (N) goto start; }
+#define UNROLL_4(MACRO,N,PTR,ARGS...) \
+ int n__=(-N)&3; PTR-=n__; N+=n__; \
+ switch (n__) { start: \
+ case 0:MACRO(0); case 1:MACRO(1); case 2:MACRO(2); case 3:MACRO(3); \
+ PTR+=4; N-=4; ARGS; if (N) goto start; }
+
+//****************************************************************
+// hook into pointer manipulation. will help find memory corruption bugs.
+
+template <class T> class Pt {
+typedef ptrdiff_t /* and not int nor long */ Z;
+public:
+ T *p;
+#ifdef HAVE_DEBUG
+ T *start;
+ Z n;
+ Pt() : p(0), start(0), n(0) {}
+ Pt(T *q, Z _n) : p(q), start(q), n(_n) {}
+ Pt(T *q, Z _n, T *_start) : p(q), start(_start), n(_n) {}
+#else
+ Pt() : p(0) {}
+ Pt(T *q, Z _n, T *_start=0) : p(q) {}
+#endif
+ T &operator *() { return *p; }
+ Pt operator+=(Z i) { p+=i; return *this; }
+ Pt operator-=(Z i) { p-=i; return *this; }
+ Pt operator++( ) { p++; return *this; }
+ Pt operator--( ) { p--; return *this; }
+ Pt operator++(int) { Pt f(*this); ++*this; return f; }
+ Pt operator--(int) { Pt f(*this); --*this; return f; }
+ T &operator[](Z i) {
+#ifdef HAVE_DEBUG_HARDER
+ if (!(p+i>=start && p+i<start+n))
+ BUG("BUFFER OVERFLOW: 0x%08lx[%ld]=0x%08lx is not in 0x%08lx..0x%08lx",
+ (long)p, (long)i, (long)(p+i),(long)start,(long)(start+n));
+#endif
+ return p[i];
+ }
+
+ void will_use(int k) {
+#ifdef HAVE_DEBUG_HARDER
+ if (k==0) return;
+ T *q = p+k;
+ if (!(p>=start && p<start+n && q>=start && q<=start+n))
+ BUG("BUFFER OVERFLOW: 0x%08lx...0x%08lx is not all inside 0x%08lx...0x%08lx",
+ (long)p,(long)q,start,(long)(start+n));
+#endif
+ }
+
+ Z operator-(Pt x) { return p-x.p; }
+ operator bool () { return (bool )p; }
+ operator void *() { return (void *)p; }
+ operator int8 *() { return (int8 *)p; }
+
+/* how do i make typecast operators that are not also default conversions??? */
+/* this should be found so i can clean up the following: */
+#define FOO(S) operator S *() { return (S *)p; }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+#ifdef HAVE_DEBUG
+#define FOO(S) operator Pt<S>() { return Pt<S>((S *)p,n*sizeof(T)/1,(S *)start); }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+#else
+#define FOO(S) operator Pt<S>() { return Pt<S>((S *)p,0); }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+#endif
+/* end 0.8 (TESTING) */
+
+#ifdef HAVE_DEBUG
+ template <class U> Pt operator+(U x) { return Pt(p+x,n,start); }
+ template <class U> Pt operator-(U x) { return Pt(p-x,n,start); }
+#else
+ template <class U> Pt operator+(U x) { return Pt(p+x,0); }
+ template <class U> Pt operator-(U x) { return Pt(p-x,0); }
+#endif
+};
+
+//template <class T> class P : Pt<T> {};
+//a reference counting pointer class
+template <class T> class P {
+public:
+#define INCR if (p) p->refcount++;
+#define DECR if (p) {p->refcount--; if (!p->refcount) delete p;}
+ T *p;
+ P() : p(0) {}
+ P(T *_p) { p = _p; INCR; }
+ P(const P<T> &_p) { p = _p.p; INCR; }
+ P<T> &operator =(T * _p) { DECR; p=_p; INCR; return *this; }
+ P<T> &operator =(P<T> _p) { DECR; p=_p.p; INCR; return *this; }
+ bool operator ==(P<T> _p) { return p == _p.p; }
+ bool operator !=(P<T> _p) { return p != _p.p; }
+ ~P() { DECR; }
+ bool operator !(){ return !p; }
+ operator bool() { return !!p; }
+ T &operator *() { return *p; }
+ T *operator ->() { return p; }
+//#undef INCR
+//#undef DECR
+};
+
+#ifndef IS_BRIDGE
+extern "C" void *gfmalloc(size_t n);
+extern "C" void gffree(void *p);
+inline void *::operator new (size_t n) { return gfmalloc(n); }
+inline void *::operator new[] (size_t n) { return gfmalloc(n); }
+inline void ::operator delete (void *p) { gffree(p); }
+inline void ::operator delete[] (void *p) { gffree(p); }
+#endif
+
+#define STACK_ARRAY(T,V,N) T V##_foo[N]; Pt<T> V(V##_foo,N);
+#define ARRAY_NEW(T,N) (Pt<T>((T *)new T[N],N))
+
+void gfmemcopy(uint8 *out, const uint8 *in, int n);
+template <class T> inline void COPY(Pt<T> dest, Pt<T> src, int n) {
+ src.will_use(n); dest.will_use(n);
+ gfmemcopy((uint8*)dest,(const uint8*)src,n*sizeof(T));
+}
+template <class T> inline void CLEAR(Pt<T> dest, int n) {
+ dest.will_use(n);
+ memset(dest,0,n*sizeof(T));
+}
+template <class T> static void memswap (Pt<T> a, Pt<T> b, int n) {
+ STACK_ARRAY(T,c,n); COPY(c,a,n); COPY(a,b,n); COPY(b,c,n);
+}
+
+//****************************************************************
+// my own little Ruby <-> C++ layer
+
+struct Arg { Ruby a; };
+struct ArgList { int n; Pt<Arg> v; };
+static inline bool INTEGER_P(Ruby x) {return FIXNUM_P(x)||TYPE(x)==T_BIGNUM;}
+static inline bool FLOAT_P(Ruby x) {return TYPE(x)==T_FLOAT;}
+#define INT(x) TO(int32,x)
+#define TO(t,x) convert(x,(t*)0)
+
+// not using NUM2INT because Ruby can convert Symbol to int
+// (by compatibility with Ruby 1.4)
+static inline int32 convert(Ruby x, int32 *foo) {
+ if (INTEGER_P(x)) return NUM2INT(x);
+ if (FLOAT_P(x)) return NUM2INT(rb_funcall(x,SI(round),0));
+ RAISE("expected Integer or Float (got %s)",
+ rb_str_ptr(rb_funcall(x,SI(inspect),0)));
+}
+static int16 convert(Ruby x, int16 *foo) {
+ int v = INT(x);
+ if (v<-0x8000 || v>=0x8000) RAISE("value %d is out of range",v);
+ return v;}
+static uint16 convert(Ruby x, uint16 *foo) {
+ int v = INT(x);
+ if (v<0 || v>=0x10000) RAISE("value %d is out of range",v);
+ return v;}
+static bool convert(Ruby x, bool *foo) {
+ if (x==Qtrue) return true;
+ if (x==Qfalse) return false;
+ switch (TYPE(x)) {
+ case T_FIXNUM: case T_BIGNUM: case T_FLOAT: return !!INT(x);
+ default: RAISE("can't convert to bool");
+ }
+}
+
+static uint64 convert(Ruby val, uint64 *foo) {
+ if (FIXNUM_P(val)) return (uint64)FIX2LONG(val);
+ if (TYPE(val)!=T_BIGNUM) RAISE("type error");
+ uint64 v = (uint64)NUM2UINT(rb_funcall(val,SI(>>),1,INT2FIX(32))) << 32;
+ return v + NUM2UINT(rb_funcall(val,SI(&),1,UINT2NUM(0xffffffff)));}
+static int64 convert(Ruby val, int64 *foo) {
+ if (FIXNUM_P(val)) return (int64)FIX2LONG(val);
+ if (TYPE(val)!=T_BIGNUM) RAISE("type error");
+ int64 v = (int64)NUM2INT(rb_funcall(val,SI(>>),1,INT2FIX(32))) << 32;
+ return v + NUM2UINT(rb_funcall(val,SI(&),1,UINT2NUM(0xffffffff)));}
+
+static Ruby gf_ull2num(uint64 val) {
+ return rb_funcall(
+ rb_funcall(UINT2NUM((uint32)(val>>32)),SI(<<),1,INT2FIX(32)),
+ SI(+),1,UINT2NUM((uint32)val));}
+static Ruby gf_ll2num(int64 val) {
+ return rb_funcall(
+ rb_funcall(INT2NUM((int32)(val>>32)),SI(<<),1,INT2FIX(32)),
+ SI(+),1,UINT2NUM((uint32)val));}
+
+static long convert(Ruby x, long *foo) {
+ return sizeof(long)==sizeof(int32) ?
+ convert(x,(int32 *)0) :
+ convert(x,(int64 *)0);
+}
+static float64 convert(Ruby x, float64 *foo) {
+ if (INTEGER_P(x)) return INT(x);
+ if (TYPE(x)!=T_FLOAT) RAISE("not a Float");
+ return ((RFloat*)x)->value;}
+static float32 convert(Ruby x, float32 *foo) {
+ return (float32) convert(x,(float64 *)0);}
+typedef Ruby Symbol, Array, String, Integer;
+static Ruby convert(Ruby x, Ruby *bogus) { return x; }
+typedef Ruby (*RMethod)(...); /* !@#$ fishy */
+
+#define BUILTIN_SYMBOLS(MACRO) \
+ MACRO(_grid,"grid") MACRO(_bang,"bang") MACRO(_float,"float") \
+ MACRO(_list,"list") MACRO(_sharp,"#") \
+ MACRO(iv_outlets,"@outlets") \
+ MACRO(iv_ninlets,"@ninlets") \
+ MACRO(iv_noutlets,"@noutlets")
+extern struct BuiltinSymbols {
+#define FOO(_sym_,_str_) Ruby _sym_;
+BUILTIN_SYMBOLS(FOO)
+#undef FOO
+} bsym;
+
+//****************************************************************
+// CObject is the base class for C++ classes that get exported to Ruby.
+// BTW: It's quite convenient to have virtual-methods in the base class
+// because otherwise the vtable* isn't at the beginning of the object
+// and that's pretty confusing to a lot of people, especially when simple
+// casting causes a pointer to change its value.
+struct CObject {
+#define OBJECT_MAGIC 1618033989
+ int32 magic;
+ int32 refcount;
+ Ruby rself; // point to Ruby peer
+ CObject() : magic(OBJECT_MAGIC), refcount(0), rself(0) {}
+ void check_magic () {
+ if (magic != OBJECT_MAGIC) {
+ fprintf(stderr,"Object memory corruption! (ask the debugger)\n");
+ for (int i=-2; i<=2; i++)
+ fprintf(stderr,"this[%d]=0x%08x\n",i,((int*)this)[i]);
+ raise(11);
+ }
+ }
+ virtual ~CObject() { magic = 0xDEADBEEF; }
+ virtual void mark() {} // not used for now
+};
+void CObject_free (void *);
+
+// you shouldn't use MethodDecl directly (used by source_filter.rb)
+struct MethodDecl { const char *selector; RMethod method; };
+void define_many_methods(Ruby rself, int n, MethodDecl *methods);
+
+extern Ruby mGridFlow, cFObject, cGridObject, cFormat;
+
+//****************************************************************
+// a Dim is a list of dimensions that describe the shape of a grid
+\class Dim < CObject
+struct Dim : CObject {
+ static const int MAX_DIMENSIONS=16; // maximum number of dimensions in a grid
+ int n;
+ Pt<int32> v; // safe pointer
+ int32 v2[MAX_DIMENSIONS]; // real stuff
+ void check(); // test invariants
+ Dim(int n, Pt<int32> v) {
+ this->v = Pt<int32>(v2,MAX_DIMENSIONS);
+ this->n = n;
+ COPY(this->v,v,n); check();
+ }
+ Dim(int n, int32* v) {
+ this->v = Pt<int32>(v2,MAX_DIMENSIONS);
+ this->n = n;
+ COPY(this->v,Pt<int32>(v,n),n); check();
+ }
+ Dim() {v=Pt<int32>(v2,MAX_DIMENSIONS); n=0; check();}
+ Dim(int a) {v=Pt<int32>(v2,MAX_DIMENSIONS); n=1;v[0]=a; check();}
+ Dim(int a,int b) {v=Pt<int32>(v2,MAX_DIMENSIONS); n=2;v[0]=a;v[1]=b; check();}
+ Dim(int a,int b,int c){v=Pt<int32>(v2,MAX_DIMENSIONS); n=3;v[0]=a;v[1]=b;v[2]=c;check();}
+ int count() {return n;}
+ int get(int i) { return v[i]; }
+ int32 prod(int start=0, int end=-1) {
+ if (end<0) end+=n;
+ int32 tot=1;
+ for (int i=start; i<=end; i++) tot *= v[i];
+ return tot;
+ }
+ char *to_s();
+ bool equal(P<Dim> o) {
+ if (n!=o->n) return false;
+ for (int i=0; i<n; i++) if (v[i]!=o->v[i]) return false;
+ return true;
+ }
+};
+\end class Dim
+
+//****************************************************************
+//BitPacking objects encapsulate optimised loops of conversion
+struct BitPacking;
+// those are the types of the optimised loops of conversion
+// inputs are const
+struct Packer {
+#define FOO(S) void (*as_##S)(BitPacking *self, int n, Pt<S> in, Pt<uint8> out);
+EACH_INT_TYPE(FOO)
+#undef FOO
+};
+struct Unpacker {
+#define FOO(S) void (*as_##S)(BitPacking *self, int n, Pt<uint8> in, Pt<S> out);
+EACH_INT_TYPE(FOO)
+#undef FOO
+};
+
+\class BitPacking < CObject
+struct BitPacking : CObject {
+ Packer *packer;
+ Unpacker *unpacker;
+ unsigned int endian; // 0=big, 1=little, 2=same, 3=different
+ int bytes;
+ int size;
+ uint32 mask[4];
+ BitPacking(){::abort();} // don't call, but don't remove. sorry.
+ BitPacking(int endian, int bytes, int size, uint32 *mask,
+ Packer *packer=0, Unpacker *unpacker=0);
+ bool is_le();
+ bool eq(BitPacking *o);
+ \decl void initialize(Ruby foo1, Ruby foo2, Ruby foo3);
+ \decl String pack2(String ins, String outs=Qnil);
+ \decl String unpack2(String ins, String outs=Qnil);
+ \decl String to_s();
+// main entrances to Packers/Unpackers
+ template <class T> void pack( int n, Pt<T> in, Pt<uint8> out);
+ template <class T> void unpack(int n, Pt<uint8> in, Pt<T> out);
+};
+\end class
+
+int high_bit(uint32 n);
+int low_bit(uint32 n);
+void swap32 (int n, Pt<uint32> data);
+void swap16 (int n, Pt<uint16> data);
+
+#define NT_UNSIGNED (1<<0)
+#define NT_FLOAT (1<<1)
+#define NT_UNIMPL (1<<2)
+#define NUMBER_TYPE_LIMITS(T,a,b,c) \
+ inline T nt_smallest(T *bogus) {return a;} \
+ inline T nt_greatest(T *bogus) {return b;} \
+ inline T nt_all_ones(T *bogus) {return c;}
+
+NUMBER_TYPE_LIMITS( uint8,0,255,255)
+NUMBER_TYPE_LIMITS( int16,-0x8000,0x7fff,-1)
+NUMBER_TYPE_LIMITS( int32,-0x80000000,0x7fffffff,-1)
+NUMBER_TYPE_LIMITS( int64,-0x8000000000000000LL,0x7fffffffffffffffLL,-1)
+NUMBER_TYPE_LIMITS(float32,-HUGE_VAL,+HUGE_VAL,(RAISE("all_ones"),0))
+NUMBER_TYPE_LIMITS(float64,-HUGE_VAL,+HUGE_VAL,(RAISE("all_ones"),0))
+
+#ifdef HAVE_LITE
+#define NT_NOTLITE NT_UNIMPL
+#define NONLITE(x...)
+#else
+#define NT_NOTLITE 0
+#define NONLITE(x...) x
+#endif
+#define NUMBER_TYPES(MACRO) \
+ MACRO( uint8, 8,NT_UNSIGNED, "u8,b") MACRO( int8, 8,NT_UNIMPL, "i8") \
+ MACRO( uint16,16,NT_UNSIGNED|NT_UNIMPL, "u16") MACRO(int16,16,0, "i16,s") \
+ MACRO( uint32,32,NT_UNSIGNED|NT_UNIMPL, "u32") MACRO(int32,32,0, "i32,i") \
+ MACRO( uint64,64,NT_UNSIGNED|NT_UNIMPL, "u64") MACRO(int64,64,NT_NOTLITE, "i64,l") \
+ MACRO(float32,32,NT_NOTLITE|NT_FLOAT, "f32,f") \
+ MACRO(float64,64,NT_NOTLITE|NT_FLOAT, "f64,d")
+
+enum NumberTypeE {
+#define FOO(_sym_,args...) _sym_##_e,
+NUMBER_TYPES(FOO)
+#undef FOO
+ number_type_table_end
+};
+
+#define FOO(_type_) \
+inline NumberTypeE NumberTypeE_type_of(_type_ &x) { \
+ return _type_##_e; }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+
+\class NumberType < CObject
+struct NumberType : CObject {
+ Ruby /*Symbol*/ sym;
+ const char *name;
+ int size;
+ int flags;
+ const char *aliases;
+ NumberTypeE index;
+ NumberType(const char *name_, int size_, int flags_, const char *aliases_) :
+ name(name_), size(size_), flags(flags_), aliases(aliases_) {}
+ \decl Symbol sym_m();
+ \decl int size_m();
+ \decl int flags_m();
+ \decl int index_m();
+};
+\end class
+
+NumberTypeE NumberTypeE_find (Ruby sym);
+
+#define TYPESWITCH(T,C,E) switch (T) { \
+ case uint8_e: C(uint8) break; case int16_e: C(int16) break; \
+ case int32_e: C(int32) break; NONLITE(case int64_e: C(int64) break; \
+ case float32_e: C(float32) break; case float64_e: C(float64) break;) \
+ default: E; RAISE("type '%s' not available here",number_type_table[T].sym);}
+#define TYPESWITCH_NOFLOAT(T,C,E) switch (T) { \
+ case uint8_e: C(uint8) break; case int16_e: C(int16) break; \
+ case int32_e: C(int32) break; NONLITE(case int64_e: C(int64) break;)\
+ default: E; RAISE("type '%s' not available here",number_type_table[T].sym);}
+
+// Numop objects encapsulate optimised loops of simple operations
+
+enum LeftRight { at_left, at_right };
+
+template <class T>
+struct NumopOn : CObject {
+ // Function Vectorisations
+ typedef void (*Map )( int n, T *as, T b ); Map op_map;
+ typedef void (*Zip )( int n, T *as, T *bs); Zip op_zip;
+ typedef void (*Fold)(int an, int n, T *as, T *bs); Fold op_fold;
+ typedef void (*Scan)(int an, int n, T *as, T *bs); Scan op_scan;
+ // Algebraic Properties (those involving simply numeric types)
+ typedef bool (*AlgebraicCheck)(T x, LeftRight side);
+ // neutral: right: forall y {f(x,y)=x}; left: forall x {f(x,y)=y};
+ // absorbent: right: exists a forall y {f(x,y)=a}; ...
+ AlgebraicCheck is_neutral, is_absorbent;
+ NumopOn(Map m, Zip z, Fold f, Scan s, AlgebraicCheck n, AlgebraicCheck a) :
+ op_map(m), op_zip(z), op_fold(f), op_scan(s),
+ is_neutral(n), is_absorbent(a) {}
+ NumopOn() {}
+ NumopOn(const NumopOn &z) {
+ op_map = z.op_map; op_zip = z.op_zip;
+ op_fold = z.op_fold; op_scan = z.op_scan;
+ is_neutral = z.is_neutral; is_absorbent = z.is_absorbent; }
+};
+
+// semigroup property: associativity: f(a,f(b,c))=f(f(a,b),c)
+#define OP_ASSOC (1<<0)
+// abelian property: commutativity: f(a,b)=f(b,a)
+#define OP_COMM (1<<1)
+
+\class Numop < CObject
+struct Numop : CObject {
+ Ruby /*Symbol*/ sym;
+ const char *name;
+ int flags;
+//private:
+#define FOO(T) \
+ NumopOn<T> on_##T; \
+ NumopOn<T> *on(T &foo) { \
+ if (!on_##T.op_map) \
+ RAISE("operator %s does not support type "#T,rb_sym_name(sym)); \
+ return &on_##T;}
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+//public:
+ template <class T> inline void map(int n, Pt<T> as, T b) {
+ as.will_use(n);
+ on(*as)->op_map(n,(T *)as,b);}
+ template <class T> inline void zip(int n, Pt<T> as, Pt<T> bs) {
+ as.will_use(n);
+ bs.will_use(n);
+ on(*as)->op_zip(n,(T *)as,(T *)bs);}
+ template <class T> inline void fold(int an, int n, Pt<T> as, Pt<T> bs) {
+ as.will_use(an);
+ bs.will_use(an*n);
+ typename NumopOn<T>::Fold f = on(*as)->op_fold;
+ if (!f) RAISE("operator %s does not support fold",rb_sym_name(sym));
+ f(an,n,(T *)as,(T *)bs);}
+ template <class T> inline void scan(int an, int n, Pt<T> as, Pt<T> bs) {
+ as.will_use(an);
+ bs.will_use(an*n);
+ typename NumopOn<T>::Scan f = on(*as)->op_scan;
+ if (!f) RAISE("operator %s does not support scan",rb_sym_name(sym));
+ f(an,n,(T *)as,(T *)bs);}
+
+ \decl void map_m (NumberTypeE nt, int n, String as, String b);
+ \decl void zip_m (NumberTypeE nt, int n, String as, String bs);
+ \decl void fold_m (NumberTypeE nt, int an, int n, String as, String bs);
+ \decl void scan_m (NumberTypeE nt, int an, int n, String as, String bs);
+
+ Numop(Ruby /*Symbol*/ sym_, const char *name_,
+#define FOO(T) NumopOn<T> op_##T,
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ int flags_) : sym(sym_), name(name_), flags(flags_) {
+#define FOO(T) on_##T = op_##T;
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ }
+};
+\end class
+
+extern NumberType number_type_table[];
+extern Ruby number_type_dict; // GridFlow.@number_type_dict={}
+extern Ruby op_dict; // GridFlow.@op_dict={}
+
+static inline NumberTypeE convert(Ruby x, NumberTypeE *bogus) {
+ return NumberTypeE_find(x);
+}
+
+#ifndef IS_BRIDGE
+static Numop *convert(Ruby x, Numop **bogus) {
+ Ruby s = rb_hash_aref(rb_ivar_get(mGridFlow,SI(@op_dict)),x);
+ if (s==Qnil) RAISE("expected two-input-operator");
+ return FIX2PTR(Numop,s);
+}
+#endif
+
+// ****************************************************************
+\class Grid < CObject
+struct Grid : CObject {
+ P<Dim> dim;
+ NumberTypeE nt;
+ void *data;
+ void *rdata;
+ Grid(P<Dim> dim, NumberTypeE nt, bool clear=false) : dim(0), nt(int32_e), data(0) {
+ if (!dim) RAISE("hell");
+ init(dim,nt);
+ if (clear) {int size = bytes(); CLEAR(Pt<char>((char *)data,size),size);}
+ }
+ Grid(Ruby x) : dim(0), nt(int32_e), data(0) { init_from_ruby(x); }
+ Grid(int n, Ruby *a, NumberTypeE nt=int32_e) : dim(0), nt(int32_e), data(0) {
+ init_from_ruby_list(n,a,nt);
+ }
+ int32 bytes() { return dim->prod()*number_type_table[nt].size/8; }
+ P<Dim> to_dim () { return new Dim(dim->prod(),(Pt<int32>)*this); }
+#define FOO(type) \
+ operator type *() { return (type *)data; } \
+ operator Pt<type>() { return Pt<type>((type *)data,dim->prod()); }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+ Grid *dup () {
+ Grid *foo=new Grid(dim,nt);
+ memcpy(foo->data,data,bytes());
+ return foo;
+ }
+ ~Grid() {if (rdata) delete[] (uint8 *)rdata;}
+private:
+ void init(P<Dim> dim, NumberTypeE nt) {
+ this->dim = dim;
+ this->nt = nt;
+ rdata = dim ? new int64[1+(bytes()+7)/8] : 0;
+ int align = ((long)rdata) & 7;
+ data = (char *)rdata + ((8-align)&7);
+ //fprintf(stderr,"rdata=%p data=%p align=%d\n",rdata,data,align);
+ }
+ void init_from_ruby(Ruby x);
+ void init_from_ruby_list(int n, Ruby *a, NumberTypeE nt=int32_e);
+};
+\end class Grid
+
+static inline Grid *convert (Ruby r, Grid **bogus) {
+ return r ? new Grid(r) : 0;
+}
+
+// DimConstraint interface:
+// return if d is acceptable
+// else RAISE with proper descriptive message
+typedef void (*DimConstraint)(P<Dim> d);
+
+struct PtrGrid : public P<Grid> {
+ DimConstraint dc;
+ void constrain(DimConstraint dc_) { dc=dc_; }
+ P<Grid> next;
+ PtrGrid() : P<Grid>(), dc(0), next(0) {}
+ PtrGrid(const PtrGrid &_p) : P<Grid>(), dc(0), next(0) {dc=_p.dc; p=_p.p; INCR;}
+ PtrGrid &operator =( Grid *_p) {if(dc&&p)dc(_p->dim); DECR; p=_p; INCR; return *this;}
+ PtrGrid &operator =(P<Grid> _p) {if(dc&&p)dc(_p->dim); DECR; p=_p.p; INCR; return *this;}
+ PtrGrid &operator =(PtrGrid _p) {if(dc&&p)dc(_p->dim); DECR; p=_p.p; INCR; return *this;}
+};
+
+#ifndef IS_BRIDGE
+static inline P<Dim> convert(Ruby x, P<Dim> *foo) {
+ Grid *d = convert(x,(Grid **)0);
+ if (!d) RAISE("urgh");
+ if (d->dim->n!=1) RAISE("dimension list must have only one dimension itself");
+ return new Dim(d->dim->v[0],(int32 *)(d->data));
+}
+
+static inline PtrGrid convert(Ruby x, PtrGrid *foo) {
+ PtrGrid pg;
+ pg = convert(x,(Grid **)0);
+ return pg;
+}
+#endif
+
+//****************************************************************
+// GridInlet represents a grid-aware inlet
+
+// four-part macro for defining the behaviour of a gridinlet in a class
+// C:Class I:Inlet
+#define GRID_INLET(C,I) \
+ template <class T> void C::grinw_##I (GridInlet *in, int n, Pt<T> data) { \
+ ((C*)(in->parent))->grin_##I(in,n,data); } \
+ template <class T> void C::grin_##I (GridInlet *in, int n, Pt<T> data) { \
+ if (n==-1)
+#define GRID_ALLOC else if (n==-3)
+#define GRID_FLOW else if (n>=0)
+#define GRID_FINISH else if (n==-2)
+#define GRID_END }
+
+/* macro for defining a gridinlet's behaviour as just storage (no backstore) */
+// V is a PtrGrid instance-var
+#define GRID_INPUT(C,I,V) \
+GRID_INLET(C,I) { V=new Grid(in->dim,NumberTypeE_type_of(*data)); } \
+GRID_FLOW { COPY((Pt<T>)*(V)+in->dex, data, n); } GRID_FINISH
+
+// macro for defining a gridinlet's behaviour as just storage (with backstore)
+// V is a PtrGrid instance-var
+#define GRID_INPUT2(C,I,V) \
+ GRID_INLET(C,I) { \
+ if (is_busy_except(in)) { \
+ V.next = new Grid(in->dim,NumberTypeE_type_of(*data)); \
+ } else V= new Grid(in->dim,NumberTypeE_type_of(*data)); \
+ } GRID_FLOW { \
+ COPY(((Pt<T>)*(V.next?V.next.p:&*V.p))+in->dex, data, n); \
+ } GRID_FINISH
+
+typedef struct GridInlet GridInlet;
+typedef struct GridHandler {
+#define FOO(T) \
+ void (*flow_##T)(GridInlet *in, int n, Pt<T> data); \
+ void flow(GridInlet *in, int n, Pt<T> data) const { \
+ assert(flow_##T); flow_##T(in,n,data); }
+EACH_NUMBER_TYPE(FOO)
+#undef FOO
+} GridHandler;
+
+typedef struct GridObject GridObject;
+\class GridInlet < CObject
+struct GridInlet : CObject {
+ GridObject *parent;
+ const GridHandler *gh;
+ GridObject *sender;
+ P<Dim> dim;
+ NumberTypeE nt;
+ int dex;
+ PtrGrid buf;// factor-chunk buffer
+ int bufi; // buffer index: how much of buf is filled
+ int mode; // 0=ignore; 4=ro; 6=rw
+
+ int allocfactor,allocmin,allocmax,allocn;
+ Pt<uint8> alloc;
+
+// methods
+ GridInlet(GridObject *parent_, const GridHandler *gh_) :
+ parent(parent_), gh(gh_), sender(0),
+ dim(0), nt(int32_e), dex(0), bufi(0), mode(4) {}
+ ~GridInlet() {}
+ void set_factor(int factor);
+ void set_mode(int mode_) { mode=mode_; }
+ int32 factor() {return buf?buf->dim->prod():1;}
+ Ruby begin(int argc, Ruby *argv);
+
+ // n=-1 is begin, and n=-2 is _finish_. the name "end" is now used
+ // as an end-marker for inlet definitions... sorry for the confusion
+ // GF-0.8: n=-3 is alloc.
+ template <class T> void flow(int mode, int n, Pt<T> data);
+ void end(); // this one ought to be called finish().
+ void from_ruby_list(int argc, Ruby *argv, NumberTypeE nt=int32_e) {
+ Grid t(argc,argv,nt); from_grid(&t);
+ }
+ void from_ruby(int argc, Ruby *argv) {
+ Grid t(argv[0]); from_grid(&t);
+ }
+ void from_grid(Grid *g);
+ bool supports_type(NumberTypeE nt);
+private:
+ template <class T> void from_grid2(Grid *g, T foo);
+};
+\end class GridInlet
+
+//****************************************************************
+// for use by source_filter.rb ONLY (for \grin and \classinfo)
+
+// C is for class, I for inlet number
+// GRIN1 : int32 only
+// GRIN4 : all types
+// GRIN2 : integers only; no floats
+// GRINF : floats only; no integers
+#ifndef HAVE_LITE
+#define GRIN(TB,TS,TI,TL,TF,TD) {TB,TS,TI,TL,TF,TD}
+#else
+#define GRIN(TB,TS,TI,TL,TF,TD) {TB,TS,TI}
+#endif // HAVE_LITE
+
+#define GRIN1(C,I) GRIN(0,0,C::grinw_##I,0,0,0)
+#define GRIN4(C,I) GRIN(C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I)
+#define GRIN2(C,I) GRIN(C::grinw_##I,C::grinw_##I,C::grinw_##I,C::grinw_##I,0,0)
+#define GRINF(C,I) GRIN(0,0,0,0,C::grinw_##I,C::grinw_##I)
+
+struct FClass { // 0.7.8: removed all GridObject-specific stuff.
+ void *(*allocator)(); // returns a new C++ object
+ void (*startup)(Ruby rself); // initializer for the Ruby class
+ const char *name; // C++/Ruby name (not PD name)
+ int methodsn; MethodDecl *methods; // C++ -> Ruby methods
+};
+
+//****************************************************************
+// GridOutlet represents a grid-aware outlet
+\class GridOutlet < CObject
+struct GridOutlet : CObject {
+// number of (minimum,maximum) BYTES to send at once
+// starting with version 0.8, this is amount of BYTES, not amount of NUMBERS.
+ static const int MIN_PACKET_SIZE = 1<<8;
+ static const int MAX_PACKET_SIZE = 1<<12;
+// those are set only once
+ GridObject *parent; // not a P<> because of circular refs
+ P<Dim> dim; // dimensions of the grid being sent
+ NumberTypeE nt;
+ PtrGrid buf; // temporary buffer
+ bool frozen; // is the "begin" phase finished?
+ std::vector<GridInlet *> inlets; // which inlets are we connected to
+// those are updated during transmission
+ int dex; // how many numbers were already sent in this connection
+ int bufi; // number of bytes used in the buffer
+// methods
+ GridOutlet(GridObject *parent_, int woutlet, P<Dim> dim_, NumberTypeE nt_=int32_e) :
+ parent(parent_), dim(dim_), nt(nt_), frozen(false), dex(0), bufi(0) {
+ int ntsz = number_type_table[nt].size;
+ buf=new Grid(new Dim(MAX_PACKET_SIZE/*/ntsz*/), nt);
+ begin(woutlet,dim,nt);
+ }
+ ~GridOutlet() {}
+ void callback(GridInlet *in);
+
+ // send/send_direct: data belongs to caller, may be stack-allocated,
+ // receiver doesn't modify the data; in send(), there is buffering;
+ // in send_direct(), there is not. When switching from buffered to
+ // unbuffered mode, flush() must be called
+ template <class T> void send(int n, Pt<T> data);
+ void flush(); // goes with send();
+
+ // give: data must be dynamically allocated as a whole: the data
+ // will be deleted eventually, and should not be used by the caller
+ // beyond the call to give().
+ template <class T> void give(int n, Pt<T> data);
+
+ // third way to send (upcoming, in GF-0.8.??) is called "ask".
+ template <class T> void ask(int &n, Pt<T> &data, int factor, int min, int max);
+
+private:
+ void begin(int woutlet, P<Dim> dim, NumberTypeE nt=int32_e);
+ template <class T> void send_direct(int n, Pt<T> data);
+ void end() {
+ flush();
+ for (uint32 i=0; i<inlets.size(); i++) inlets[i]->end();
+ dim=0;
+ }
+};
+\end class GridOutlet
+
+//****************************************************************
+
+typedef struct BFObject BFObject; // Pd t_object or something
+
+// represents objects that have inlets/outlets
+\class FObject < CObject
+struct FObject : CObject {
+ BFObject *bself; // point to PD peer
+ uint64 total_time;
+ FObject() : bself(0), total_time(0) {}
+ const char *args() {
+ Ruby s=rb_funcall(rself,SI(args),0);
+ if (s==Qnil) return 0;
+ return rb_str_ptr(s);
+ }
+ \decl Ruby total_time_get();
+ \decl Ruby total_time_set(Ruby x);
+ \decl void send_in (...);
+ \decl void send_out (...);
+ \decl void delete_m ();
+};
+\end class FObject
+
+\class GridObject < FObject
+struct GridObject : FObject {
+ std::vector<P<GridInlet> > in;
+ P<GridOutlet> out;
+ // Make sure you distinguish #close/#delete, and C++'s delete. The first
+ // two are quite equivalent and should never make an object "crashable".
+ // C++'s delete is called by Ruby's garbage collector or by PureData's delete.
+ GridObject() {}
+ ~GridObject() {check_magic();}
+ bool is_busy_except(P<GridInlet> gin) {
+ for (uint32 i=0; i<in.size(); i++)
+ if (in[i] && in[i]!=gin && in[i]->dim) return true;
+ return false;
+ }
+ \decl Ruby method_missing(...);
+ \decl Array inlet_dim(int inln);
+ \decl Symbol inlet_nt(int inln);
+ \decl void inlet_set_factor(int inln, int factor);
+ \decl void send_out_grid_begin(int outlet, Array dim, NumberTypeE nt=int32_e);
+ \decl void send_out_grid_flow (int outlet, String buf, NumberTypeE nt=int32_e);
+};
+\end class GridObject
+
+uint64 gf_timeofday();
+extern "C" void Init_gridflow ();
+void gfpost(const char *fmt, ...);
+extern Numop *op_add,*op_sub,*op_mul,*op_div,*op_mod,*op_shl,*op_and,*op_put;
+
+#define INFO(OBJ) rb_str_ptr(rb_funcall(OBJ->rself,SI(info),0))
+//#define INFO(OBJ) "(bleh)"
+#define NOTEMPTY(_a_) if (!(_a_)) RAISE("in [%s], '%s' is empty",INFO(this), #_a_);
+#define SAME_TYPE(_a_,_b_) \
+ if ((_a_)->nt != (_b_)->nt) RAISE("%s: same type please (%s has %s; %s has %s)", \
+ INFO(this), \
+ #_a_, number_type_table[(_a_)->nt].name, \
+ #_b_, number_type_table[(_b_)->nt].name);
+static void SAME_DIM(int n, P<Dim> a, int ai, P<Dim> b, int bi) {
+ if (ai+n > a->n) RAISE("left hand: not enough dimensions");
+ if (bi+n > b->n) RAISE("right hand: not enough dimensions");
+ for (int i=0; i<n; i++) {
+ if (a->v[ai+i] != b->v[bi+i]) {
+ RAISE("mismatch: left dim #%d is %d, right dim #%d is %d",
+ ai+i, a->v[ai+i],
+ bi+i, b->v[bi+i]);}}}
+
+// a stack for the profiler, etc.
+#define GF_STACK_MAX 256
+struct GFStack {
+ struct GFStackFrame {
+ FObject *o;
+ void *bp; // a pointer into system stack
+ uint64 time;
+ }; // sizeof() == 16 (in 32-bit mode)
+ GFStackFrame s[GF_STACK_MAX];
+ int n;
+ GFStack() { n = 0; }
+ void push (FObject *o) __attribute__((noinline));
+ void pop () __attribute__((noinline));
+};
+extern GFStack gf_stack;
+struct GFStackMarker {
+ int n;
+ bool flag;
+ GFStackMarker(FObject *o) { n = gf_stack.n; gf_stack.push(o); flag=true; }
+ ~GFStackMarker() { while (gf_stack.n != n) gf_stack.pop(); }
+ bool once () {
+ if (flag) { flag=false; return true; } else return false;
+ }
+};
+
+typedef GridObject Format;
+
+#endif // __GF_GRID_H
diff --git a/externals/gridflow/base/main.c b/externals/gridflow/base/main.c
new file mode 100644
index 00000000..5cdd92f4
--- /dev/null
+++ b/externals/gridflow/base/main.c
@@ -0,0 +1,647 @@
+/*
+ $Id: main.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include "grid.h.fcs"
+#include "../config.h"
+#include <assert.h>
+#include <limits.h>
+
+BuiltinSymbols bsym;
+GFStack gf_stack;
+Ruby mGridFlow;
+Ruby cFObject;
+
+extern "C"{
+void rb_raise0(
+const char *file, int line, const char *func, VALUE exc, const char *fmt, ...) {
+ va_list args;
+ char buf[BUFSIZ];
+ va_start(args,fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ buf[BUFSIZ-1]=0;
+ va_end(args);
+ VALUE e = rb_exc_new2(exc, buf);
+ char buf2[BUFSIZ];
+ snprintf(buf2, BUFSIZ, "%s:%d:in `%s'", file, line, func);
+ buf2[BUFSIZ-1]=0;
+ VALUE ary = rb_funcall(e,SI(caller),0);
+ if (gf_stack.n) {
+ rb_funcall(ary,SI(unshift),2,rb_str_new2(buf2),
+ rb_str_new2(INFO(gf_stack.s[gf_stack.n-1].o)));
+ } else {
+ rb_funcall(ary,SI(unshift),1,rb_str_new2(buf2));
+ }
+ rb_funcall(e,SI(set_backtrace),1,ary);
+ rb_exc_raise(e);
+}};
+
+Ruby rb_ary_fetch(Ruby rself, int i) {
+ Ruby argv[] = { INT2NUM(i) };
+ return rb_ary_aref(COUNT(argv),argv,rself);
+}
+
+//----------------------------------------------------------------
+// CObject
+
+static void CObject_mark (void *z) {}
+void CObject_free (void *foo) {
+ CObject *self = (CObject *)foo;
+ self->check_magic();
+ if (!self->rself) {
+ fprintf(stderr,"attempt to free object that has no rself\n");
+ abort();
+ }
+ self->rself = 0; /* paranoia */
+ delete self;
+}
+
+//----------------------------------------------------------------
+// Dim
+
+void Dim::check() {
+ if (n>MAX_DIMENSIONS) RAISE("too many dimensions");
+ for (int i=0; i<n; i++) if (v[i]<0) RAISE("Dim: negative dimension");
+}
+
+// !@#$ big leak machine?
+// returns a string like "Dim[240,320,3]"
+char *Dim::to_s() {
+ // if you blow 256 chars it's your own fault
+ char buf[256];
+ char *s = buf;
+ s += sprintf(s,"Dim[");
+ for(int i=0; i<n; i++) s += sprintf(s,"%s%d", ","+!i, v[i]);
+ s += sprintf(s,"]");
+ return strdup(buf);
+}
+
+//----------------------------------------------------------------
+\class FObject < CObject
+
+static void FObject_prepare_message(int &argc, Ruby *&argv, Ruby &sym, FObject *foo=0) {
+ if (argc<1) {
+ sym = bsym._bang;
+ } else if (argc>1 && !SYMBOL_P(*argv)) {
+ sym = bsym._list;
+ } else if (INTEGER_P(*argv)||FLOAT_P(*argv)) {
+ sym = bsym._float;
+ } else if (SYMBOL_P(*argv)) {
+ sym = *argv;
+ argc--, argv++;
+ } else if (argc==1 && TYPE(*argv)==T_ARRAY) {
+ sym = bsym._list;
+ argc = rb_ary_len(*argv);
+ argv = rb_ary_ptr(*argv);
+ } else {
+ RAISE("%s received bad message: argc=%d; argv[0]=%s",foo?INFO(foo):"", argc,
+ argc ? rb_str_ptr(rb_inspect(argv[0])) : "");
+ }
+}
+
+struct Helper {
+ int argc;
+ Ruby *argv;
+ FObject *self;
+ Ruby rself;
+ int n; // stack level
+};
+
+static Ruby GridFlow_handle_braces(Ruby rself, Ruby argv);
+
+// inlet #-1 is reserved for SystemInlet messages
+// inlet #-2 is for inlet #0 messages that happen at start time
+static void send_in_2 (Helper *h) { PROF(h->self) {
+ int argc = h->argc;
+ Ruby *argv = h->argv;
+ if (h->argc<1) RAISE("not enough args");
+ int inlet = INT(argv[0]);
+ argc--, argv++;
+ Ruby foo;
+ if (argc==1 && TYPE(argv[0])==T_STRING /* && argv[0] =~ / / */) {
+ foo = rb_funcall(mGridFlow,SI(parse),1,argv[0]);
+ argc = rb_ary_len(foo);
+ argv = rb_ary_ptr(foo);
+ }
+ if (argc>1) {
+ foo = rb_ary_new4(argc,argv);
+ GridFlow_handle_braces(0,foo);
+ argc = rb_ary_len(foo);
+ argv = rb_ary_ptr(foo);
+ }
+ if (inlet==-2) {
+ Array init_messages = rb_ivar_get(h->rself,SI(@init_messages));
+ rb_ary_push(init_messages, rb_ary_new4(argc,argv));
+ inlet=0;
+ }
+ if (inlet<0 || inlet>9 /*|| inlet>real_inlet_max*/)
+ if (inlet!=-3 && inlet!=-1) RAISE("invalid inlet number: %d", inlet);
+ Ruby sym;
+ FObject_prepare_message(argc,argv,sym,h->self);
+// if (rb_const_get(mGridFlow,SI(@verbose))==Qtrue) gfpost m.inspect
+ char buf[256];
+ if (inlet==-1) sprintf(buf,"_sys_%s",rb_sym_name(sym));
+ else sprintf(buf,"_%d_%s",inlet,rb_sym_name(sym));
+ rb_funcall2(h->rself,rb_intern(buf),argc,argv);
+} /* PROF */ }
+
+static void send_in_3 (Helper *h) {
+ while (gf_stack.n > h->n) gf_stack.pop();
+}
+
+\def void send_in (...) {
+ Helper h = {argc,argv,this,rself,gf_stack.n};
+ rb_ensure(
+ (RMethod)send_in_2,(Ruby)&h,
+ (RMethod)send_in_3,(Ruby)&h);
+}
+
+\def void send_out (...) {
+ int n=0;
+ if (argc<1) RAISE("not enough args");
+ int outlet = INT(*argv);
+ if (outlet<0 || outlet>9 /*|| outlet>real_outlet_max*/)
+ RAISE("invalid outlet number: %d",outlet);
+ argc--, argv++;
+ Ruby sym;
+ FObject_prepare_message(argc,argv,sym,this);
+ Ruby noutlets2 = rb_ivar_get(rb_obj_class(rself),SYM2ID(SYM(@noutlets)));
+ if (TYPE(noutlets2)!=T_FIXNUM) {
+ IEVAL(rself,"STDERR.puts inspect");
+ RAISE("don't know how many outlets this has");
+ }
+ int noutlets = INT(noutlets2);
+ //if (outlet<0 || outlet>=noutlets) RAISE("outlet %d does not exist",outlet);
+ // was PROF(0) a hack because of exception-handling problems?
+ PROF(0) {
+ Ruby argv2[argc+2];
+ for (int i=0; i<argc; i++) argv2[2+i] = argv[i];
+ argv2[0] = INT2NUM(outlet);
+ argv2[1] = sym;
+ rb_funcall2(rself,SI(send_out2), argc+2, argv2);
+
+ Ruby ary = rb_ivar_defined(rself,SYM2ID(bsym.iv_outlets)) ?
+ rb_ivar_get(rself,SYM2ID(bsym.iv_outlets)) : Qnil;
+ if (ary==Qnil) goto end;
+ if (TYPE(ary)!=T_ARRAY) RAISE("send_out: expected array");
+ ary = rb_ary_fetch(ary,outlet);
+ if (ary==Qnil) goto end;
+ if (TYPE(ary)!=T_ARRAY) RAISE("send_out: expected array");
+ n = rb_ary_len(ary);
+
+ for (int i=0; i<n; i++) {
+ Ruby conn = rb_ary_fetch(ary,i);
+ Ruby rec = rb_ary_fetch(conn,0);
+ int inl = INT(rb_ary_fetch(conn,1));
+ argv2[0] = INT2NUM(inl);
+ rb_funcall2(rec,SI(send_in),argc+2,argv2);
+ }
+ } /* PROF */
+end:;
+}
+
+Ruby FObject_s_new(Ruby argc, Ruby *argv, Ruby qlass) {
+ Ruby allocator = rb_ivar_defined(qlass,SI(@allocator)) ?
+ rb_ivar_get(qlass,SI(@allocator)) : Qnil;
+ FObject *self;
+ if (allocator==Qnil) {
+ // this is a pure-ruby FObject/GridObject
+ // !@#$ GridObject is in FObject constructor (ugly)
+ self = new GridObject;
+ } else {
+ // this is a C++ FObject/GridObject
+ void*(*alloc)() = (void*(*)())FIX2PTR(void,allocator);
+ self = (FObject *)alloc();
+ }
+ self->check_magic();
+ Ruby keep = rb_ivar_get(mGridFlow, SI(@fobjects));
+ self->bself = 0;
+ Ruby rself = Data_Wrap_Struct(qlass, CObject_mark, CObject_free, self);
+ self->rself = rself;
+ rb_hash_aset(keep,rself,Qtrue); // prevent sweeping
+ rb_funcall2(rself,SI(initialize),argc,argv);
+ return rself;
+}
+
+Ruby FObject_s_install(Ruby rself, Ruby name, Ruby inlets2, Ruby outlets2) {
+ int inlets, outlets;
+ Ruby name2;
+ if (SYMBOL_P(name)) {
+ name2 = rb_funcall(name,SI(to_str),0);
+ } else if (TYPE(name) == T_STRING) {
+ name2 = rb_funcall(name,SI(dup),0);
+ } else {
+ RAISE("expect symbol or string");
+ }
+ inlets = INT(inlets2); if ( inlets<0 || inlets>9) RAISE("...");
+ outlets = INT(outlets2); if (outlets<0 || outlets>9) RAISE("...");
+ rb_ivar_set(rself,SI(@ninlets),INT2NUM(inlets));
+ rb_ivar_set(rself,SI(@noutlets),INT2NUM(outlets));
+ rb_ivar_set(rself,SI(@foreign_name),name2);
+ rb_hash_aset(rb_ivar_get(mGridFlow,SI(@fclasses)), name2, rself);
+ rb_funcall(rself, SI(install2), 1, name2);
+ return Qnil;
+}
+
+\def Ruby total_time_get () {return gf_ull2num(total_time);}
+
+\def Ruby total_time_set (Ruby x) {
+ if (argc<1) RAISE("muh");
+ total_time = TO(uint64,x);
+ return argv[0];
+}
+
+\def void delete_m () {
+ Ruby keep = rb_ivar_get(mGridFlow, SI(@fobjects));
+ rb_funcall(keep,SI(delete),1,rself);
+}
+
+\classinfo
+\end class FObject
+
+/* ---------------------------------------------------------------- */
+/* C++<->Ruby bridge for classes/functions in base/number.c */
+
+static Ruby String_swap32_f (Ruby rself) {
+ int n = rb_str_len(rself)/4;
+ swap32(n,Pt<uint32>((uint32 *)rb_str_ptr(rself),n));
+ return rself;
+}
+
+static Ruby String_swap16_f (Ruby rself) {
+ int n = rb_str_len(rself)/2;
+ swap16(n,Pt<uint16>((uint16 *)rb_str_ptr(rself),n));
+ return rself;
+}
+
+NumberTypeE NumberTypeE_find (Ruby sym) {
+ if (TYPE(sym)!=T_SYMBOL) RAISE("expected symbol (not %s)",
+ rb_str_ptr(rb_inspect(rb_obj_class(sym))));
+ Ruby nt_dict = rb_ivar_get(mGridFlow,SI(@number_type_dict));
+ Ruby v = rb_hash_aref(nt_dict,sym);
+ if (v!=Qnil) return FIX2PTR(NumberType,v)->index;
+ RAISE("unknown number type \"%s\"", rb_sym_name(sym));
+}
+
+/* **************************************************************** */
+\class BitPacking < CObject
+
+\def void initialize(Ruby foo1, Ruby foo2, Ruby foo3) {}
+
+// !@#$ doesn't support number types
+\def String pack2 (String ins, String outs=Qnil) {
+ int n = rb_str_len(ins) / sizeof(int32) / size;
+ Pt<int32> in = Pt<int32>((int32 *)rb_str_ptr(ins),rb_str_len(ins));
+ int bytes2 = n*bytes;
+ Ruby out = outs!=Qnil ? rb_str_resize(outs,bytes2) : rb_str_new("",bytes2);
+ rb_str_modify(out);
+ pack(n,Pt<int32>(in,n),Pt<uint8>((uint8 *)rb_str_ptr(out),bytes2));
+ return out;
+}
+
+// !@#$ doesn't support number types
+\def String unpack2 (String ins, String outs=Qnil) {
+ int n = rb_str_len(argv[0]) / bytes;
+ Pt<uint8> in = Pt<uint8>((uint8 *)rb_str_ptr(ins),rb_str_len(ins));
+ int bytes2 = n*size*sizeof(int32);
+ Ruby out = outs!=Qnil ? rb_str_resize(outs,bytes2) : rb_str_new("",bytes2);
+ rb_str_modify(out);
+ unpack(n,Pt<uint8>((uint8 *)in,bytes2),Pt<int32>((int32 *)rb_str_ptr(out),n));
+ return out;
+}
+
+static Ruby BitPacking_s_new(Ruby argc, Ruby *argv, Ruby qlass) {
+ Ruby keep = rb_ivar_get(mGridFlow, rb_intern("@fobjects"));
+ if (argc!=3) RAISE("bad args");
+ if (TYPE(argv[2])!=T_ARRAY) RAISE("bad mask");
+ int endian = INT(argv[0]);
+ int bytes = INT(argv[1]);
+ Ruby *masks = rb_ary_ptr(argv[2]);
+ uint32 masks2[4];
+ int size = rb_ary_len(argv[2]);
+ if (size<1) RAISE("not enough masks");
+ if (size>4) RAISE("too many masks (%d)",size);
+ for (int i=0; i<size; i++) masks2[i] = NUM2UINT(masks[i]);
+ BitPacking *self = new BitPacking(endian,bytes,size,masks2);
+ Ruby rself = Data_Wrap_Struct(qlass, 0, CObject_free, self);
+ self->rself = rself;
+ rb_hash_aset(keep,rself,Qtrue); // prevent sweeping (leak) (!@#$ WHAT???)
+ rb_funcall2(rself,SI(initialize),argc,argv);
+ return rself;
+}
+
+\classinfo
+\end class BitPacking
+
+void gfpost(const char *fmt, ...) {
+ va_list args;
+ int length;
+ va_start(args,fmt);
+ const int n=256;
+ char post_s[n];
+ length = vsnprintf(post_s,n,fmt,args);
+ if (length<0 || length>=n) sprintf(post_s+n-6,"[...]"); /* safety */
+ va_end(args);
+ rb_funcall(mGridFlow,SI(gfpost2),2,rb_str_new2(fmt),rb_str_new2(post_s));
+}
+
+void define_many_methods(Ruby rself, int n, MethodDecl *methods) {
+ for (int i=0; i<n; i++) {
+ MethodDecl *md = &methods[i];
+ char *buf = strdup(md->selector);
+ if (strlen(buf)>2 && strcmp(buf+strlen(buf)-2,"_m")==0)
+ buf[strlen(buf)-2]=0;
+ rb_define_method(rself,buf,(RMethod)md->method,-1);
+ rb_enable_super(rself,buf);
+ free(buf);
+ }
+}
+
+static Ruby GridFlow_fclass_install(Ruby rself_, Ruby fc_, Ruby super) {
+ FClass *fc = FIX2PTR(FClass,fc_);
+ Ruby rself = super!=Qnil ?
+ rb_define_class_under(mGridFlow, fc->name, super) :
+ rb_funcall(mGridFlow,SI(const_get),1,rb_str_new2(fc->name));
+ define_many_methods(rself,fc->methodsn,fc->methods);
+ rb_ivar_set(rself,SI(@allocator),PTR2FIX((void*)(fc->allocator))); //#!@$??
+ if (fc->startup) fc->startup(rself);
+ return Qnil;
+}
+
+//----------------------------------------------------------------
+// GridFlow.class
+//\class GridFlow_s < patate
+
+typedef void (*Callback)(void*);
+static Ruby GridFlow_exec (Ruby rself, Ruby data, Ruby func) {
+ void *data2 = FIX2PTR(void,data);
+ Callback func2 = (Callback) FIX2PTR(void,func);
+ func2(data2);
+ return Qnil;
+}
+
+static Ruby GridFlow_get_id (Ruby rself, Ruby arg) {
+ fprintf(stderr,"%ld\n",arg);
+ return INT2NUM((int)arg);
+}
+
+Ruby GridFlow_rdtsc (Ruby rself) { return gf_ull2num(rdtsc()); }
+
+/* This code handles nested lists because PureData (0.38) doesn't do it */
+static Ruby GridFlow_handle_braces(Ruby rself, Ruby argv) {
+ int stack[16];
+ int stackn=0;
+ Ruby *av = rb_ary_ptr(argv);
+ int ac = rb_ary_len(argv);
+ int j=0;
+ for (int i=0; i<ac; ) {
+ int close=0;
+ if (SYMBOL_P(av[i])) {
+ const char *s = rb_sym_name(av[i]);
+ while (*s=='(' || *s=='{') {
+ if (stackn==16) RAISE("too many nested lists (>16)");
+ stack[stackn++]=j;
+ s++;
+ }
+ const char *se = s+strlen(s);
+ while (se[-1]==')' || se[-1]=='}') { se--; close++; }
+ if (s!=se) {
+ Ruby u = rb_str_new(s,se-s);
+ av[j++] = rb_funcall(rself,SI(FloatOrSymbol),1,u);
+ }
+ } else {
+ av[j++]=av[i];
+ }
+ i++;
+ while (close--) {
+ if (!stackn) RAISE("unbalanced '}' or ')'",av[i]);
+ Ruby a2 = rb_ary_new();
+ int j2 = stack[--stackn];
+ for (int k=j2; k<j; k++) rb_ary_push(a2,av[k]);
+ j=j2;
+ av[j++] = a2;
+ }
+ }
+ if (stackn) RAISE("unbalanced '{' or '(' (stackn=%d)",stackn);
+ RARRAY(argv)->len = j;
+ return rself;
+}
+
+/* ---------------------------------------------------------------- */
+
+static uint32 memcpy_calls = 0;
+static uint64 memcpy_bytes = 0;
+static uint64 memcpy_time = 0;
+static uint32 malloc_calls = 0; /* only new not delete */
+static uint64 malloc_bytes = 0; /* only new not delete */
+static uint64 malloc_time = 0; /* in cpu ticks */
+
+// don't touch.
+static void gfmemcopy32(int32 *as, int32 *bs, int n) {
+ int32 ba = bs-as;
+#define FOO(I) as[I] = (as+ba)[I];
+ UNROLL_8(FOO,n,as)
+#undef FOO
+
+}
+
+void gfmemcopy(uint8 *out, const uint8 *in, int n) {
+ uint64 t = rdtsc();
+ memcpy_calls++;
+ memcpy_bytes+=n;
+ for (; n>16; in+=16, out+=16, n-=16) {
+ ((int32*)out)[0] = ((int32*)in)[0];
+ ((int32*)out)[1] = ((int32*)in)[1];
+ ((int32*)out)[2] = ((int32*)in)[2];
+ ((int32*)out)[3] = ((int32*)in)[3];
+ }
+ for (; n>4; in+=4, out+=4, n-=4) { *(int32*)out = *(int32*)in; }
+ for (; n; in++, out++, n--) { *out = *in; }
+ t=rdtsc()-t;
+ memcpy_time+=t;
+}
+
+extern "C" {
+void *gfmalloc(size_t n) {
+ uint64 t = rdtsc();
+ void *p = malloc(n);
+ long align = (long)p & 7;
+ if (align) fprintf(stderr,"malloc alignment = %ld mod 8\n",align);
+ t=rdtsc()-t;
+ malloc_time+=t;
+ malloc_calls++;
+ malloc_bytes+=n;
+ return p;
+}
+void gffree(void *p) {
+ uint64 t = rdtsc();
+ free(p);
+ t=rdtsc()-t;
+ malloc_time+=t;
+}};
+
+Ruby GridFlow_memcpy_calls (Ruby rself) { return LONG2NUM(memcpy_calls); }
+Ruby GridFlow_memcpy_bytes (Ruby rself) { return gf_ull2num(memcpy_bytes); }
+Ruby GridFlow_memcpy_time (Ruby rself) { return gf_ull2num(memcpy_time); }
+Ruby GridFlow_malloc_calls (Ruby rself) { return LONG2NUM(malloc_calls); }
+Ruby GridFlow_malloc_bytes (Ruby rself) { return gf_ull2num(malloc_bytes); }
+Ruby GridFlow_malloc_time (Ruby rself) { return gf_ull2num(malloc_time); }
+
+Ruby GridFlow_profiler_reset2 (Ruby rself) {
+ memcpy_calls = memcpy_bytes = memcpy_time = 0;
+ malloc_calls = malloc_bytes = malloc_time = 0;
+ return Qnil;
+}
+
+/* ---------------------------------------------------------------- */
+
+void startup_number();
+void startup_grid();
+void startup_flow_objects();
+void startup_flow_objects_for_image();
+void startup_flow_objects_for_matrix();
+
+Ruby cFormat;
+
+#define SDEF(_class_,_name_,_argc_) \
+ rb_define_singleton_method(c##_class_,#_name_,(RMethod)_class_##_s_##_name_,_argc_)
+#define SDEF2(_name1_,_name2_,_argc_) \
+ rb_define_singleton_method(mGridFlow,_name1_,(RMethod)_name2_,_argc_)
+
+STARTUP_LIST(void)
+
+// Ruby's entrypoint.
+void Init_gridflow () {
+#define FOO(_sym_,_name_) bsym._sym_ = ID2SYM(rb_intern(_name_));
+BUILTIN_SYMBOLS(FOO)
+#undef FOO
+ signal(11,SIG_DFL); // paranoia
+ mGridFlow = EVAL("module GridFlow; CObject = ::Object; "
+ "class<<self; attr_reader :bridge_name; end; "
+ "def post_string(s) STDERR.puts s end; "
+ "self end");
+ SDEF2("exec",GridFlow_exec,2);
+ SDEF2("get_id",GridFlow_get_id,1);
+ SDEF2("rdtsc",GridFlow_rdtsc,0);
+ SDEF2("profiler_reset2",GridFlow_profiler_reset2,0);
+ SDEF2("memcpy_calls",GridFlow_memcpy_calls,0);
+ SDEF2("memcpy_bytes",GridFlow_memcpy_bytes,0);
+ SDEF2("memcpy_time", GridFlow_memcpy_time,0);
+ SDEF2("malloc_calls",GridFlow_malloc_calls,0);
+ SDEF2("malloc_bytes",GridFlow_malloc_bytes,0);
+ SDEF2("malloc_time", GridFlow_malloc_time,0);
+ SDEF2("handle_braces!",GridFlow_handle_braces,1);
+ SDEF2("fclass_install",GridFlow_fclass_install,2);
+
+//#define FOO(A) fprintf(stderr,"sizeof("#A")=%d\n",sizeof(A));
+//FOO(Dim) FOO(BitPacking) FOO(GridHandler) FOO(GridInlet) FOO(GridOutlet) FOO(GridObject)
+//#undef FOO
+
+ rb_ivar_set(mGridFlow, SI(@fobjects), rb_hash_new());
+ rb_ivar_set(mGridFlow, SI(@fclasses), rb_hash_new());
+ rb_ivar_set(mGridFlow, SI(@bsym), PTR2FIX(&bsym));
+ rb_define_const(mGridFlow, "GF_VERSION", rb_str_new2(GF_VERSION));
+ rb_define_const(mGridFlow, "GF_COMPILE_TIME", rb_str_new2(GF_COMPILE_TIME));
+
+ cFObject = rb_define_class_under(mGridFlow, "FObject", rb_cObject);
+ EVAL(
+\ruby
+ module GridFlow
+ class FObject
+ def send_out2(*) end
+ def self.install2(*) end
+ def self.add_creator(name)
+ name=name.to_str.dup
+ GridFlow.fclasses[name]=self
+ GridFlow.add_creator_2 name end
+ end
+ end
+\end ruby
+);
+ define_many_methods(cFObject,COUNT(FObject_methods),FObject_methods);
+ SDEF(FObject, install, 3);
+ SDEF(FObject, new, -1);
+ ID gbi = SI(gf_bridge_init);
+ if (rb_respond_to(rb_cData,gbi)) rb_funcall(rb_cData,gbi,0);
+ Ruby cBitPacking =
+ rb_define_class_under(mGridFlow, "BitPacking", rb_cObject);
+ define_many_methods(cBitPacking,
+ ciBitPacking.methodsn,
+ ciBitPacking.methods);
+ SDEF(BitPacking,new,-1);
+ rb_define_method(rb_cString, "swap32!", (RMethod)String_swap32_f, 0);
+ rb_define_method(rb_cString, "swap16!", (RMethod)String_swap16_f, 0);
+
+ startup_number();
+ startup_grid();
+ startup_flow_objects();
+ startup_flow_objects_for_image();
+ startup_flow_objects_for_matrix();
+ if (!EVAL("begin require 'gridflow/base/main.rb'; true\n"
+ "rescue Exception => e; "
+ "STDERR.puts \"can't load: #{$!}\n"
+ "backtrace: #{$!.backtrace.join\"\n\"}\n"
+ "$: = #{$:.inspect}\"\n; false end")) return;
+ cFormat = EVAL("GridFlow::Format");
+ STARTUP_LIST()
+ EVAL("h=GridFlow.fclasses; h['#io:window'] = h['#io:quartz']||h['#io:x11']||h['#io:sdl']");
+ EVAL("GridFlow.load_user_config");
+ signal(11,SIG_DFL); // paranoia
+}
+
+void GFStack::push (FObject *o) {
+ void *bp = &o; // really. just finding our position on the stack.
+ if (n>=GF_STACK_MAX)
+ RAISE("stack overflow (maximum %d FObject activations at once)", GF_STACK_MAX);
+ uint64 t = rdtsc();
+ if (n) s[n-1].time = t - s[n-1].time;
+ s[n].o = o;
+ s[n].bp = bp;
+ s[n].time = t;
+ n++;
+}
+
+void GFStack::pop () {
+ uint64 t = rdtsc();
+ if (!n) RAISE("stack underflow (WHAT?)");
+ n--;
+ if (s[n].o) s[n].o->total_time += t - s[n].time;
+ if (n) s[n-1].time = t - s[n-1].time;
+}
+
+uint64 gf_timeofday () {
+ timeval t;
+ gettimeofday(&t,0);
+ return t.tv_sec*1000000+t.tv_usec;
+}
diff --git a/externals/gridflow/base/main.rb b/externals/gridflow/base/main.rb
new file mode 100644
index 00000000..82976fb6
--- /dev/null
+++ b/externals/gridflow/base/main.rb
@@ -0,0 +1,369 @@
+=begin
+ $Id: main.rb,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+=end
+
+# ENV["RUBY_VERBOSE_GC"]="yes"
+
+# this file gets loaded by main.c upon startup
+# module GridFlow is supposed to be created by main.c
+# this includes GridFlow.post_string(s)
+
+# because Ruby1.6 has no #object_id and Ruby1.8 warns on #id
+unless Object.instance_methods(true).include? "object_id"
+ class Object; alias object_id id end
+end
+
+# in case of bug in Ruby ("Error: Success")
+module Errno; class E000 < StandardError; end; end
+
+#$post_log = File.open "/tmp/gridflow.log", "w"
+$post_log = nil
+
+class Array
+ def split(elem)
+ r=[]
+ j=0
+ for i in 0...length
+ (r<<self[j,i-j]; j=i+1) if self[i]==elem
+ end
+ r<<self[j,length-j]
+ end
+end
+
+module GridFlow #------------------
+
+def self.post(s,*a)
+ post_string(sprintf("%s"+s,post_header,*a))
+ ($post_log << sprintf(s,*a); $post_log.flush) if $post_log
+end
+
+class<<self
+ attr_accessor :data_path
+ attr_accessor :post_header
+ attr_accessor :verbose
+ attr_reader :fobjects
+ attr_reader :fclasses
+ attr_reader :cpu_hertz
+ attr_reader :subprocesses
+ attr_reader :bridge_name
+ alias gfpost post
+end
+
+@subprocesses={}
+@verbose=false
+@data_path=[]
+if GridFlow.respond_to? :config then
+ @data_path << GridFlow.config["PUREDATA_PATH"]+"/extra/gridflow/images"
+end
+
+def self.hunt_zombies
+ #STDERR.puts "GridFlow.hunt_zombies"
+ # the $$ value is bogus
+ begin
+ died = []
+ subprocesses.each {|x,v|
+ Process.waitpid2(x,Process::WNOHANG) and died<<x
+ }
+ rescue Errno::ECHILD
+ end
+ #STDERR.puts died.inspect
+ died.each {|x| subprocesses.delete x }
+end
+
+def self.packstring_for_nt(nt)
+ case nt
+ when :u, :u8, :uint8; "C*"
+ when :s, :i16, :int16; "s*"
+ when :i, :i32, :int32; "l*"
+ when :f, :f32, :float32; "f*"
+ when :d, :f64, :float64; "d*"
+ else raise "no decoder for #{nt.inspect}"
+ end
+end
+
+self.post_header = "[gf] "
+
+def self.gfpost2(fmt,s); post("%s",s) end
+
+if GridFlow.bridge_name then
+ post "This is GridFlow #{GridFlow::GF_VERSION} within Ruby version #{RUBY_VERSION}"
+ post "base/main.c was compiled on #{GridFlow::GF_COMPILE_TIME}"
+ post "Please use at least 1.6.6 if you plan to use sockets" if RUBY_VERSION<"1.6.6"
+end
+
+if not GridFlow.bridge_name then
+ require "gridflow/bridge/placebo"
+end
+
+Brace1 = "{".intern
+Brace2 = "}".intern
+Paren1 = "(".intern
+Paren2 = ")".intern
+
+def self.parse(m)
+ m = m.gsub(/(\{|\})/," \\1 ").split(/\s+/)
+ m.map! {|x| case x
+ when Integer, Symbol; x
+ when /^[+\-]?[0-9]+$/; x.to_i
+ when String; x.intern
+ end
+ }
+ m
+end
+
+def self.stringify_list(argv)
+ argv.map {|x| stringify x }.join(" ")
+end
+
+def self.stringify(arg)
+ case arg
+ when Integer, Float, Symbol; arg.to_s
+ when Array; "{#{stringify_list arg}}"
+ end
+end
+
+::Object.module_eval do def FloatOrSymbol(x) Float(x) rescue x.intern end end
+
+# adding some functionality to that:
+class FObject
+ @broken_ok = false
+ @do_loadbangs = true
+ class<<self
+ # global
+ attr_accessor :broken_ok
+ # per-class
+ attr_reader :ninlets
+ attr_reader :noutlets
+ attr_accessor :do_loadbangs
+ attr_accessor :comment
+ def foreign_name; @foreign_name if defined? @foreign_name end
+ end
+ def post(*a) GridFlow.post(*a) end
+ def self.subclass(*args,&b)
+ qlass = Class.new self
+ qlass.install(*args)
+ qlass.module_eval(&b)
+ end
+ alias :total_time :total_time_get
+ alias :total_time= :total_time_set
+ attr_writer :args # String
+ attr_accessor :argv # Array
+ attr_reader :outlets
+ attr_accessor :parent_patcher
+ attr_accessor :properties
+ attr_accessor :classname
+ def initialize2; end
+ def args
+ if defined? @args
+ @args
+ else
+ "[#{self.class} ...]"
+ end
+ end
+ alias info args
+ def connect outlet, object, inlet
+ @outlets ||= []
+ @outlets[outlet] ||= []
+ @outlets[outlet].push [object, inlet]
+ end
+ def self.name_lookup sym
+ qlasses = GridFlow.fclasses
+ qlass = qlasses[sym.to_s]
+ if not qlass
+ return qlasses['broken'] if @broken_ok
+ raise "object class '#{sym}' not found"
+ end
+ qlass
+ end
+ def self.[](*m)
+ o=nil
+ if m.length==1 and m[0] =~ / /
+ o="[#{m[0]}]"
+ m=GridFlow.parse(m[0])
+ else
+ o=m.inspect
+ end
+ GridFlow.handle_braces!(m)
+ ms = m.split ','.intern
+ m = ms.shift
+ qlass = m.shift
+ qlassname = qlass.to_s
+ qlass = name_lookup qlass.to_s unless Class===qlass
+ r = qlass.new(*m)
+ r.classname = qlassname
+ GridFlow.post "%s",r.args if GridFlow.verbose
+ for x in ms do r.send_in(-2, *x) end if FObject.do_loadbangs
+ r
+ end
+ def inspect
+ if args then "#<#{self.class} #{args}>" else super end
+ end
+ def initialize(*argv)
+ s = GridFlow.stringify_list argv
+ @argv = argv
+ @args = "["
+ @args << (self.class.foreign_name || self.to_s)
+ @args << " " if s.length>0
+ @args << s << "]"
+ @parent_patcher = nil
+ @properties = {}
+ @init_messages = []
+ end
+end
+
+class FPatcher < FObject
+ class << self
+ attr_reader :fobjects
+ attr_reader :wires
+ end
+ def initialize(*)
+ super
+ fobjects = self.class.fobjects
+ wires = self.class.wires
+ @fobjects = fobjects.map {|x| if String===x then FObject[x] else x.call end }
+ @inlets = []
+ @ninlets = self.class.ninlets or raise "oops"
+ i=0
+ @fobjects << self
+ while i<wires.length do
+ a,b,c,d = wires[i,4]
+ if a==-1 then
+ a=self
+ @inlets[b]||=[]
+ @inlets[b] << [@fobjects[c],d]
+ else
+ if c==-1 then
+ @fobjects[a].connect b,self,d+@ninlets
+ else
+ @fobjects[a].connect b,@fobjects[c],d
+ end
+ end
+ i+=4
+ end
+ end
+ def method_missing(sym,*args)
+ sym=sym.to_s
+ if sym =~ /^_(\d)_(.*)/ then
+ inl = Integer $1
+ sym = $2.intern
+ if inl<@ninlets then
+ raise "#{inspect} has not @inlets[#{inl}]" if not @inlets[inl]
+ for x in @inlets[inl] do
+ x[0].send_in x[1],sym,*args end
+ else
+ send_out(inl-@ninlets,sym,*args)
+ end
+ else super end
+ end
+end
+
+def GridFlow.estimate_cpu_clock
+ u0,t0=GridFlow.rdtsc,Time.new.to_f; sleep 0.01
+ u1,t1=GridFlow.rdtsc,Time.new.to_f; (u1-u0)/(t1-t0)
+end
+
+begin
+ @cpu_hertz = (0...3).map {
+ GridFlow.estimate_cpu_clock
+ }.sort[1] # median of three tries
+rescue
+ GridFlow.post $!
+end
+
+def GridFlow.find_file s
+ s=s.to_s
+ if s==File.basename(s) then
+ dir = GridFlow.data_path.find {|x| File.exist? "#{x}/#{s}" }
+ if dir then "#{dir}/#{s}" else s end
+ elsif GridFlow.respond_to? :find_file_2
+ GridFlow.find_file_2 s
+ else
+ s
+ end
+end
+
+def GridFlow.macerr(i)
+ begin
+ f=File.open("/System/Library/Frameworks/CoreServices.framework/"+
+ "Versions/A/Frameworks/CarbonCore.framework/Versions/A/Headers/"+
+ "MacErrors.h")
+ while f.gets
+ m = /^\s*(\w+)\s*=\s*(-\d+),\s*\/\*\s*(.*)\s*\*\/$/.match $_
+ next if not m
+ if m[2].to_i == i then return "#{m[2]}: \"#{m[3]}\"" end
+ end
+ return "no error message available for this error number"
+ rescue FileError
+ return "Can't find Apple's precious copyrighted list of error messages on this system."
+ ensure
+ f.close if f
+ end
+end
+
+end # module GridFlow
+
+class IO
+ def nonblock= flag
+ bit = Fcntl::O_NONBLOCK
+ state = fcntl(Fcntl::F_GETFL, 0)
+ fcntl(Fcntl::F_SETFL, (state & ~bit) |
+ (if flag; bit else 0 end))
+ end
+end
+
+def protect
+ yield
+rescue Exception => e
+ STDERR.puts "#{e.class}: #{e}"
+ STDERR.puts e.backtrace
+end
+
+def GridFlow.load_user_config
+ require "gridflow/bridge/puredata.rb" if GridFlow.bridge_name == "puredata"
+ user_config_file = ENV["HOME"] + "/.gridflow_startup"
+ begin
+ load user_config_file if File.exist? user_config_file
+ rescue Exception => e
+ GridFlow.post "#{e.class}: #{e}:\n" + e.backtrace.join("\n")
+ GridFlow.post "while loading ~/.gridflow_startup"
+ end
+end
+
+require "gridflow/base/flow_objects.rb"
+require "gridflow/format/main.rb"
+
+%w(
+ # #for #finished #type #dim #transpose #perspective #store #outer
+ #grade #redim #import #export #export_list #cast
+ #scale_by #downscale_by #draw_polygon #draw_image #layer
+ #print #pack #export_symbol #rotate
+ #in #out
+).each {|k|
+ GridFlow::FObject.name_lookup(k).add_creator k.gsub(/#/,"@")
+}
+
+END {
+ GridFlow.fobjects.each {|k,v| k.delete if k.respond_to? :delete }
+ GridFlow.fobjects.clear
+ GC.start
+}
+
diff --git a/externals/gridflow/base/number.c b/externals/gridflow/base/number.c
new file mode 100644
index 00000000..c483cf0b
--- /dev/null
+++ b/externals/gridflow/base/number.c
@@ -0,0 +1,365 @@
+/*
+ $Id: number.c,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+ GridFlow
+ Copyright (c) 2001,2002,2003,2004 by Mathieu Bouchard
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ See file ../COPYING for further informations on licensing terms.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "grid.h.fcs"
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#ifdef PASS1
+NumberType number_type_table[] = {
+#define FOO(_sym_,_size_,_flags_,args...) NumberType( #_sym_, _size_, _flags_, args ),
+NUMBER_TYPES(FOO)
+#undef FOO
+};
+const long number_type_table_n = COUNT(number_type_table);
+#endif
+
+// those are bogus class-templates in the sense that you don't create
+// objects from those, you just call static functions. The same kind
+// of pattern is present in STL to overcome some limitations of C++.
+
+template <class T> class Op {
+public:
+ // I call abort() on those because I can't say they're purevirtual.
+ static T f(T a, T b) {abort();}
+ static bool is_neutral(T x, LeftRight side) {assert(!"Op::is_neutral called?");}
+ static bool is_absorbent(T x, LeftRight side) {assert(!"Op::is_absorbent called?");}
+};
+
+template <class O> class OpLoops {
+public:
+ template <class T> static void op_map (int n, T *as, T b) {
+ if (!n) return;
+#define FOO(I) as[I]=O::f(as[I],b);
+ UNROLL_8(FOO,n,as)
+#undef FOO
+ }
+ template <class T> static void op_zip (int n, T *as, T *bs) {
+ if (!n) return;
+ int ba=bs-as; // really!
+#define FOO(I) as[I]=O::f(as[I],as[ba+I]);
+ UNROLL_8(FOO,n,as)
+#undef FOO
+ }
+ // disabled
+ template <class T> static void op_zip2 (int n, T *as, T *bs, T *cs) {
+ if (!n) return;
+ int ba=bs-as, ca=cs-as;
+#define FOO(I) as[ca+I]=O::f(as[I],as[ba+I]);
+ UNROLL_8(FOO,n,as)
+#undef FOO
+ }
+#define W(i) as[i]=O::f(as[i],bs[i]);
+#define Z(i,j) as[i]=O::f(O::f(O::f(O::f(as[i],bs[i]),bs[i+j]),bs[i+j+j]),bs[i+j+j+j]);
+ template <class T> static void op_fold (int an, int n, T *as, T *bs) {
+ switch (an) {
+ case 1: for (; (n&3)!=0; bs++, n--) W(0);
+ for (; n; bs+=4, n-=4) { Z(0,1); } break;
+ case 2: for (; (n&3)!=0; bs+=2, n--) { W(0); W(1); }
+ for (; n; bs+=8, n-=4) { Z(0,2); Z(1,2); } break;
+ case 3: for (; (n&3)!=0; bs+=3, n--) { W(0); W(1); W(2); }
+ for (; n; bs+=12, n-=4) { Z(0,3); Z(1,3); Z(2,3); } break;
+ case 4: for (; (n&3)!=0; bs+=4, n--) { W(0); W(1); W(2); W(3); }
+ for (; n; bs+=16, n-=4) { Z(0,4); Z(1,4); Z(2,4); Z(3,4); } break;
+ default:for (; n--; ) {
+ int i=0;
+ for (; i<(an&-4); i+=4, bs+=4) {
+ as[i+0]=O::f(as[i+0],bs[0]);
+ as[i+1]=O::f(as[i+1],bs[1]);
+ as[i+2]=O::f(as[i+2],bs[2]);
+ as[i+3]=O::f(as[i+3],bs[3]);
+ }
+ for (; i<an; i++, bs++) as[i] = O::f(as[i],*bs);
+ }
+ }
+ }
+ template <class T> static void op_scan (int an, int n, T *as, T *bs) {
+ for (; n--; as=bs-an) {
+ for (int i=0; i<an; i++, as++, bs++) *bs=O::f(*as,*bs);
+ }
+ }
+};
+
+template <class T>
+static void quick_mod_map (int n, T *as, T b) {
+ if (!b) return;
+#define FOO(I) as[I]=mod(as[I],b);
+ UNROLL_8(FOO,n,as)
+#undef FOO
+}
+
+template <class T> static void quick_ign_map (int n, T *as, T b) {}
+template <class T> static void quick_ign_zip (int n, T *as, T *bs) {}
+template <class T> static void quick_put_map (int n, T *as, T b) {
+#define FOO(I) as[I]=b;
+ UNROLL_8(FOO,n,as)
+#undef FOO
+}
+
+#ifdef PASS1
+void quick_put_map (int n, int16 *as, int16 b) {
+ if (n&1!=0 && (long)as&4!=0) { *as++=b; n--; }
+ quick_put_map (n>>1, (int32 *)as, (int32)(b<<16)+b);
+ if (n&1!=0) *as++=b;
+}
+void quick_put_map (int n, uint8 *as, uint8 b) {
+ while (n&3!=0 && (long)as&4!=0) { *as++=b; n--; }
+ int32 c=(b<<8)+b; c+=c<<16;
+ quick_put_map (n>>2, (int32 *)as, c);
+ while (n&3!=0) *as++=b;
+}
+#endif
+template <class T> static void quick_put_zip (int n, T *as, T *bs) {
+ gfmemcopy((uint8 *)as, (uint8 *)bs, n*sizeof(T));
+}
+
+// classic two-input operator
+#define DEF_OP(op,expr,neutral,absorbent) \
+ template <class T> class Y##op : Op<T> { public: \
+ inline static T f(T a, T b) { return expr; } \
+ inline static bool is_neutral (T x, LeftRight side) { return neutral; } \
+ inline static bool is_absorbent(T x, LeftRight side) { return absorbent; } };
+#define DEF_OPFT(op,expr,neutral,absorbent,T) \
+ template <> class Y##op<T> : Op<T> { public: \
+ inline static T f(T a, T b) { return expr; } \
+ inline static bool is_neutral (T x, LeftRight side) { return neutral; } \
+ inline static bool is_absorbent(T x, LeftRight side) { return absorbent; } }; \
+// this macro is for operators that have different code for the float version
+#define DEF_OPF(op,expr,expr2,neutral,absorbent) \
+ DEF_OP( op,expr, neutral,absorbent) \
+ DEF_OPFT(op,expr2,neutral,absorbent,float32) \
+ DEF_OPFT(op,expr2,neutral,absorbent,float64)
+
+#define DECL_OPON(base,op,T) NumopOn<T>( \
+ &base<Y##op<T> >::op_map, &base<Y##op<T> >::op_zip, \
+ &base<Y##op<T> >::op_fold, &base<Y##op<T> >::op_scan, \
+ &Y##op<T>::is_neutral, &Y##op<T>::is_absorbent)
+#define DECL_OPON_NOFOLD(base,op,T) NumopOn<T>( \
+ &base<Y##op<T> >::op_map, &base<Y##op<T> >::op_zip, 0,0, \
+ &Y##op<T>::is_neutral, &Y##op<T>::is_absorbent)
+#define DECL_OP(op,sym,flags) Numop(0, sym, \
+ DECL_OPON(OpLoops,op,uint8), DECL_OPON(OpLoops,op,int16), \
+ DECL_OPON(OpLoops,op,int32) NONLITE(, DECL_OPON(OpLoops,op,int64), \
+ DECL_OPON(OpLoops,op,float32), DECL_OPON(OpLoops,op,float64)), flags)
+#define DECL_OP_NOFLOAT(op,sym,flags) Numop(0, sym, \
+ DECL_OPON(OpLoops,op,uint8), DECL_OPON(OpLoops,op,int16), \
+ DECL_OPON(OpLoops,op,int32) NONLITE(, DECL_OPON(OpLoops,op,int64), \
+ NumopOn<float32>(0,0,0,0,0,0), NumopOn<float64>(0,0,0,0,0,0)), flags)
+#define DECL_OP_NOFOLD(op,sym,flags) Numop(0, sym, \
+ DECL_OPON_NOFOLD(OpLoops,op,uint8), DECL_OPON_NOFOLD(OpLoops,op,int16), \
+ DECL_OPON_NOFOLD(OpLoops,op,int32) NONLITE(, DECL_OPON_NOFOLD(OpLoops,op,int64), \
+ DECL_OPON_NOFOLD(OpLoops,op,float32), DECL_OPON_NOFOLD(OpLoops,op,float64)), flags)
+
+template <class T> static inline T gf_floor (T a) {
+ return (T) floor((double)a); }
+template <class T> static inline T gf_trunc (T a) {
+ return (T) floor(abs((double)a)) * (a<0?-1:1); }
+
+/*
+uint8 clipadd(uint8 a, uint8 b) { int32 c=a+b; return c<0?0:c>255?255:c; }
+int16 clipadd(int16 a, int16 b) { int32 c=a+b; return c<-0x8000?-0x8000:c>0x7fff?0x7fff:c; }
+int32 clipadd(int32 a, int32 b) { int64 c=a+b; return c<-0x80000000?-0x80000000:c>0x7fffffff?0x7fffffff:c; }
+int64 clipadd(int64 a, int64 b) { int64 c=(a>>1)+(b>>1)+(a&b&1);
+ return c<(nt_smallest(0LL)/2?nt_smallest(0LL):c>nt_greatest(0LL)/2?nt_greatest(0LL):a+b; }
+uint8 clipsub(uint8 a, uint8 b) { int32 c=a-b; return c<0?0:c>255?255:c; }
+int16 clipsub(int16 a, int16 b) { int32 c=a-b; return c<-0x8000?-0x8000:c>0x7fff?0x7fff:c; }
+int32 clipsub(int32 a, int32 b) { int64 c=a-b; return c<-0x80000000?-0x80000000:c>0x7fffffff?0x7fffffff:c; }
+int64 clipsub(int64 a, int64 b) { int64 c=(a>>1)-(b>>1); //???
+ return c<(nt_smallest(0LL)/2?nt_smallest(0LL):c>nt_greatest(0LL)/2?nt_greatest(0LL):a-b; }
+*/
+
+#ifdef PASS1
+DEF_OP(ignore, a, side==at_right, side==at_left)
+DEF_OP(put, b, side==at_left, side==at_right)
+DEF_OP(add, a+b, x==0, false)
+DEF_OP(sub, a-b, side==at_right && x==0, false)
+DEF_OP(bus, b-a, side==at_left && x==0, false)
+DEF_OP(mul, a*b, x==1, x==0)
+DEF_OP(mulshr8, ((int32)a*(int32)b)>>8, (int64)x==256, x==0) //!@#$ bug with int64
+DEF_OP(div, b==0 ? 0 : a/b, side==at_right && x==1, false)
+DEF_OP(div2, b==0 ? 0 : div2(a,b), side==at_right && x==1, false)
+DEF_OP(vid, a==0 ? 0 : b/a, side==at_left && x==1, false)
+DEF_OP(vid2, a==0 ? 0 : div2(b,a), side==at_left && x==1, false)
+DEF_OPF(mod, b==0 ? 0 : mod(a,b), b==0 ? 0 : a-b*gf_floor(a/b),
+ false, side==at_left && x==0 || side==at_right && x==1)
+DEF_OPF(dom, a==0 ? 0 : mod(b,a), a==0 ? 0 : b-a*gf_floor(b/a),
+ false, side==at_left && x==0 || side==at_right && x==1)
+//DEF_OPF(rem, b==0 ? 0 : a%b, b==0 ? 0 : a-b*gf_trunc(a/b))
+//DEF_OPF(mer, a==0 ? 0 : b%a, a==0 ? 0 : b-a*gf_trunc(b/a))
+DEF_OP(rem, b==0?0:a%b, false, side==at_left&&x==0 || side==at_right&&x==1)
+DEF_OP(mer, a==0?0:b%a, false, side==at_left&&x==0 || side==at_right&&x==1)
+#endif
+#ifdef PASS2
+DEF_OP(gcd, gcd(a,b), x==0, x==1)
+DEF_OP(gcd2, gcd2(a,b), x==0, x==1) // should test those and pick one of the two
+DEF_OP(lcm, a==0 || b==0 ? 0 : lcm(a,b), x==1, x==0)
+DEF_OPF(or , a|b, (float32)((int32)a | (int32)b), x==0, x==nt_all_ones(&x))
+DEF_OPF(xor, a^b, (float32)((int32)a ^ (int32)b), x==0, false)
+DEF_OPF(and, a&b, (float32)((int32)a & (int32)b), x==nt_all_ones(&x), x==0)
+DEF_OPF(shl, a<<b, a*pow(2.0,+b), side==at_right && x==0, false)
+DEF_OPF(shr, a>>b, a*pow(2.0,-b), side==at_right && x==0, false)
+DEF_OP(sc_and, a ? b : a, side==at_left && x!=0, side==at_left && x==0)
+DEF_OP(sc_or, a ? a : b, side==at_left && x==0, side==at_left && x!=0)
+DEF_OP(min, min(a,b), x==nt_greatest(&x), x==nt_smallest(&x))
+DEF_OP(max, max(a,b), x==nt_smallest(&x), x==nt_greatest(&x))
+#endif
+#ifdef PASS3
+DEF_OP(cmp, cmp(a,b), false, false)
+DEF_OP(eq, a == b, false, false)
+DEF_OP(ne, a != b, false, false)
+DEF_OP(gt, a > b, false, side==at_left&&x==nt_smallest(&x)||side==at_right&&x==nt_greatest(&x))
+DEF_OP(le, a <= b, false, side==at_left&&x==nt_smallest(&x)||side==at_right&&x==nt_greatest(&x))
+DEF_OP(lt, a < b, false, side==at_left&&x==nt_greatest(&x)||side==at_right&&x==nt_smallest(&x))
+DEF_OP(ge, a >= b, false, side==at_left&&x==nt_greatest(&x)||side==at_right&&x==nt_smallest(&x))
+DEF_OP(sin, (T)(b * sin(a * (M_PI / 18000))), false, false) // "LN=9000+36000n RA=0 LA=..."
+DEF_OP(cos, (T)(b * cos(a * (M_PI / 18000))), false, false) // "LN=36000n RA=0 LA=..."
+DEF_OP(atan, (T)(atan2(a,b) * (18000 / M_PI)), false, false) // "LA=0"
+DEF_OP(tanh, (T)(b * tanh(a * (M_PI / 18000))), false, x==0)
+DEF_OP(gamma, b<=0 ? 0 : (T)(0+floor(pow(a/256.0,256.0/b)*256.0)), false, false) // "RN=256"
+DEF_OP(pow, ipow(a,b), false, false) // "RN=1"
+DEF_OP(log, (T)(a==0 ? 0 : b * log(gf_abs(a))), false, false) // "RA=0"
+// 0.7.8
+//DEF_OPF(clipadd, clipadd(a,b), a+b, x==0, false)
+//DEF_OPF(clipsub, clipsub(a,b), a-b, side==at_right && x==0, false)
+DEF_OP(abssub, gf_abs(a-b), false, false)
+DEF_OP(sqsub, (a-b)*(a-b), false, false)
+DEF_OP(avg, (a+b)/2, false, false)
+DEF_OP(hypot, (T)(0+floor(sqrt(a*a+b*b))), false, false)
+DEF_OP(sqrt, (T)(0+floor(sqrt(a))), false, false)
+DEF_OP(rand, a==0 ? 0 : random()%(int32)a, false, false)
+//DEF_OP(erf,"erf*", 0)
+#endif
+
+extern Numop op_table1[], op_table2[], op_table3[];
+extern const long op_table1_n, op_table2_n, op_table3_n;
+
+#ifdef PASS1
+Numop op_table1[] = {
+ DECL_OP(ignore, "ignore", OP_ASSOC),
+ DECL_OP(put, "put", OP_ASSOC),
+ DECL_OP(add, "+", OP_ASSOC|OP_COMM), // "LINV=sub"
+ DECL_OP(sub, "-", 0),
+ DECL_OP(bus, "inv+", 0),
+ DECL_OP(mul, "*", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFLOAT(mulshr8, "*>>8", OP_ASSOC|OP_COMM),
+ DECL_OP(div, "/", 0),
+ DECL_OP_NOFLOAT(div2, "div", 0),
+ DECL_OP(vid, "inv*", 0),
+ DECL_OP_NOFLOAT(vid2, "swapdiv", 0),
+ DECL_OP_NOFLOAT(mod, "%", 0),
+ DECL_OP_NOFLOAT(dom, "swap%", 0),
+ DECL_OP_NOFLOAT(rem, "rem", 0),
+ DECL_OP_NOFLOAT(mer, "swaprem", 0),
+};
+const long op_table1_n = COUNT(op_table1);
+#endif
+#ifdef PASS2
+Numop op_table2[] = {
+ DECL_OP_NOFLOAT(gcd, "gcd", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFLOAT(gcd2, "gcd2", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFLOAT(lcm, "lcm", OP_ASSOC|OP_COMM),
+ DECL_OP(or , "|", OP_ASSOC|OP_COMM),
+ DECL_OP(xor, "^", OP_ASSOC|OP_COMM),
+ DECL_OP(and, "&", OP_ASSOC|OP_COMM),
+ DECL_OP_NOFOLD(shl, "<<", 0),
+ DECL_OP_NOFOLD(shr, ">>", 0),
+ DECL_OP_NOFOLD(sc_and,"&&", 0),
+ DECL_OP_NOFOLD(sc_or, "||", 0),
+ DECL_OP(min, "min", OP_ASSOC|OP_COMM),
+ DECL_OP(max, "max", OP_ASSOC|OP_COMM),
+};
+const long op_table2_n = COUNT(op_table2);
+#endif
+#ifdef PASS3
+Numop op_table3[] = {
+ DECL_OP_NOFOLD(eq, "==", OP_COMM),
+ DECL_OP_NOFOLD(ne, "!=", OP_COMM),
+ DECL_OP_NOFOLD(gt, ">", 0),
+ DECL_OP_NOFOLD(le, "<=", 0),
+ DECL_OP_NOFOLD(lt, "<", 0),
+ DECL_OP_NOFOLD(ge, ">=", 0),
+ DECL_OP_NOFOLD(cmp, "cmp", 0),
+ DECL_OP_NOFOLD(sin, "sin*", 0),
+ DECL_OP_NOFOLD(cos, "cos*", 0),
+ DECL_OP_NOFOLD(atan, "atan", 0),
+ DECL_OP_NOFOLD(tanh, "tanh*", 0),
+ DECL_OP_NOFOLD(gamma, "gamma", 0),
+ DECL_OP_NOFOLD(pow, "**", 0),
+ DECL_OP_NOFOLD(log, "log*", 0),
+// 0.7.8
+// DECL_OP(clipadd,"clip+", OP_ASSOC|OP_COMM),
+// DECL_OP(clipsub,"clip-", 0),
+ DECL_OP_NOFOLD(abssub,"abs-", OP_COMM),
+ DECL_OP_NOFOLD(sqsub,"sq-", OP_COMM),
+ DECL_OP_NOFOLD(avg,"avg", OP_COMM),
+ DECL_OP_NOFOLD(hypot,"hypot", OP_COMM),
+ DECL_OP_NOFOLD(sqrt,"sqrt", 0),
+ DECL_OP_NOFOLD(rand,"rand", 0),
+ //DECL_OP_NOFOLD(erf,"erf*", 0),
+};
+const long op_table3_n = COUNT(op_table3);
+#endif
+
+// D=dictionary, A=table, A##_n=table count.
+#define INIT_TABLE(D,A) { D=IEVAL(mGridFlow,"@"#D" ||= {}"); \
+ for(int i=0; i<A##_n; i++) { \
+ A[i].sym = ID2SYM(rb_intern(A[i].name)); \
+ rb_hash_aset(D,A[i].sym,PTR2FIX((A+i)));}}
+
+#ifdef PASS1
+Ruby op_dict = Qnil;
+Ruby number_type_dict = Qnil;
+void startup_number () {
+ INIT_TABLE(op_dict,op_table1)
+ INIT_TABLE(op_dict,op_table2)
+ INIT_TABLE(op_dict,op_table3)
+ INIT_TABLE(number_type_dict,number_type_table)
+
+ for (int i=0; i<COUNT(number_type_table); i++) {
+ number_type_table[i].index = (NumberTypeE) i;
+ char a[64];
+ strcpy(a,number_type_table[i].aliases);
+ char *b = strchr(a,',');
+ if (b) {
+ *b=0;
+ rb_hash_aset(number_type_dict, ID2SYM(rb_intern(b+1)),
+ PTR2FIX(&number_type_table[i]));
+ }
+ rb_hash_aset(number_type_dict, ID2SYM(rb_intern(a)),
+ PTR2FIX(&number_type_table[i]));
+ }
+// S:name; M:mode; F:replacement function;
+#define OVERRIDE_INT(S,M,F) { \
+ Numop *foo = FIX2PTR(Numop,rb_hash_aref(op_dict,SYM(S))); \
+ foo->on_uint8.op_##M=F; \
+ foo->on_int16.op_##M=F; \
+ foo->on_int32.op_##M=F; }
+ OVERRIDE_INT(ignore,map,quick_ign_map);
+ OVERRIDE_INT(ignore,zip,quick_ign_zip);
+ //OVERRIDE_INT(put,map,quick_put_map);
+ //OVERRIDE_INT(put,zip,quick_put_zip);
+ //OVERRIDE_INT(%,map,quick_mod_map); // !@#$ does that make an improvement at all?
+}
+#endif
diff --git a/externals/gridflow/base/source_filter.rb b/externals/gridflow/base/source_filter.rb
new file mode 100644
index 00000000..59e24867
--- /dev/null
+++ b/externals/gridflow/base/source_filter.rb
@@ -0,0 +1,274 @@
+$keywords = %w(class decl def end grdecl)
+$stack = []
+$classes = []
+
+ClassDecl = Struct.new(:name,:supername,:methods,:grins,:attrs,:info)
+MethodDecl = Struct.new(:rettype,:selector,:arglist,:minargs,:maxargs,:where)
+Arg = Struct.new(:type,:name,:default)
+
+class MethodDecl
+ def ==(o)
+ return false unless rettype==o.rettype &&
+ maxargs==o.maxargs # && minargs==o.minargs
+ arglist.each_index{|i| arglist[i] == o.arglist[i] or return false }
+ return true
+ end
+end
+
+class Arg
+ def ==(o)
+ type==o.type && name==o.name # && default==o.default
+ end
+end
+
+In = File.open ARGV[0], "r"
+Out = File.open ARGV[1], "w"
+
+def handle_class(line)
+ raise "already in class #{where}" if $stack[-1] and ClassDecl===$stack[-1]
+ #STDERR.puts "class: #{line}"
+ /^(\w+)(?:\s*<\s*(\w+))?$/.match line or raise "syntax error #{where}"
+ q=ClassDecl.new($1,$2,{},{},{},false)
+ $stack << q
+ $classes << q
+ Out.puts ""
+end
+
+def parse_methoddecl(line,term)
+ /^(\w+)\s+(\w+)\s*\(([^\)]*)\)\s*#{term}/.match line or
+ raise "syntax error #{where} #{line}"
+ rettype,selector,arglist = $1,$2,$3
+ arglist,minargs,maxargs = parse_arglist arglist
+ MethodDecl.new(rettype,selector,arglist,minargs,maxargs,where)
+end
+
+def parse_arglist(arglist)
+ arglist = arglist.split(/,/)
+ maxargs = arglist.length
+ args = arglist.map {|arg|
+ if /^\s*\.\.\.\s*$/.match arg then maxargs=-1; next end
+ /^\s*([\w\s\*<>]+)\s*\b(\w+)\s*(?:\=(.*))?/.match arg or
+ raise "syntax error in \"#{arg}\" #{where}"
+ type,name,default=$1,$2,$3
+ Arg.new(type.sub(/\s+$/,""),name,default)
+ }.compact
+ minargs = args.length
+ minargs-=1 while minargs>0 and args[minargs-1].default
+ [args,minargs,maxargs]
+end
+
+def unparse_arglist(arglist,with_default=true)
+ arglist.map {|arg|
+ x="#{arg.type} #{arg.name} "
+ x<<'='<<arg.default if with_default and arg.default
+ x
+ }.join(", ")
+end
+
+def where
+ "[#{ARGV[0]}:#{$linenumber}]"
+end
+
+def handle_attr(line)
+ type = line.gsub(%r"//.*$","").gsub(%r"/\*.*\*/","").gsub(%r";?\s*$","")
+ name = type.slice!(/\w+$/)
+ raise "missing \\class #{where}" if
+ not $stack[-1] or not ClassDecl===$stack[-1]
+ $stack[-1].attrs[name]=Arg.new(type,name,nil)
+ Out.print line
+ Out.puts "//FCS"
+ handle_decl "void _0_#{name}_m (#{type} #{name});"
+ Out.puts "# #{$linenumber}"
+end
+
+def handle_decl(line)
+ raise "missing \\class #{where}" if
+ not $stack[-1] or not ClassDecl===$stack[-1]
+ classname = $stack[-1].name
+ m = parse_methoddecl(line,";\s*$")
+ $stack[-1].methods[m.selector] = m
+
+ Out.print "#{m.rettype} #{m.selector}(int argc, Ruby *argv"
+ Out.print "," if m.arglist.length>0
+ Out.print "#{unparse_arglist m.arglist});"
+ Out.puts "static Ruby #{m.selector}_wrap"+
+ "(int argc, Ruby *argv, Ruby rself);//FCS"
+ Out.puts "# #{$linenumber+1}"
+end
+
+def handle_def(line)
+ m = parse_methoddecl(line,"\{?.*$")
+ term = line[/\{.*/]
+ qlass = $stack[-1]
+ raise "missing \\class #{where}" if not qlass or not ClassDecl===qlass
+ classname = qlass.name
+ if qlass.methods[m.selector]
+ n = m; m = qlass.methods[m.selector]
+ if m!=n then
+ STDERR.puts "warning: def does not match decl:"
+ STDERR.puts "#{m.where}: \\decl #{m.inspect}"
+ STDERR.puts "#{n.where}: \\def #{n.inspect}"
+ end
+ else
+ qlass.methods[m.selector] = m
+ end
+
+ Out.print "Ruby #{classname}::#{m.selector}_wrap"+
+ "(int argc, Ruby *argv, Ruby rself) {"+
+ "static const char *methodspec = "+
+ "\"#{qlass.name}::#{m.selector}(#{unparse_arglist m.arglist,false})\";"+
+ "DGS(#{classname});"
+
+ Out.print "if (argc<#{m.minargs}"
+ Out.print "||argc>#{m.maxargs}" if m.maxargs!=-1
+ Out.print ") RAISE(\"got %d args instead of %d..%d in %s\""+
+ ",argc,#{m.minargs},#{m.maxargs},methodspec);"
+
+ error = proc {|x,y|
+ "RAISE(\"got %s instead of #{x} in %s\","+
+ "rb_str_ptr(rb_inspect(rb_obj_class(#{y}))),methodspec)"
+ }
+
+ m.arglist.each_with_index{|arg,i|
+ case arg.type
+ when "Symbol"
+ Out.print "if (argc>#{i} && TYPE(argv[#{i}])!=T_SYMBOL) "+
+ error[arg.type,"argv[#{i}]"]+";"
+ when "Array"
+ Out.print "if (argc>#{i} && TYPE(argv[#{i}])!=T_ARRAY) "+
+ error[arg.type,"argv[#{i}]"]+";"
+ when "String"
+ Out.print "if (argc>#{i} && TYPE(argv[#{i}])==T_SYMBOL) "+
+ "argv[#{i}]=rb_funcall(argv[#{i}],SI(to_s),0);"
+ Out.print "if (argc>#{i} && TYPE(argv[#{i}])!=T_STRING) "+
+ error[arg.type,"argv[#{i}]"]+";"
+ end
+ }
+
+# Out.print "return " if m.rettype!="void"
+ Out.print "VALUE foo = " if m.rettype!="void" ###
+
+ Out.print " self->#{m.selector}(argc,argv"
+ m.arglist.each_with_index{|arg,i|
+ if arg.default then
+ Out.print ",argc<#{i+1}?#{arg.default}:convert(argv[#{i}],(#{arg.type}*)0)"
+ else
+ Out.print ",convert(argv[#{i}],(#{arg.type}*)0)"
+ end
+ }
+ Out.print ");"
+ Out.print "self->check_magic();"
+ Out.print "return Qnil;" if m.rettype=="void"
+ Out.print "return foo;" if m.rettype!="void" ###
+ Out.print "} #{m.rettype} #{classname}::#{m.selector}(int argc, Ruby *argv"
+ Out.print "," if m.arglist.length>0
+ Out.puts "#{unparse_arglist m.arglist, false})#{term}//FCS"
+end
+
+def handle_classinfo(line)
+ frame = $stack[-1]
+ cl = frame.name
+ line="{}" if /^\s*$/ =~ line
+ Out.puts "static void #{cl}_startup (Ruby rself);"
+ Out.puts "static void *#{cl}_allocator () {return new #{cl};}"
+ Out.puts "static MethodDecl #{cl}_methods[] = {"
+ Out.puts frame.methods.map {|foo,method|
+ c,s = frame.name,method.selector
+ "{ \"#{s}\",(RMethod)#{c}::#{s}_wrap }"
+ }.join(",")
+ Out.puts "}; FClass ci#{cl} = { #{cl}_allocator, #{cl}_startup,"
+ Out.puts "#{cl.inspect}, COUNT(#{cl}_methods), #{cl}_methods };"
+ Out.puts "void #{frame.name}_startup (Ruby rself) "+line
+end
+
+def handle_grin(line)
+ fields = line.split(/\s+/)
+ i = fields[0].to_i
+ c = $stack[-1].name
+ Out.print "template <class T> void grin_#{i}(GridInlet *in, int n, Pt<T> data);"
+ Out.print "template <class T> static void grinw_#{i} (GridInlet *in, int n, Pt<T> data);"
+ Out.print "static GridHandler grid_#{i}_hand;"
+ handle_decl "Ruby _#{i}_grid(...);"
+ $stack[-1].grins[i] = fields.dup
+end
+
+def handle_end(line)
+ frame = $stack.pop
+ fields = line.split(/\s+/)
+ n = fields.length
+ if ClassDecl===frame then
+ #handle_classinfo if not frame.info
+ cl = frame.name
+ if fields[0]!="class" or
+ (n>1 and fields[1]!=cl)
+ then raise "end not matching #{where}" end
+ $stack.push frame
+ frame.attrs.each {|name,attr|
+ type,name,default = attr.to_a
+ #STDERR.puts "type=#{type} name=#{name} default=#{default}"
+ handle_def "void _0_#{name}_m (#{type} #{name}) { this->#{name}=#{name}; }"
+ }
+ frame.grins.each {|i,v|
+ k = case v[1]
+ when nil; '4'
+ when 'int32'; '1'
+ when 'int'; '2'
+ when 'float'; 'F'
+ else raise 'BORK BORK BORK' end
+ Out.print "static GridHandler #{cl}_grid_#{i}_hand = GRIN#{k}(#{cl},#{i});"
+ handle_def "Ruby _#{i}_grid(...) {"+
+ "if (in.size()<=#{i}) in.resize(#{i}+1);"+
+ "if (!in[#{i}]) in[#{i}]=new GridInlet((GridObject *)this,&#{cl}_grid_#{i}_hand);"+
+ "return in[#{i}]->begin(argc,argv);}"
+
+ }
+ $stack.pop
+ Out.puts "# #{$linenumber}"
+ end
+ if :ruby==frame then
+ if fields[0]!="ruby" then raise "expected \\end ruby" end
+ end
+ Out.puts ""
+end
+
+def handle_startall(line)
+ $classes.each {|q|
+ Out.print "rb_funcall(EVAL(\"GridFlow\"),SI(fclass_install),2,PTR2FIX(&ci#{q.name}),"
+ if q.supername then
+ Out.print "EVAL(\"GridFlow::#{q.supername}\"));"
+ else
+ Out.print "Qnil);"
+ end
+ }
+ Out.puts ""
+end
+
+def handle_ruby(line)
+ Out.puts ""
+ $stack.push :ruby
+end
+
+$rubymode=false
+$linenumber=1
+loop{
+ x = In.gets
+ break if not x
+ if /^\s*\\(\w+)\s*(.*)$/.match x then
+ begin
+ send("handle_#{$1}",$2)
+ rescue StandardError => e
+ STDERR.puts e.inspect
+ STDERR.puts "at line #{$linenumber}"
+ STDERR.puts e.backtrace
+ File.unlink ARGV[1]
+ exit 1
+ end
+ else
+ if $stack[-1]==:ruby then
+ x.gsub!(/([\\\"])/) { "\\"+$1 }
+ x="\"#{x.chomp}\\n\"\n"
+ end
+ Out.puts x
+ end
+ $linenumber+=1
+}
diff --git a/externals/gridflow/base/test.rb b/externals/gridflow/base/test.rb
new file mode 100644
index 00000000..1d44f918
--- /dev/null
+++ b/externals/gridflow/base/test.rb
@@ -0,0 +1,1086 @@
+# $Id: test.rb,v 1.1 2005-10-04 02:02:13 matju Exp $
+
+$:.delete_if {|x| x=='.' }
+require "gridflow"
+
+include GridFlow
+GridFlow.verbose=true
+
+$imdir = "./images"
+$animdir = "/opt/mex"
+srand Time.new.to_i
+$port = 4200+rand(100)
+
+def pressakey; puts "press return to continue."; readline; end
+
+class Expect < FObject
+ def praise(*a)
+ #raise(*a)
+ puts a
+ end
+ def expect(*v)
+ @count=0
+ @v=v
+ @expecting=true
+ yield
+ @expecting=false
+ praise "wrong number of messages (#{@count}), expecting #{@v.inspect}" if @count!=@v.length
+ end
+ def _0_list(*l)
+ return if not @expecting
+ praise "wrong number of messages (#{@count})" if @count==@v.length
+ praise "got #{l.inspect} expecting #{@v.inspect}" if @v[@count]!=l
+ @count+=1
+ end
+ def method_missing(s,*a)
+ praise "stray message: #{s}: #{a.inspect}"
+ end
+ install "expect", 1, 0
+end
+
+def test_bitpacking
+ #!@#$ WRITE ME
+end
+
+def test_operators
+ #!@#$ WRITE ME
+end
+
+def cast value, type
+ case type
+ when :uint8; value & 0xff
+ when :int16; (value & 0x7fff) - (value & 0x8000)
+ when :int32; value
+ when :int64; value
+ when :float32; value.to_f
+ when :float64; value.to_f
+ else raise "hell"
+ end
+end
+
+def test_math
+ hm = "#".intern
+#for nt in [:uint8, :int16, :int32, :int64, :float32, :float64] do
+for nt in [:uint8, :int16, :int32, :float32] do
+
+ #GridFlow.verbose = false
+ GridFlow.gfpost "starting test for #{nt}"
+ e = FObject["@export_list"]
+ x = Expect.new
+ e.connect 0,x,0
+
+ x.expect([1,2,3,11,12,13,21,22,23]) {
+ e.send_in 0, 3,3,nt,hm,1,2,3,11,12,13,21,22,23 }
+
+ a = FObject["fork"]
+ b = FObject["@ +"]
+ a.connect 0,b,0
+ a.connect 1,b,1
+ b.connect 0,e,0
+ x.expect([4]) { a.send_in 0, 2 }
+
+ x.expect([2,3,5,7]) { e.send_in 0,"list #{nt} 2 3 5 7" }
+ a = FObject["@fold + {#{nt} # 0}"]
+ a.connect 0,e,0
+ x.expect([cast(420000,nt)]) { a.send_in 0,"10000 #{nt} # 42" }
+
+ (a = FObject["@ + {#{nt} 0 10}"]).connect 0,e,0
+ x.expect([1,12,4,18,16,42,64]) {
+ a.send_in 0,:list,nt, 1,2,4,8,16,32,64 }
+
+ a = FObject["@ + {#{nt} 2 3 5}"]
+ b = FObject["@fold + {#{nt} # 0}"]
+ a.connect 0,b,0
+ b.connect 0,e,0
+ x.expect([cast(45332,nt)]) { a.send_in 0, 1000,nt,hm,42 }
+
+
+ (a = FObject["@ + {#{nt} # 42}"]).connect 0,e,0
+ x.expect((43..169).to_a) {
+ a.send_in 0,:list,nt, *(1..127).to_a }
+
+ x.expect([3,5,9,15]) {
+ a.send_in 1,:list,4,nt,hm, 2,3,5,7
+ a.send_in 0,:list,4,nt,hm, 1,2,4,8 }
+ x.expect([11,12,14,18]) {
+ a.send_in 1, "list #{nt} # 10"
+ a.send_in 0,:list,nt, 1,2,4,8 }
+
+if nt!=:uint8 and nt!=:float32 and nt!=:float64
+ (a = FObject["@ / {#{nt} # 3}"]).connect 0,e,0
+ x.expect([-2,-1,-1,-1,0,0,0,0,0,1,1,1,2]) {
+ a.send_in(0,:list,nt, *(-6..6).to_a) }
+
+ (a = FObject["@ div {#{nt} # 3}"]).connect 0,e,0
+ x.expect([-2,-2,-2,-1,-1,-1,0,0,0,1,1,1,2]) {
+ a.send_in(0, :list, nt, *(-6..6).to_a) }
+end
+
+ (a = FObject["@ ignore {#{nt} # 42}"]).connect 0,e,0
+ x.expect((42..52).to_a) { a.send_in(0, :list, nt, *(42..52).to_a) }
+
+ (a = FObject["@ put {#{nt} # 42}"]).connect 0,e,0
+ x.expect([42]*13) { a.send_in(0, :list, nt, *(-6..6).to_a) }
+
+if nt!=:uint8
+ (a = FObject["@! abs"]).connect 0,e,0
+ x.expect([2,3,5,7]) {
+ a.send_in 0,:list,nt, -2,3,-5,7 }
+end
+
+ (a = FObject["@fold * {#{nt} # 1}"]).connect 0,e,0
+ x.expect([210]) { a.send_in 0,:list,nt, 2,3,5,7 }
+ x.expect([128]) { a.send_in 0,:list,nt, 1,1,2,1,2,2,2,1,1,2,1,2,2 }
+
+ (a = FObject["@fold + {#{nt} 0 0}"]).connect 0,e,0
+ x.expect([18,23]) { a.send_in 0, 3,2,nt,hm,2,3,5,7,11,13 }
+
+ (a = FObject["@scan + {#{nt} 0 0}"]).connect 0,e,0
+ x.expect([2,3,7,10,18,23]) { a.send_in 0, 3,2,nt,hm,2,3,5,7,11,13 }
+
+ (a = FObject["@scan * {#{nt} # 1}"]).connect 0,e,0
+ x.expect([2,6,30,210]) { a.send_in 0,:list,nt, 2,3,5,7 }
+ x.expect([1,1,2,2,4,8,16,16,16,32,32,64,128]) {
+ a.send_in 0,:list,nt, 1,1,2,1,2,2,2,1,1,2,1,2,2 }
+
+ (a = FObject["@scan + {#{nt} 0 0 0}"]).connect 0,e,0
+ x.expect([1,2,3,5,7,9,12,15,18]) {
+ a.send_in 0,:list,3,3,nt,hm,*(1..9).to_a }
+
+ (a = FObject["@scan + {#{nt} # 0}"]).connect 0,e,0
+ x.expect([1,3,6, 4,9,15, 7,15,24]) {
+ a.send_in 0,:list,3,3,nt,hm,*(1..9).to_a }
+
+ (a = FObject["@outer +"]).connect 0,e,0
+ x.expect([9,10,12,17,18,20,33,34,36]) {
+ a.send_in 1,:list,nt, 1,2,4
+ a.send_in 0,:list,nt, 8,16,32 }
+
+ x.expect((0...100).to_a) {
+ a.send_in 1,(0...10).to_a
+ a.send_in 0,(0...10).map{|i| 10*i }}
+
+if nt!=:uint8 and nt!=:float32 and nt!=:float64
+ (a = FObject["@outer",:%,[nt,3,-3]]).connect 0,e,0
+ x.expect([0,0,1,-2,2,-1,0,0,1,-2,2,-1,0,0]) {
+ a.send_in 0,:list,nt, -30,-20,-10,0,+10,+20,+30 }
+
+ (a = FObject["@outer","swap%".intern,[nt,3,-3]]).connect 0,e,0
+ x.expect([-27,-3,-17,-3,-7,-3,0,0,3,7,3,17,3,27]) {
+ a.send_in 0,:list,nt, -30,-20,-10,0,+10,+20,+30 }
+end
+
+ (a = FObject["@import {3}"]).connect 0,e,0
+ x.expect([2,3,5]) { [2,3,5].each {|v| a.send_in 0,:list,nt,hm,v }}
+
+ (a = FObject["@import {3}"]).connect 0,e,0
+ x.expect([2,3,5]) { [2,3,5].each {|v| a.send_in 0,:list,nt,v }}
+
+ (a = FObject["@redim {5}"]).connect 0,e,0
+ x.expect([2,3,5,2,3]) { a.send_in 0,:list,2,3,5 }
+
+ (a = FObject["@redim {5}"]).connect 0,e,0
+ x.expect([0,0,0,0,0]) { a.send_in 0,:list }
+
+ (a = FObject["@redim {0}"]).connect 0,e,0
+ x.expect([]) { a.send_in 0,:list,42,37,69 }
+
+ (a = FObject["@inner * + {#{nt} # 0} {2 2 #{nt} # 2 3 5 7}"]).connect 0,e,0
+ (i0 = FObject["@redim {2 2}"]).connect 0,a,0
+ x.expect([12,17,48,68]) { i0.send_in 0,:list,nt, 1,2,4,8 }
+
+#if nt!=:int64
+if false
+ a = FObject["@print"]
+ a.send_in 0, "3 3 #{nt} # 1 0 0 0"
+ a.send_in 0, "3 3 3 #{nt} # 1 2 3 4"
+ a.send_in 0, "base 16"
+ a.send_in 0, "3 3 3 #{nt} # 255 0 0 0"
+end
+
+ (a = FObject["@outer * {3 2 #{nt} # 1 2 3}"]).connect 0,e,0
+ b = FObject["@dim"]
+ c = FObject["@export_list"]
+ a.connect 0,b,0
+ y = Expect.new
+ b.connect 0,c,0
+ c.connect 0,y,0
+
+ y.expect([2,3,2]) {
+ x.expect([1,2,3,1,2,3,10,20,30,10,20,30]) {
+ a.send_in 0,:list,nt, 1, 10 }}
+
+ #pr=GridPrint.new
+ (b = FObject["@redim {5 5}"]).connect 0,e,0
+ (a = FObject["@convolve * + {#{nt} # 0}"]).connect 0,b,0
+ (i0 = FObject["@redim {5 5 1}"]).connect 0,a,0
+ (i1 = FObject["@redim {3 1}"]).connect 0,a,1
+ i1.send_in 1, 3,3
+ x.expect([5,6,5,4,4,4,6,7,6,4,3,3,6,7,5,4,2,3,6,6,5,4,3,4,5]) {
+ a.send_in 1,:list,3,3,nt,hm, 1,1,1,1,1,1,1,1,1
+ i0.send_in 0,:list,nt, 1,1,1,0,0,0 }
+
+ (a = FObject["@convolve * + {#{nt} # 0}"]).connect 0,e,0
+ x.expect([1,3,6,4,0]) {
+ a.send_in 1, 1,2,nt,hm, 1,1
+ a.send_in 0, 1,5,nt,hm, 0,1,2,4,0 }
+
+ (a = FObject["@import {4}"]).connect 0,e,0
+ x.expect([2,3,5,7]) {
+ [2,3,5,7].each {|v| a.send_in 0,v }}
+ x.expect([1,2,3],[4,5,6],[7,8,9]) {
+ a.send_in 1, :list, 3
+ a.send_in 0, :list, *(1..9).to_a}
+
+ for o in ["@store"]
+ (a = FObject[o]).connect 0,e,0
+ a.send_in 1, 5, 4, nt, hm, 1,2,3,4,5
+ x.expect([1,2,3,4,4,5,1,2,2,3,4,5]) {
+ a.send_in 0, 3,1, hm, 0,2,4 }
+ x.expect([1,2,3,4,5]*24) { a.send_in 0, 2,3,0,hm }
+ x.expect([1,2,3,4,5]*4) { a.send_in 0, 0,hm }
+ x.expect([1,2,3,4,5]*4) { a.send_in 0 }
+ x.expect([1,2,3,4]) { a.send_in 0,[0] }
+ a.send_in 1,:put_at,[0,0]
+ a.send_in 1,2,2,nt,hm,6,7,8,9
+ x.expect([6,7,3,4, 8,9,2,3, 4,5,1,2, 3,4,5,1, 2,3,4,5]) { a.send_in 0 }
+ x.expect([6,7,3,4]) { a.send_in 0,[0] }
+ x.expect([8,9,2,3]) { a.send_in 0,[1] }
+ a.send_in 1,:put_at,[1,1]
+ a.send_in 1,2,2,nt,hm,11,13,17,19
+ x.expect([6,7,3,4, 8,11,13,3, 4,17,19,2, 3,4,5,1, 2,3,4,5]) { a.send_in 0 }
+ end
+
+ b = FObject["@dim"]
+ c = FObject["@export_list"]
+ a.connect 0,b,0
+ y = Expect.new
+ b.connect 0,c,0
+ c.connect 0,y,0
+
+if nt!=:uint8 and nt!=:float32 and nt!=:float64 and nt!=:int64
+ (a = FObject["@for {#{nt} # 0} {#{nt} # 10} {#{nt} # 1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([10]) {
+ x.expect((0...10).to_a) {
+ a.send_in 0 } }
+
+ (a = FObject["@for {#{nt} # 0} {#{nt} # -10} {#{nt} # 1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([0]) { x.expect([]) { a.send_in 0 } }
+
+ (a = FObject["@for {#{nt} # 0} {#{nt} # -10} {#{nt} # -1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([10]) { x.expect([0,-1,-2,-3,-4,-5,-6,-7,-8,-9]) { a.send_in 0 } }
+
+ (a = FObject["@for {#{nt} 0} {#{nt} 10} {#{nt} 1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([10,1]) {
+ x.expect((0...10).to_a) {
+ a.send_in 0 } }
+
+ (a = FObject["@for {#{nt} 2 3} {#{nt} 5 7} {#{nt} 1 1}"]).connect 0,e,0
+ a.connect 0,b,0
+ y.expect([3,4,2]) {
+ x.expect([2,3,2,4,2,5,2,6,3,3,3,4,3,5,3,6,4,3,4,4,4,5,4,6]) {
+ a.send_in 0 } }
+end
+
+ (a = FObject["@complex_sq"]).connect 0,e,0
+ x.expect([8,0]) { a.send_in 0, 2, 2 }
+ x.expect([0,9]) { a.send_in 0, 0, 3 }
+
+ (a = FObject["@rotate 3000 {1 2 5}"]).connect 0,e,0
+# pr = GridPrint.new
+# a.connect 0,pr,0
+# x.expect([]) {
+ a.send_in 0, "5 5 # 1000 0 0 0 0 0"
+# }
+
+#if nt==:float32 or nt==:float64
+# (a = FObject["@matrix_solve"]).connect 0,e,0
+# x.expect([1,0,0,0,1,0,0,0,1]) { a.send_in 0, 3, 3, nt, hm, 1,0,0,0,1,0,0,0,1 }
+#end
+ GridFlow.gfpost "ending test for #{nt}"
+end # for nt
+
+ (a = FObject["@two"]).connect 0,e,0
+ x.expect([42,0]) { a.send_in 0,42 }
+ x.expect([42,28]) { a.send_in 1,28 }
+ x.expect([1313,28]) { a.send_in 0,1313 }
+
+ (a = FObject["@three"]).connect 0,e,0
+ x.expect([42,0,0]) { a.send_in 0,42 }
+ x.expect([42,28,0]) { a.send_in 1,28 }
+ x.expect([42,28,-1]) { a.send_in 2,-1 }
+
+ (a = FObject["@four"]).connect 0,e,0
+ x.expect([42,0,0,0]) { a.send_in 0,42 }
+ x.expect([42,0,0,-42]) { a.send_in 3,-42 }
+
+# glob = FObject["@global"]
+# glob.send_in 0, "profiler_dump"
+
+ e = FObject["@export_list"]
+ e.connect 0,x,0
+
+ a = FObject["@import per_message"]
+ a.connect 0,e,0
+ x.expect([1,2,3]) { a.send_in 0,1,2,3 }
+ x.expect([102,111,111]) { a.send_in 0,:symbol,:foo }
+ x.expect([ 70, 79, 79]) { a.send_in 0,:symbol,:FOO }
+
+ a = FObject["@join 1"]
+ a.connect 0,e,0
+ a.send_in 1,2,2,nt,hm,11,13,17,19
+ x.expect([2,3,11,13,5,7,17,19]) { a.send_in 0,2,2,nt,hm,2,3,5,7 }
+
+if nt!=:float64; #!@#$
+ a.send_in 1, 5,1,nt,hm,42
+ y.expect([5,4]) {
+ x.expect([2,3,5,42,7,11,13,42,17,19,23,42,29,31,37,42,41,43,47,42]) {
+ a.send_in 0, 5,3,nt,hm,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47 }}
+end
+ a = FObject["@join 0"]
+ a.connect 0,e,0
+ a.send_in 1,2,2,nt,hm,11,13,17,19
+ x.expect([2,3,5,7,11,13,17,19]) { a.send_in 0,2,2,nt,hm,2,3,5,7 }
+
+ a = FObject["@join 0 {2 2 2 #{nt} # 1 2 3}"]
+ a.connect 0,e,0
+ a.connect 0,b,0
+ y.expect([2,2,2]) { x.expect([1,2,3,1,2,3,1,2]) { a.send_in 0,0,2,2,nt,hm }}
+
+ a = FObject["@ravel"]
+ b = FObject["@dim"]
+ be = FObject["@export_list"]
+ bx = Expect.new
+ a.connect 0,e,0
+ a.connect 0,b,0
+ b.connect 0,be,0
+ be.connect 0,bx,0
+ bx.expect([9]) {
+ x.expect([1,2,3,2,4,6,3,6,9]) {
+ o = FObject["@outer *"]
+ o.connect 0,a,0
+ o.send_in 1,1,2,3
+ o.send_in 0,1,2,3
+ }
+ }
+
+ a = FObject["@grade"]
+ a.connect 0,e,0
+ x.expect([0,2,4,6,8,9,7,5,3,1]) { a.send_in 0, 0,9,1,8,2,7,3,6,4,5 }
+ x.expect([0,9,1,8,2,7,3,6,4,5]) { a.send_in 0, 0,2,4,6,8,9,7,5,3,1 }
+ x.expect([7,6,5,4,3,2,1,0]) { a.send_in 0, 7,6,5,4,3,2,1,0 }
+
+ a = FObject["@grade"]
+ b = FObject["@fold +"]
+ a.connect 0,b,0
+ b.connect 0,e,0
+ x.expect([100*99/2]) { a.send_in 0, (0...100).map { (rand*0x10000).to_i }}
+ x.expect([100*99/2]) { a.send_in 0, (0...100).map { (rand*0x10).to_i }}
+ x.expect([100*99/2]) { a.send_in 0, (0...100).map { 0 }}
+
+ a = FObject["@perspective"]
+ a.connect 0,e,0
+ c = []
+ 8.times {|v|
+ 3.times {|i|
+ c << (v[i] * 1000 - 500) + (if i==2 then 2000 else 0 end)
+ }
+ }
+ x.expect([
+ -85,-85,85,-85,-85,85,85,85,
+ -51,-51,51,-51,-51,51,51,51]) {
+ a.send_in 0, 8,3,hm,*c }
+
+# regressiontests for past bugs
+ a = FObject["@inner"] # that's it.
+end
+
+def test_rtmetro
+ rt = FObject["rtmetro 1000"]
+ pr = FObject["rubyprint"]
+ rt.connect 0,pr,0
+ GridFlow.post "trying to start the rtmetro"
+ rt.send_in 0,1
+ $mainloop.timers.after(10.0) {
+ GridFlow.post "trying to stop the rtmetro (after 10 sec delay)"
+ rt.send_in 0,0
+ }
+ $mainloop.loop
+end
+
+def test_print
+ i = FObject["@redim {3}"]
+ pr = FObject["@print"]
+# pr = GridFlow::RubyPrint.new
+ i.connect 0,pr,0
+ i.send_in 0, 85, 170, 255
+ i.send_in 1, 3, 3
+ i.send_in 0, 1, 0, 0, 0
+ i.send_in 1, 2, 2, 2
+ i.send_in 0, 2, 3, 5, 7, 11, 13, 17, 19
+end
+
+class Barf < GridObject
+ def _0_rgrid_begin
+ raise "barf"
+ end
+ install_rgrid 0
+ install "barf", 1, 0
+end
+
+def test_nonsense
+# (a = FObject["@! abs"]).connect 0,e,0
+# x.expect_error {
+# a.send_in 1, 42,42 }
+
+ a = FObject["@import {3}"]
+ b = Barf.new
+ a.connect 0,b,0
+ begin
+ a.send_in 0, 1, 2, 3
+ rescue StandardError
+ nil
+ else
+ raise "Expected StandardError"
+ end
+ p b.inlet_dim(0)
+end
+
+def test_store
+ a = FObject["@in file #{$imdir}/teapot.png"]
+ b = FObject["@store"]
+ c = FObject["@out x11"]
+ a.connect 0,b,1
+ a.send_in 0,"cast uint8"
+ a.send_in 0
+ b.connect 0,c,0
+ d = FObject["@for {0 0} {256 256} {1 1}"]
+ e = FObject["@ ^ 85"]
+ d.connect 0,e,0
+ e.connect 0,b,0
+ f = FObject["fps detailed"]
+ c.connect 0,f,0
+ pr = FObject["rubyprint"]
+ f.connect 0,pr,0
+ GridFlow.verbose = false
+ 256.times {|t|
+ e.send_in 1,t
+ d.send_in 0
+ }
+end
+
+# generates recursive checkerboard pattern (munchies) in bluish colours.
+class Munchies < FPatcher
+ @fobjects = ["fork","fork","@for 0 64 1","@for 0 64 1","@for 2 5 1",
+ "@outer ^","@outer *"]
+ @wires = [-1,0,0,0, 0,0,1,0, 1,1,4,0, 4,0,6,1,
+ 1,0,3,0, 3,0,5,1, 0,0,2,0, 2,0,5,0, 5,0,6,0, 6,0,-1,0 ]
+ def initialize() super end
+ install "munchies",1,1
+end
+
+def test_munchies
+ m=Munchies.new
+ gout = FObject["@out quartz"]
+ m.connect 0,gout,0
+ m.send_in 0
+ $mainloop.loop
+end
+
+def test_image command
+ gin = FObject["@in"]
+ gout = FObject["@out x11"]
+ gin.connect 0,gout,0
+# 31.times {
+ 3.times {
+ gin.send_in 0,"open #{command}"
+ gout.send_in 0,"timelog 1"
+ gin.send_in 0
+ }
+ FObject["@global"].send_in 0, "profiler_dump"
+ $mainloop.loop
+end
+
+def test_ppm2
+ gin = FObject["@in"]
+ store = FObject["@store"]
+ pa = FObject["@convolve << + 0"]
+ pb = FObject["@ / 9"]
+ ra = FObject["@redim {3 3}"]
+ gout = FObject["@out x11"]
+ gin.connect 0,store,1
+ store.connect 0,pa,0
+ pa.connect 0,pb,0
+ pb.connect 0,gout,0
+ ra.connect 0,pa,1
+ ra.send_in 0,"0 0"
+ gout.send_in 0,"timelog 1"
+ gin.send_in 0,"open file #{$imdir}/teapot.png"
+# gin.send_in 0,"open file #{$imdir}/g001.jpg"
+ gin.send_in 0
+# 40.times { store.send_in 0 }
+ loop { store.send_in 0 }
+ v4j = FObject["@global"]
+ v4j.send_in 0,"profiler_dump"
+# $mainloop.loop
+end
+
+def test_foo
+ foo = FObject["@for {0 0} {64 64} {1 1}"]
+ che = FObject["@checkers"]
+ sca = FObject["@scale_by {5 3}"]
+ out = FObject["@out x11"]
+ foo.connect 0,che,0
+ che.connect 0,sca,0
+ sca.connect 0,out,0
+ foo.send_in 0
+ $mainloop.loop
+end
+
+def test_anim(*msgs)
+ GridFlow.verbose = false
+ gin = FObject["@in"]
+ #gout1 = FObject["@out x11"]
+ gout1 = FObject["@out sdl"]
+ #gout1 = FObject["@out quicktime file test.mov"]
+ #gout1.send_in 0, :codec, :jpeg
+ fps = FObject["fps detailed"]
+ rpr = FObject["rubyprint"]
+ gout1.connect 0,fps,0
+ #fps.connect 0,rpr,0
+=begin
+ gout1 = FObject["@downscale_by {3 2}"]
+ gout2 = FObject["@rgb_to_greyscale"]
+ gout3 = FObject["@out aalib X11 -height 60 -width 132"]
+ gout1.connect 0,gout2,0
+ gout2.connect 0,gout3,0
+=end
+
+ rpr = FObject["rubyprint"]
+# gin.connect 1,rpr,0
+
+ gin.connect 0,gout1,0
+=begin
+ layer=FObject["@layer"]
+ gin.connect 0,layer,0
+ layer.connect 0,gout1,0
+ check=FObject["@checkers"]
+ phor=FObject["@for {0 0} {256 256} {1 1}"]
+ phor.connect 0,check,0
+ check.connect 0,layer,1
+ phor.send_in 0
+=end
+
+# scale = FObject["@scale_by 3"]
+# gin.connect 0,scale,0
+# scale.connect 0,gout1,0
+
+# pr = FObject["rubyprint time"]; gout.connect 0,pr,0
+ msgs.each {|m| gin.send_in 0,m }
+ gin.send_in 0, "cast uint8"
+# gout.send_in 0,"timelog 1"
+ d=Time.new
+ frames=2000
+ frames.times {|n|
+ #GridFlow.post "%d", n
+ gin.send_in 0
+ #gin.send_in 0, rand(1000)
+ }
+# loop { gin.send_in 0 }
+# metro = FObject["rtmetro 80"]
+# metro.connect 0,gin,0
+# metro.send_in 0,1
+# $mainloop.loop
+
+ d=Time.new-d
+ printf "%d frames in %.6f seconds (avg %.6f ms, %.6f fps)\n",
+ frames, d, 1000*d/frames, frames/d
+# global.send_in 0,"dfgdfgdkfjgl"
+ gout1.send_in 0, :close
+ global = FObject["@global"]
+ global.send_in 0,"profiler_dump"
+end
+
+class TestTCP < FObject
+ attr_accessor :idle
+ def initialize
+ @idle = true
+ end
+ def _0_bang
+ # GridFlow.gfpost "tick"
+ # avoid recursion
+ $mainloop.timers.after(0) {
+ ($in_client.send_in 0; @idle=false) if @idle
+ }
+ end
+ install "tcptest",1,1
+end
+
+def test_tcp
+ if fork
+ # client (is receiving)
+ GridFlow.post_header = "[client] "
+ $in_client = in1 = FObject["@in"]
+ out = FObject["@out x11"]
+ in1.connect 0,out,0
+ out.send_in 0,"timelog 1"
+ out.send_in 0,"autodraw 2"
+ GridFlow.post "test: waiting 1 second"
+ sleep 1
+ p "HI"
+ #in1.send_in 0,"open grid tcp localhost #{$port}"
+ in1.send_in 0,"open grid tcp 127.0.0.1 #{$port}"
+ p "HI"
+
+ test_tcp = TestTCP.new
+
+ out.connect 0,test_tcp,0
+ test_tcp.connect 0,in1,0
+
+ GridFlow.post "entering mainloop..."
+ $mainloop.loop
+ else
+ # server (is sending)
+ GridFlow.post_header = "[server] "
+ $in1_server = in1 = FObject["@in"]
+ $in2_server = in2 = FObject["@in"]
+ $out = out = FObject["@out"]
+ toggle = 0
+ in1.connect 0,out,0
+ in2.connect 0,out,0
+ in1.send_in 0,"open #{$imdir}/r001.jpg"
+ in2.send_in 0,"open #{$imdir}/b001.jpg"
+ out.send_in 0,"open grid tcpserver #{$port}"
+ out.send_in 0,"type uint8"
+ test_tcp = GridFlow::FObject.new
+ def test_tcp._0_bang
+ # GridFlow.post "tick"
+ @toggle ||= 0
+ # avoid recursion
+ $mainloop.timers.after(0.01) {
+ if $out.format.stream
+ if @toggle==0; $in1_server else $in2_server end.send_in 0
+ @toggle ^= 1
+ end
+ _0_bang
+ }
+ end
+ out.connect 0,test_tcp,0
+ test_tcp.send_in 0
+ GridFlow.post "entering mainloop..."
+ $mainloop.loop
+ end
+end
+
+def test_layer
+
+ gin = FObject["@in png file ShaunaKennedy/atmosphere-bleu.png"]
+# gin1 = FObject["@in file #{$imdir}/r001.jpg"]
+# gin = FObject["@join 2 {240 320 1 # 128}"]
+# gin1.connect 0,gin,0
+
+# gfor = FObject["@for {0 0} {120 160} {1 1}"]
+# gfor = FObject["@for {0 0} {240 320} {1 1}"]
+ gfor = FObject["@for {0 0} {480 640} {1 1}"]
+ gche = FObject["@checkers"]
+
+ gove = FObject["@layer"]
+# gove = FObject["@fold + {0 0 0 0}"]
+# gout = FObject["@print"]
+# gove = FObject["@inner2 * + 0 {3 4 # 1 0 0 0 0}"]
+# gout = FObject["@out sdl"]
+ gout = FObject["@out x11"]
+
+ gin.connect 0,gove,0
+ gfor.connect 0,gche,0
+ gche.connect 0,gove,1
+ gove.connect 0,gout,0
+
+ gfor.send_in 0
+
+ fps = FObject["fps detailed"]
+ pr = FObject["rubyprint"]
+ gout.connect 0,fps,0
+ fps.connect 0,pr,0
+
+ loop{gin.send_in 0}
+# gin.send_in 0
+# gin1.send_in 0
+ $mainloop.loop
+end
+
+Images = [
+ "png file opensource.png",
+# "png file ShaunaKennedy/atmosphere.png",
+# "targa file #{$imdir}/tux.tga",
+# "png file #{$imdir}/lena.png",
+ "file #{$imdir}/ruby0216.jpg",
+ "file #{$imdir}/g001.jpg",
+ "file #{$imdir}/teapot.tga",
+ "grid gzfile #{$imdir}/foo.grid.gz",
+ "grid gzfile #{$imdir}/foo2.grid.gz",
+# "videodev /dev/video0",
+]
+
+def test_formats_speed
+ gin = FObject["@in"]
+ gout = FObject["@out x11"]
+ gin.connect 0,gout,0
+ GridFlow.verbose=false
+ t1=[]
+ Images.each {|command|
+ gin.send_in 0,"open #{command}"
+ t0 = Time.new
+ 10.times {gin.send_in 0}
+ t1 << (Time.new - t0)
+ sleep 1
+ }
+ p t1
+end
+
+def test_formats
+ gin = FObject["@in"]
+ gout = FObject["@out window"]
+ gs = FObject["@ + {int16 # 0}"]
+ gin.connect 0,gs,0
+ gs.connect 0,gout,0
+# GridFlow.verbose=false
+ t1=[]
+ Images.each {|command|
+ gin.send_in 0,"open #{command}"
+ gin.send_in 0,"cast int16"
+ # test for load, rewind, load
+ 5.times {|x| gs.send_in 1, [:int16, '#'.intern, x*128]; gin.send_in 0}
+ # test for filehandle leak
+ #1000.times { gin.send_in 0,"open #{command}" }
+ sleep 1
+ }
+ p t1
+end
+
+def test_rewind
+ gin = FObject["@in videodev /dev/video1 noinit"]
+ gin.send_in 0, "transfer read"
+ gout = FObject["@out ppm file /tmp/foo.ppm"]
+# gout = FObject["@out x11"]
+ gin.connect 0,gout,0
+ loop {
+ gin.send_in 0
+ gout.send_in 0, "rewind"
+ }
+end
+
+def test_formats_write
+ # read files, store and save them, reload, compare, expect identical
+ a = FObject["@in"]
+ b = FObject["@out"]; a.connect 0,b,0
+ c = FObject["@ -"]; a.connect 0,c,1
+ d = FObject["@in"]; d.connect 0,c,0
+ e = FObject["@fold +"]; c.connect 0,e,0
+ f = FObject["@fold +"]; e.connect 0,f,0
+ g = FObject["@fold +"]; f.connect 0,g,0
+ h = FObject["@ / 15000"]; g.connect 0,h,0
+ i = FObject["@export_list"]; h.connect 0,i,0
+ x = Expect.new; i.connect 0,x,0
+ [
+ ["ppm file", "#{$imdir}/g001.jpg"],
+ ["targa file", "#{$imdir}/teapot.tga"],
+ ["targa file", "#{$imdir}/tux.tga"],
+ ["jpeg file", "#{$imdir}/ruby0216.jpg"],
+ ["grid gzfile", "#{$imdir}/foo.grid.gz", "endian little"],
+ ["grid gzfile", "#{$imdir}/foo.grid.gz", "endian big"],
+ ].each {|type,file,*rest|
+ a.send_in 0, "open #{type} #{file}"
+ b.send_in 0, "open #{type} /tmp/patate"
+ rest.each {|r| b.send_in 0,r }
+ a.send_in 0
+ b.send_in 0, "close"
+ raise "written file does not exist" if not File.exist? "/tmp/patate"
+ d.send_in 0, "open #{type} /tmp/patate"
+ x.expect([0]) { d.send_in 0 }
+# d.send_in 0
+ }
+end
+
+def test_mpeg_write
+ a = FObject["@in ppm file /opt/mex/r.ppm.cat"]
+ b = FObject["@out x11"]
+ a.connect 0,b,0
+ loop{a.send_in 0}
+end
+
+def test_headerless
+ gout = FObject["@out"]
+ gout.send_in 0, "open grid file #{$imdir}/hello.txt"
+ gout.send_in 0, "headerless"
+ gout.send_in 0, "type uint8"
+ gout.send_in 0, "104 101 108 108 111 32 119 111 114 108 100 33 10"
+ gout.send_in 0, "close"
+ gin = FObject["@in"]
+ pr = FObject["@print"]
+ gin.connect 0,pr,0
+ gin.send_in 0, "open grid file #{$imdir}/hello.txt"
+ gin.send_in 0, "headerless 13"
+ gin.send_in 0, "type uint8"
+ gin.send_in 0
+end
+
+
+def test_sound
+# o2 = FObject["@ * 359"] # @ 439.775 Hz
+# o1 = FObject["@for 0 44100 1"] # 1 sec @ 1.225 Hz ?
+ o1 = FObject["@for 0 4500 1"]
+ o2 = FObject["@ * 1600"] # @ 439.775 Hz
+ o3 = FObject["@ sin* 255"]
+ o4 = FObject["@ gamma 400"]
+ o5 = FObject["@ << 7"]
+ out = FObject["@out"]
+ o1.connect 0,o2,0
+ o2.connect 0,o3,0
+ o3.connect 0,o4,0
+ o4.connect 0,o5,0
+ o5.connect 0,out,0
+# out.send_in 0,"open raw file /dev/dsp"
+ out.send_in 0,"open grid file /dev/dsp"
+ out.send_in 0,"type int16"
+ x=0
+ loop {
+ o4.send_in 1, x
+ o1.send_in 0
+ x+=10
+ }
+end
+
+include Math
+def test_polygon
+ o1 = FObject["@for 0 5 1"]
+ o2 = FObject["@ * 14400"]
+ o3 = FObject["@outer + {0 9000}"]
+ o4 = FObject["@ +"]
+ o5 = FObject["@ cos* 112"]
+ o6 = FObject["@ + {120 160}"]
+ poly = FObject["@draw_polygon + {3 uint8 # 255}"]
+if false
+ out1 = FObject["@cast int32"]
+ out2 = FObject["@solarize"]
+# out1 = FObject["@downscale_by 2 smoothly"]
+ out3 = FObject["@out x11"]
+ out1.connect 0,out2,0
+ out2.connect 0,out3,0
+else
+ out1 = FObject["@out x11"]
+ fps = FObject["fps detailed cpu"]
+ out1.connect 0,fps,0
+ pr = FObject["rubyprint"]
+ fps.connect 0,pr,0
+end
+ store = FObject["@store"]; store.send_in 1, "240 320 3 uint8 # 0"
+# store2 = FObject["@store"]
+ store.connect 0,poly,0
+ poly.connect 0,store,1
+# store2.connect 0,store,1
+ o1.connect 0,o2,0
+ o2.connect 0,o3,0
+ o3.connect 0,o4,0
+ o4.connect 0,o5,0
+ o5.connect 0,o6,0
+ o6.connect 0,poly,2
+# cast = FObject["@cast int32"]
+# poly.connect 0,cast,0
+# cast.connect 0,out1,0
+ poly.connect 0,out1,0
+ x=0
+ GridFlow.verbose=false
+ task=proc {
+ o4.send_in 1, 5000*x
+ o5.send_in 1, 200+200*sin(x)
+ poly.send_in 1,:list,:uint8, *(0..2).map{|i| 4+4*cos(0.2*x+i*PI*2/3) }
+ o1.send_in 0
+ store.send_in 0
+# store2.send_in 0
+ x+=1
+ if x<1000 then $mainloop.timers.after(0.0) {task[]}
+ else GridGlobal.new.send_in 0,"profiler_dump"; exit end
+ }
+ task[]
+ $mainloop.loop
+end
+
+class FRoute2 < FObject
+ def initialize(selector)
+ @selector = selector.to_s
+ end
+ def method_missing(sym,*a)
+ sym=sym.to_s
+ if sym =~ /^_0_(.*)/
+ send_out((if $1==@selector then 0 else 1 end), $1.intern, *a)
+ else super end
+ end
+ install "route2", 1, 2
+end
+
+def test_aalib
+# gin = FObject["@in ppm file #{$imdir}/r001.jpg"]
+ gin = FObject["@in ppm file #{$animdir}/b.jpg.cat"]
+ grey = FObject["@rgb_to_greyscale"]
+ cont = FObject["@ << 1"]
+ clip = FObject["@ min 255"]
+ gout = FObject["@out aalib X11"]
+ sto = FObject["@store"]
+ op = FObject["aa_fill_with_text {localhost 4242}"]
+ filt = FObject["route2 grid"]
+ gin.connect 0,grey,0
+ grey.connect 0,cont,0
+ cont.connect 0,clip,0
+ clip.connect 0,gout,0
+ gout.connect 0,filt,0
+ filt.connect 0,sto,1
+ sto.connect 0,op,0
+ op.connect 0,gout,0
+ gout.send_in 0, :autodraw, 0
+ GridFlow.verbose = false
+ task=proc{
+ gin.send_in 0
+ gout.send_in 0, :dump
+ sto.send_in 0
+ gout.send_in 0, :draw
+ $mainloop.timers.after(0.1,&task)
+ }
+ task[]
+ $mainloop.loop
+end
+
+def test_store2
+ o = [
+ FObject["@for {0 0} {240 320} {1 1}"],
+ FObject["@ / 2"],
+ FObject["@ + 0"],
+ FObject["@ + 0"],
+ FObject["@store"],
+ FObject["@out x11"]]
+ (0..4).each {|x| o[x].connect 0,o[x+1],0 }
+ q = FObject["@in ppm file images/r001.jpg"]
+ q.connect 0,o[4],1
+ q.send_in 0
+ o[0].send_in 0
+ $mainloop.loop
+end
+
+def test_remap
+ rem = FObject["@remap_image"]
+ rot = FObject["@rotate 4000"]
+ rem.connect 1,rot,0
+ rot.connect 0,rem,1
+ gin = FObject["@in ppm file #{$imdir}/teapot.png"]
+ gout = FObject["@out x11"]
+ gin.connect 0,rem,0
+ rem.connect 0,gout,0
+ gin.send_in 0
+ $mainloop.loop
+end
+
+def test_asm
+ GridFlow.verbose=false
+ a = FObject["@in ppm file images/r001.jpg"]
+ aa = FObject["@cast uint8"]
+ b = FObject["@store"]
+ d = FObject["@store"]
+ a.connect 0,aa,0
+ aa.connect 0,b,1
+ aa.connect 0,d,1
+ a.send_in 0
+ c = FObject["@ + {uint8 # 0}"]
+ t0 = Time.new; 1000.times {b.send_in 0}; t1 = Time.new-t0
+ t1 *= 1
+ b.connect 0,c,0
+ stuff=proc{
+ 3.times{
+ t0 = Time.new; 1000.times {b.send_in 0}; t2 = Time.new-t0
+ t2 *= 1
+ GridFlow.post " %f %f %f", t1, t2, t2-t1
+ }
+ }
+ puts "map:"
+ stuff[]
+ d.connect 0,c,1 # for zip
+ d.send_in 0
+ puts "zip:"
+ stuff[]
+end
+
+def test_metro
+ o1 = RtMetro.new(1000,:geiger)
+ o2 = RubyPrint.new(:time)
+ o1.connect 0,o2,0
+ o1.send_in 0,1
+ $mainloop.loop
+end
+
+def test_outer
+ o = FObject["@outer + {0}"]
+ o.send_in 0, 25, 240, 320, 3, "#".intern, 42
+ g = FObject["@global"]
+ g.send_in 0, :profiler_dump
+end
+
+def test_jmax_to_pd filename
+ require "gridflow/extra/jmax_format.rb"
+ require "gridflow/extra/puredata_format.rb"
+ jfr = JMaxFileReader.new(File.open(filename),FObject)
+ FObject.broken_ok = true
+ my_patcher = jfr.parse
+# my_patcher.subobjects.each {|x,| x.trigger if LoadBang===x }
+# $mainloop.loop
+# $p=my_patcher; ARGV.clear; load "/home/matju/bin/iruby"
+ filename = File.basename filename
+ filename[File.extname(filename)]=".pd"
+ filename[0,0]="pd_examples/"
+ pfw = PureDataFileWriter.new(filename)
+ pfw.write_patcher my_patcher
+ pfw.close
+end
+
+def test_error
+ x = FObject["@store"]
+ x.send_in 0
+end
+
+if ARGV[0] then
+ name = ARGV.shift
+ send "test_#{name}", *ARGV
+# ARGV.each {|a| send "test_#{a}" }
+ exit 0
+end
+
+#test_polygon
+#test_math
+#test_munchies
+#test_image "grid file #{$imdir}/foo.grid"
+#test_image "grid gzfile #{$imdir}/foo.grid.gz"
+#test_print
+#test_nonsense
+#test_ppm2
+#test_anim "open file #{$imdir}/g001.jpg"#,"loop 0"
+#test_anim "open ppm file #{$animdir}/b.ppm.cat"
+#test_anim "open jpeg file #{$imdir}/rgb.jpeg.cat"
+#test_anim "open quicktime file BLAH"
+#test_anim "open quicktime file #{$imdir}/rgb_uncompressed.mov"
+#test_anim "open quicktime file #{$imdir}/test_mjpega.mov"
+#test_anim "open ppm gzfile motion_tracking.ppm.cat.gz"
+#test_anim "open videodev /dev/video","channel 1","size 480 640"
+#test_anim "open videodev /dev/video1 noinit","transfer read"
+#test_anim "open videodev /dev/video","channel 1","size 120 160"
+#test_anim "open mpeg file /home/matju/net/Animations/washington_zoom_in.mpeg"
+#test_anim "open quicktime file /home/matju/Shauna/part_1.mov"
+#test_anim "open quicktime file #{$imdir}/gt.mov"
+#test_anim "open quicktime file /home/matju/pics/domopers_hi.mov"
+#test_anim "open quicktime file /home/matju/net/c.mov"
+#test_formats
+#test_tcp
+#test_sound
+#test_metro
+#$mainloop.loop