www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - help. resizing dynamic array of a sound effect class.

reply clayasaurus <clayasaurus_member pathlink.com> writes:
hello. i'm trying to make it so in my game engine sound effects are dynamic. 

That is, you add a sound effect to the manager, play it, and when it is done
playing, you remove it. every frame i run a process called 'process' which
checks to see if the sound effects are done playing, and if it does, it deletes
them. 

i've tried deleting the sound effects using the 'delete soundFX[index];'
command, but this gives me a segmentation fault. instead i'm using my own sloppy
function 'removeSoundFXInArray(soundFX, num)'. this function seems to work and
doesn't cause a crash (well as long as not too many sounds are playing, i think
i can fix this though)


here is the supplemental code...

void process()
{
for (int i = 0; i < soundFX.length; i++) // delete the soundFX if they are not
playing
{
if (!soundFX[i].playing()) // if soundFX is not playing
{
//delete soundFX[i]; // theoretically, this should work, it doesn't
soundFX = removeSoundFXInArray(soundFX, i); // this does, but it's slower

printf("soundfx done playing. deleted.\n");
}
}
}


SoundFX[] removeSoundFXInArray(inout SoundFX array[], int remove)
{// removes a value in an array and returns the new value
SoundFX newarray[];

newarray = array[0 .. (remove)];
newarray ~= array[(remove+1) .. array.length];

delete array[remove]; // so it doesn't hang around in memory

return newarray;
}

anyway, i'd figured i was doing something wrong with the delete operator in
'delete soundFX[i]' which was causing the seg fault. It crashes as soon as it
tries to delete it that way. but my other way works for some reason. 

i'm not sure what i'd be doing wrong though. i'd appreciate some help. thx :)
Jun 28 2004
next sibling parent reply Brian <Brian_member pathlink.com> writes:
In article <cbpv4s$2287$1 digitaldaemon.com>, clayasaurus says...
hello. i'm trying to make it so in my game engine sound effects are dynamic. 

That is, you add a sound effect to the manager, play it, and when it is done
playing, you remove it. every frame i run a process called 'process' which
checks to see if the sound effects are done playing, and if it does, it deletes
them. 

i've tried deleting the sound effects using the 'delete soundFX[index];'
command, but this gives me a segmentation fault. instead i'm using my own sloppy
function 'removeSoundFXInArray(soundFX, num)'. this function seems to work and
doesn't cause a crash (well as long as not too many sounds are playing, i think
i can fix this though)


here is the supplemental code...

void process()
{
for (int i = 0; i < soundFX.length; i++) // delete the soundFX if they are not
playing
{
if (!soundFX[i].playing()) // if soundFX is not playing
{
//delete soundFX[i]; // theoretically, this should work, it doesn't
soundFX = removeSoundFXInArray(soundFX, i); // this does, but it's slower

printf("soundfx done playing. deleted.\n");
}
}
}


SoundFX[] removeSoundFXInArray(inout SoundFX array[], int remove)
{// removes a value in an array and returns the new value
SoundFX newarray[];

newarray = array[0 .. (remove)];
newarray ~= array[(remove+1) .. array.length];

delete array[remove]; // so it doesn't hang around in memory

return newarray;
}

anyway, i'd figured i was doing something wrong with the delete operator in
'delete soundFX[i]' which was causing the seg fault. It crashes as soon as it
tries to delete it that way. but my other way works for some reason. 

