diff options
author | N.N. <matju@users.sourceforge.net> | 2005-10-04 02:02:15 +0000 |
---|---|---|
committer | N.N. <matju@users.sourceforge.net> | 2005-10-04 02:02:15 +0000 |
commit | 5e2a1bc9e56003349e533f7e5841041ba5c04e28 (patch) | |
tree | ad040f6894d9383b732423a74420e732f62a66a5 /externals/gridflow/base | |
parent | 520a243c297175386ab31c78c84693a664934a69 (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.c | 309 | ||||
-rw-r--r-- | externals/gridflow/base/flow_objects.c | 1189 | ||||
-rw-r--r-- | externals/gridflow/base/flow_objects.rb | 1476 | ||||
-rw-r--r-- | externals/gridflow/base/flow_objects_for_image.c | 618 | ||||
-rw-r--r-- | externals/gridflow/base/flow_objects_for_matrix.c | 77 | ||||
-rw-r--r-- | externals/gridflow/base/grid.c | 604 | ||||
-rw-r--r-- | externals/gridflow/base/grid.h | 1109 | ||||
-rw-r--r-- | externals/gridflow/base/main.c | 647 | ||||
-rw-r--r-- | externals/gridflow/base/main.rb | 369 | ||||
-rw-r--r-- | externals/gridflow/base/number.c | 365 | ||||
-rw-r--r-- | externals/gridflow/base/source_filter.rb | 274 | ||||
-rw-r--r-- | externals/gridflow/base/test.rb | 1086 |
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 |