i'm not sure what i'd be doing wrong though. i'd appreciate some help. thx :)
Is it crashing at delete or is it crashing the next time around when you call soundfx[i].playing on a null pointer. which is fixed by your remove function.
Jun 28 2004
next sibling parent reply Regan Heath <regan netwin.co.nz> writes:
On Mon, 28 Jun 2004 21:04:13 +0000 (UTC), Brian 
<Brian_member pathlink.com> wrote:
 In article <cbpv4s$2287$1 digitaldaemon.com>, clayasaurus says...
 hello. i'm trying to make it so in my game engine sound effects are 
 dynamic.

 That is, you add a sound effect to the manager, play it, and when it is 
 done
 playing, you remove it. every frame i run a process called 'process' 
 which
 checks to see if the sound effects are done playing, and if it does, it 
 deletes
 them.

 i've tried deleting the sound effects using the 'delete soundFX[index];'
 command, but this gives me a segmentation fault. instead i'm using my 
 own sloppy
 function 'removeSoundFXInArray(soundFX, num)'. this function seems to 
 work and
 doesn't cause a crash (well as long as not too many sounds are playing, 
 i think
 i can fix this though)


 here is the supplemental code...

 void process()
 {
 for (int i = 0; i < soundFX.length; i++) // delete the soundFX if they 
 are not
 playing
 {
 if (!soundFX[i].playing()) // if soundFX is not playing
 {
 //delete soundFX[i]; // theoretically, this should work, it doesn't
 soundFX = removeSoundFXInArray(soundFX, i); // this does, but it's 
 slower

 printf("soundfx done playing. deleted.\n");
 }
 }
 }


 SoundFX[] removeSoundFXInArray(inout SoundFX array[], int remove)
 {// removes a value in an array and returns the new value
 SoundFX newarray[];

 newarray = array[0 .. (remove)];
 newarray ~= array[(remove+1) .. array.length];

 delete array[remove]; // so it doesn't hang around in memory

 return newarray;
 }

 anyway, i'd figured i was doing something wrong with the delete 
 operator in
 'delete soundFX[i]' which was causing the seg fault. It crashes as soon 
 as it
 tries to delete it that way. but my other way works for some reason.

 i'm not sure what i'd be doing wrong though. i'd appreciate some help. 
 thx :)
Is it crashing at delete or is it crashing the next time around when you call soundfx[i].playing on a null pointer. which is fixed by your remove function.
Good call. I reckon if you modify process so that it checks for null entries in your array it will be fixed. Also, why not simply set entries to null instead of deleting, the GC should collect them (as long as you're setting the last reference to null) right? eg. foreach(SoundFX fx; soundFX) { if (fx === null) continue; if (!fx.playing()) // if soundFX is not playing fx = null; //you can do this instead of delete } However, something I just discovered that is disturbing... void main() { char[] test; test ~= "0"; test ~= "1"; test ~= "2"; test ~= "3"; test ~= "4"; test ~= "5"; test ~= "6"; test ~= "7"; test ~= "8"; test ~= "9"; foreach(char c; test) printf("(%02x) %c\n",c,c); printf("\n"); delete test[4]; foreach(char c; test) printf("(%02x) %c\n",c,c); } D:\D\src\build\temp>dmd arr.d d:\D\dmd\bin\..\..\dm\bin\link.exe arr,,,user32+kernel32/noi; D:\D\src\build\temp>arr (30) 0 (31) 1 (32) 2 (33) 3 (34) 4 (35) 5 (36) 6 (37) 7 (38) 8 (39) 9 (30) 0 (31) 1 (32) 2 (33) 3 (00) (00) (00) (00) (38) 8 (39) 9 It appears to have deleted 32bits (int?) worth of data from the array?!? Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jun 28 2004
parent clayasaurus <clayasaurus_member pathlink.com> writes:
In article <opsabrr2wv5a2sq9 digitalmars.com>, Regan Heath says...
Good call. I reckon if you modify process so that it checks for null 
entries in your array it will be fixed. Also, why not simply set entries 
to null instead of deleting, the GC should collect them (as long as you're 
setting the last reference to null) right?
eg.

foreach(SoundFX fx; soundFX)
{
	if (fx === null) continue;
	if (!fx.playing()) // if soundFX is not playing
	fx = null; //you can do this instead of delete
}	

However, something I just discovered that is disturbing...

void main() {
	char[] test;
	
	test ~= "0";
	test ~= "1";
	test ~= "2";
	test ~= "3";
	test ~= "4";
	test ~= "5";
	test ~= "6";
	test ~= "7";
	test ~= "8";
	test ~= "9";

	foreach(char c; test)
		printf("(%02x) %c\n",c,c);	
	
	printf("\n");
	delete test[4];
	
	foreach(char c; test)
		printf("(%02x) %c\n",c,c);	
}

D:\D\src\build\temp>dmd arr.d
d:\D\dmd\bin\..\..\dm\bin\link.exe arr,,,user32+kernel32/noi;

D:\D\src\build\temp>arr
(30) 0
(31) 1
(32) 2
(33) 3
(34) 4
(35) 5
(36) 6
(37) 7
(38) 8
(39) 9

(30) 0
(31) 1
(32) 2
(33) 3
(00)
(00)
(00)
(00)
(38) 8
(39) 9

It appears to have deleted 32bits (int?) worth of data from the array?!?

Regan.
well i took your suggestion and now my code looks like foreach(SoundFX fx; soundFX) { if (fx is null) continue; if (!fx.playing()) // if soundFX is not playing { fx = null; } } are you sure it is a good solution to rely on the garbage collector to clean up my array ? well anyway, i did a little stress test on it and i didn't see any major performance decrease or anything like that, and it doesn't crash (unlike my previous codes) btw. before i tried 'delete fx;' and it crashes then, even when i check to see if the array is null. oh well.
Jun 28 2004
prev sibling parent reply clayasaurus <clayasaurus_member pathlink.com> writes:
In article <cbq14d$253l$1 digitaldaemon.com>, Brian says...
Is it crashing at delete or is it crashing the next time around when you call
soundfx[i].playing on a null pointer. which is fixed by your remove function.
now that i think about it, it doesn't seem to crash right on the delete, since i can still printf after the delete. i guess it must be crashing next time it tries to access the .playing() function. but i thought if i delete an element in an array, the array will be resized and the element will be gone, so it shouldn't even try to access my playing() function ?
Jun 28 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Tue, 29 Jun 2004 02:09:03 +0000 (UTC), clayasaurus 
<clayasaurus_member pathlink.com> wrote:

 In article <cbq14d$253l$1 digitaldaemon.com>, Brian says...
 Is it crashing at delete or is it crashing the next time around when 
 you call
 soundfx[i].playing on a null pointer. which is fixed by your remove 
 function.
now that i think about it, it doesn't seem to crash right on the delete, since i can still printf after the delete. i guess it must be crashing next time it tries to access the .playing() function. but i thought if i delete an element in an array, the array will be resized and the element will be gone
it does not resize the array. (like your function did)
 , so it shouldn't even try to access my
 playing() function ?
I think I gave some bad advice, do you have? foreach(SoundFX fx; soundFX) if so, try foreach(inout SoundFX fx; soundFX) here is a test program I wrote, as you can see it works with delete :) import std.random; import std.date; class Foo { int tick; this(int _tick) { tick = _tick; } bool dec() { if (tick > 0) tick--; return tick == 0; } } void main() { Foo[] p; int cnt; rand_seed(getUTCtime(),0); p ~= new Foo(rand()&12); p ~= new Foo(rand()&12); p ~= new Foo(rand()&12); p ~= new Foo(rand()&12); p ~= new Foo(rand()&12); p ~= new Foo(rand()&12); p ~= new Foo(rand()&12); p ~= new Foo(rand()&12); p ~= new Foo(rand()&12); while(true) { cnt = 0; foreach(inout Foo f; p) { if (f is null) continue; cnt++; if (!f.dec()) continue; delete f; } foreach(Foo f2; p) { if (f2 is null) printf("%x()\n",f2); else printf("%x(%d)\n",f2,f2.tick); } printf("\n"); if (cnt == 0) break; } } Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jun 28 2004
next sibling parent reply clayasaurus <clayasaurus_member pathlink.com> writes:
one more thing i would like to add. see my post 'array operation request' to
understand why i thought you could do delete array[i]; and have it resize and
all. but the thing is i tried it with an int array and a matrix type array and
it worked then, but it doesn't work for classes ? 

maybe a 'remove' type thing would be a good array thing to have after all? or is
it better to leave it to the user.  
Jun 28 2004
parent Regan Heath <regan netwin.co.nz> writes:
On Tue, 29 Jun 2004 04:00:21 +0000 (UTC), clayasaurus 
<clayasaurus_member pathlink.com> wrote:

 one more thing i would like to add. see my post 'array operation 
 request' to
 understand why i thought you could do delete array[i]; and have it 
 resize and
 all. but the thing is i tried it with an int array and a matrix type 
 array and
 it worked then, but it doesn't work for classes ?
It doesn't work for me, with an int array... void main() { int[] a; a ~= 1; a ~= 2; a ~= 3; a ~= 4; a ~= 5; delete a[2]; foreach(int i; a) printf("%x\n",i); } prints 1 2 0 4 5 meaning it did not resize, all it did was destruct the int. (setting it to the .init value of 0)
 maybe a 'remove' type thing would be a good array thing to have after 
 all? or is
 it better to leave it to the user.
One of the aims is to have a small fast array type. As we can achieve what you want with a simple template.. ie. template remove(T) { T remove(inout T a, uint i) { a = a[0..i] ~ a[i+1..a.length]; return a; } } or even.. extern (C) void *memmove( void *dest, void *src, size_t count ); template removeAlt(T) { T removeAlt(inout T a, uint i) { memmove(&a[i],&a[i+1],typeof(a[0]).sizeof*(a.length-i)); a.length = a.length-1; return a; } } used like so.. void main() { int[] a; a ~= 1; a ~= 2; a ~= 3; a ~= 4; a ~= 5; removeAlt!(int[])(a,2); //delete a[2]; foreach(int i; a) printf("%x\n",i); } I don't think a remove property/method is required. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jun 28 2004
prev sibling parent reply clayasaurus <clayasaurus_member pathlink.com> writes:
another thing, i hate to flood my own topic, but i have a question about the 
foreach expression. is it possible to tell what number of the array you're on?
like with (for int i = 0; i < blah; i++) the current array item is 'i'. is there
a way to get the current item in a foreach statement since there is no 'i'?
thanks. 
Jun 28 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Tue, 29 Jun 2004 04:10:19 +0000 (UTC), clayasaurus 
<clayasaurus_member pathlink.com> wrote:

 another thing, i hate to flood my own topic, but i have a question about 
 the
 foreach expression. is it possible to tell what number of the array 
 you're on?
 like with (for int i = 0; i < blah; i++) the current array item is 'i'. 
 is there
 a way to get the current item in a foreach statement since there is no 
 'i'?
 thanks.
yep. a additional int or uint added to the foreach gives you that. example: Foo[] a; foreach(int i, Foo f; a) { } see: http://www.digitalmars.com/d/statement.html#foreach Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jun 28 2004
parent reply Arcane Jill <Arcane_member pathlink.com> writes:


This only works for ASSOCIATIVE arrays. For other types of array, it will call
the element's destructor.

Actually, this non-standard use of delete is dead confusing. I'd prefer a new
keyword (remove). In such a scheme, we could have:





and, even better...




could be equivalent to:








That would be neat.
Arcane Jill
Jun 29 2004
next sibling parent reply pragma <EricAnderton at yahoo dot com> <pragma_member pathlink.com> writes:
In article <cbr6l0$rj5$1 digitaldaemon.com>, Arcane Jill says...


This only works for ASSOCIATIVE arrays. For other types of array, it will call
the element's destructor.

Actually, this non-standard use of delete is dead confusing. I'd prefer a new
keyword (remove). In such a scheme, we could have:





and, even better...




could be equivalent to:








That would be neat.
Arcane Jill
While you're at it, wouldn't the following... ..work well for removing a slice from an array? I've yearned for this kind of flexibility from arrays in a language for some time now. I'd even settle for some standard array methods like '.remove(x)' '.clear()' or '.isEmpty()'. - Pragma
Jun 29 2004
next sibling parent reply "Bent Rasmussen" <exo bent-rasmussen.info> writes:
 While you're at it, wouldn't the following...



 ..work well for removing a slice from an array?
That would be cool.
 I've yearned for this kind of flexibility from arrays in a language for
some
 time now.  I'd even settle for some standard array methods like
'.remove(x)'
 '.clear()' or '.isEmpty()'.
I don't think they need to be pre-defined.
Jun 29 2004
parent reply pragma <EricAnderton at yahoo dot com> <pragma_member pathlink.com> writes:
In article <cbrrtf$1otv$1 digitaldaemon.com>, Bent Rasmussen says...
 While you're at it, wouldn't the following...



 ..work well for removing a slice from an array?
That would be cool.
 I've yearned for this kind of flexibility from arrays in a language for
some
 time now.  I'd even settle for some standard array methods like
'.remove(x)'
 '.clear()' or '.isEmpty()'.
I don't think they need to be pre-defined.
I'm genuinely curious about your take on this. What do you have in mind instead of this? Should there should be an Array class with static array mutators, or a free-function library to supplement D arrays? Something else? :) (I'm just thinking aloud here, but maybe what we're missing here is something that can be added to Demios to extend arrays beyond their current use and design... provided its not already there yet.) - Pragma
Jun 29 2004
parent "Bent Rasmussen" <exo bent-rasmussen.info> writes:
 I'm genuinely curious about your take on this.  What do you have in mind
instead
 of this?  Should there should be an Array class with static array
mutators, or a
 free-function library to supplement D arrays? Something else? :)
A free-function module should do it. Of course there'll probably be classes for sophisticated structures in DTL. I don't at all like the Java way of using classes with static methods for array and collection operations. For collections they ought to be part of the datatype they operate upon. For arrays its probably an artifact of the language: look at Math. Now it seems Java will get static import so you can import Math functions in the Tiger release. http://www.jcp.org/aboutJava/communityprocess/jsr/tiger/static-import.html Ahh, the benefits of D... I haven't looked into slicing in D yet, but I suspect sorting with them will be a joy.
Jun 29 2004
prev sibling parent Regan Heath <regan netwin.co.nz> writes:
On Tue, 29 Jun 2004 13:15:33 +0000 (UTC), pragma <EricAnderton at yahoo 
dot com pragma_member pathlink.com> wrote:
 In article <cbr6l0$rj5$1 digitaldaemon.com>, Arcane Jill says...


 This only works for ASSOCIATIVE arrays. For other types of array, it 
 will call
 the element's destructor.

 Actually, this non-standard use of delete is dead confusing. I'd prefer 
 a new
 keyword (remove). In such a scheme, we could have:



 the array a


 and, even better...




 could be equivalent to:








 That would be neat.
 Arcane Jill
While you're at it, wouldn't the following... ..work well for removing a slice from an array?
Hey cool.. I wonder what type m..n turns into exactly, if it was accessable to us then we could modify this: extern (C) void *memmove( void *dest, void *src, size_t count ); template remove(T) { T remove(inout T a, uint i) { memmove(&a[i],&a[i+1],typeof(a[0]).sizeof*(a.length-i)); a.length = a.length-1; return a; } } to remove the complete slice/range from the array.
 I've yearned for this kind of flexibility from arrays in a language for 
 some
 time now.  I'd even settle for some standard array methods like 
 '.remove(x)'
 '.clear()' or '.isEmpty()'.

 - Pragma
-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jun 29 2004
prev sibling next sibling parent Regan Heath <regan netwin.co.nz> writes:
On Tue, 29 Jun 2004 07:44:32 +0000 (UTC), Arcane Jill 
<Arcane_member pathlink.com> wrote:


 This only works for ASSOCIATIVE arrays. For other types of array, it 
 will call
 the element's destructor.

 Actually, this non-standard use of delete is dead confusing. I'd prefer 
 a new
 keyword (remove). In such a scheme, we could have:



 array a


 and, even better...




 could be equivalent to:







Yep. extern (C) void *memmove( void *dest, void *src, size_t count ); template remove(T) { T remove(inout T a, uint i) { memmove(&a[i],&a[i+1],typeof(a[0]).sizeof*(a.length-i)); a.length = a.length-1; return a; } } Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jun 29 2004
prev sibling parent "Carlos Santander B." <carlos8294 msn.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> escribió en el mensaje
news:cbr6l0$rj5$1 digitaldaemon.com

|
| This only works for ASSOCIATIVE arrays. For other types of array, it will
call
| the element's destructor.
|
| Actually, this non-standard use of delete is dead confusing. I'd prefer a
new
| keyword (remove). In such a scheme, we could have:
|


array a

|

Even it's been said to death, I agree (95%).

| and, even better...
|


|
| could be equivalent to:
|






|
| That would be neat.

I tend to agree (60%).

| Arcane Jill

-----------------------
Carlos Santander Bernal
Jun 29 2004
prev sibling next sibling parent reply Andy Friesen <andy ikagames.com> writes:
clayasaurus wrote:
 anyway, i'd figured i was doing something wrong with the delete operator in
 'delete soundFX[i]' which was causing the seg fault. It crashes as soon as it
 tries to delete it that way. but my other way works for some reason. 
 
 i'm not sure what i'd be doing wrong though. i'd appreciate some help. thx :)
It looks to me like delete array[x] does not remove an element from an array, but instead calls the destructor of that element. It's probably best just to do: void deleteSFX(inout SoundFX[] array, uint remove) { for (uint i = remove; i < array.length - 1; i++) { array[i] = array[i + 1]; } array.length = array.length - 1; } -- andy
Jun 28 2004
parent clayasaurus <clayasaurus_member pathlink.com> writes:
In article <cbqjab$2u85$1 digitaldaemon.com>, Andy Friesen says...
It's probably best just to do:

     void deleteSFX(inout SoundFX[] array, uint remove) {
         for (uint i = remove; i < array.length - 1; i++) {
             array[i] = array[i + 1];
         }
         array.length = array.length - 1;
     }

  -- andy
thanks andy, i think i like your solution because my arrays length doesn't climb to drastic heights. in fact when i do delete soundFX[i]; deleteSFX(soundFX,i); not only does it call the constructer, but it resizes the array and doesn't crash! woohoo.
Jun 28 2004
prev sibling parent reply Ben Hinkle <bhinkle4 juno.com> writes:
The statement
 delete x[key];
removes the element with the given key if x is an associative array. If x is
a dynamic array then
 delete x[i];
will call the destructor of the ith element of x. If you will be inserting
and removing elements from arbitrary positions in the array I'd recommend
going with an associative array since removing elements is faster.

clayasaurus wrote:

 hello. i'm trying to make it so in my game engine sound effects are
 dynamic.
 
 That is, you add a sound effect to the manager, play it, and when it is
 done playing, you remove it. every frame i run a process called 'process'
 which checks to see if the sound effects are done playing, and if it does,
 it deletes them.
 
 i've tried deleting the sound effects using the 'delete soundFX[index];'
 command, but this gives me a segmentation fault. instead i'm using my own
 sloppy function 'removeSoundFXInArray(soundFX, num)'. this function seems
 to work and doesn't cause a crash (well as long as not too many sounds are
 playing, i think i can fix this though)
 
 
 here is the supplemental code...
 
 void process()
 {
 for (int i = 0; i < soundFX.length; i++) // delete the soundFX if they are
 not playing
 {
 if (!soundFX[i].playing()) // if soundFX is not playing
 {
 //delete soundFX[i]; // theoretically, this should work, it doesn't
 soundFX = removeSoundFXInArray(soundFX, i); // this does, but it's slower
 
 printf("soundfx done playing. deleted.\n");
 }
 }
 }
 
 
 SoundFX[] removeSoundFXInArray(inout SoundFX array[], int remove)
 {// removes a value in an array and returns the new value
 SoundFX newarray[];
 
 newarray = array[0 .. (remove)];
 newarray ~= array[(remove+1) .. array.length];
 
 delete array[remove]; // so it doesn't hang around in memory
 
 return newarray;
 }
 
 anyway, i'd figured i was doing something wrong with the delete operator
 in 'delete soundFX[i]' which was causing the seg fault. It crashes as soon
 as it tries to delete it that way. but my other way works for some reason.
 
 i'm not sure what i'd be doing wrong though. i'd appreciate some help. thx
 :)
Jun 28 2004
parent clayasaurus <clayasaurus_member pathlink.com> writes:
In article <cbqjoi$2uu9$1 digitaldaemon.com>, Ben Hinkle says...
The statement
 delete x[key];
removes the element with the given key if x is an associative array. If x is
a dynamic array then
 delete x[i];
will call the destructor of the ith element of x. If you will be inserting
and removing elements from arbitrary positions in the array I'd recommend
going with an associative array since removing elements is faster.
ok. i'll keep that in mind for the future. i've never worked with associative arrays before and i'll probably need to play around with them a bit to figure them out.
Jun 28 2004