Updating GUIs

Hi,

I'm still fairly new to writing GUIs. A common approach to me is to
write some model classes that have a function called "update". That
function should get called a few times a second, and then the GUI
should get updated reflecting those changes.

What's the best way to do this? Should I have a Ruby thread that
calls the update function, sleeps for 0.2 seconds, and then loops?
How should the GUI get notified of those changes?

For discussion purposes, say I have

class Player
  attr_accessor :x_position, :y_position, :player_type
  # just a data store
end

class PlayerList
  attr_accessor :players
  def initialize
    @players = [Player.new, Player.new, Player.new]
  end
  def update
    # update goes out and updates the x and y position
  end
end

And then the GUI knows about PlayerList and needs to display updated
information on each Player (move a player icon on a map, update
tabular data, that kind of thing).

I could have up to 500 players, and the application isn't supposed to
be terribly CPU intensive, so performance can be an issue.

Hello Joe,

Hi,

I'm still fairly new to writing GUIs. A common approach to me is to
write some model classes that have a function called "update". That
function should get called a few times a second, and then the GUI
should get updated reflecting those changes.

What's the best way to do this? Should I have a Ruby thread that
calls the update function, sleeps for 0.2 seconds, and then loops?
How should the GUI get notified of those changes?

Only update/invalidate when you must update and then update/invalidate the
smallest possible rectangle. Thats the normal rule for writing GUI's

···

--
Best regards, emailto: scholz at scriptolutions dot com
Lothar Scholz http://www.ruby-ide.com
CTO Scriptolutions Ruby, PHP, Python IDE 's

Joe,

I am looking at this very problem. I don't have any good answers yet, but to avoid simply saying "me too" to your thread... :slight_smile:

Have you looked at module Observable? For a large number of updatables, that might reduce your overhead. The Player class would have to call notify_observers every time it changed, but the end result is that any GUI update threads would only need to update things that are specific to the GUI (like a clock or animations). Player updates would be sent directly to the GUI from the players when they update.

Ironically, notify_observers calls "update" in the observer. In this case, the observer would probably want to be the code that controls that one object in the GUI, not the entire GUI program.

-dB

···

--
David Brady
ruby-talk@shinybit.com
I'm having a really surreal day... OR AM I?

Joe Van Dyk wrote:

What's the best way to do this? Should I have a Ruby thread that
calls the update function, sleeps for 0.2 seconds, and then loops? How should the GUI get notified of those changes?

I designed the OpenUI cross-platform GUI tool. We maintained a
draw-list to which any changed GUI object was added. The actual
draw was delayed. The delay was implemented by processing all
queued and incoming events (we controlled the main event loop),
then waiting for a (customisable) delay, usually 0.1-0.2 seconds,
before processing the drawlist. 0.1 seconds is about right, small
enough that a human won't notice the delay but long enough to
ensure that everything that reasonably can happen before redraw
has happened.

Redraw does need to be forced if you want to achieve the impression
of rapid progress (flickering through lists of files being scanned,
etc).

This is an excellent way to minimise redraw activity. It gives
automatic type-ahead - a user can even learn to enter keyboard
data into a popup window and Ok it *without the popup even
appearing*... On a slower computer of course - OpenUI was initially
developed to run on a Windows 3.1 system with only 4M of RAM :-).
The NASDAQ exchange ran on 486's with Windows 3.1 and only 8Mb
of RAM for many years - this was an OpenUI app that was receiving
and displaying sustained UDP broadcast bids and offers at an
average of several per second, maximum rate about ten per second,
with a very low lost-packet rate (there was no recovery from lost
packets). Pretty impressive, even by today's standards.

Clifford Heath.

Hi,

I think I tried that approach a while ago. It worked pretty well
except with large amounts of Players. If Players get updated 2 times
a second and there's 500 players, that would be at least 1000 update
calls per second.

···

On 7/12/05, David Brady <ruby_talk@shinybit.com> wrote:

Joe,

I am looking at this very problem. I don't have any good answers yet,
but to avoid simply saying "me too" to your thread... :slight_smile:

Have you looked at module Observable? For a large number of updatables,
that might reduce your overhead. The Player class would have to call
notify_observers every time it changed, but the end result is that any
GUI update threads would only need to update things that are specific to
the GUI (like a clock or animations). Player updates would be sent
directly to the GUI from the players when they update.

Ironically, notify_observers calls "update" in the observer. In this
case, the observer would probably want to be the code that controls that
one object in the GUI, not the entire GUI program.

-dB

--
David Brady
ruby-talk@shinybit.com
I'm having a really surreal day... OR AM I?

Joe Van Dyk wrote:

Hi,

I think I tried that approach a while ago. It worked pretty well
except with large amounts of Players. If Players get updated 2 times
a second and there's 500 players, that would be at least 1000 update
calls per second.

Ah. You're not trying to figure out how to setup an update structure. You're trying to figure out how to pay for an 1KHz update rate. :slight_smile:

Eliminating function calls may save you some time, but the biggest thing you can do to save time is to reduce needless duplication of drawing routines. Don't take my word for it; check the profiler. My bet is that the graphics routines take the most time.

If you have 500 discrete widgets on the GUI, one for each player, try to make sure they each only update themselves when updated. Don't redraw the entire UI on each cycle. This works if, for example, Player is a thing that plays songs and has its own little display.

If each Player is drawn to the same area, for example if you have 500 sprites each running around the field doing their own thing, you'll want to poll all the players then update the GUI area all at once. A classic game programming technique is to divide each frame into separate components: get input, process input/physics/collisions, move objects, render. render() is done to an off-screen buffer, then blitted to the display. The end result is that the poky 2D GUI drawing routines are limited to the blit at the end of the frame.

-dB

···

--
David Brady
ruby-talk@shinybit.com
I'm having a really surreal day... OR AM I?

Thanks for the help.

I'm doing this on a TkCanvas. Each Player has a different polygon
(each polygon has about 30 points on it or so). The polygons have to
move around on the canvas and rotate depending on which direction they
are going. So, I'm calling a rotate function for every point on every
polygon. I think that's a source of the slowness. I could post the
code for that section if needed. Are there any techniques to make
that part faster? I was thinking that converting the section of the
code that does the math on every point to C would help a lot, but
perhaps I'm doing it completely the wrong way.

# rotate function
def rotate(deg, x, y, c_x = 0, c_y = 0)
rad = (deg * Math::PI)/180.0
s_rad = Math::sin(rad)
c_rad = Math::cos(rad)
x -= c_x
y -= c_y
[c_x + (x * c_rad - y * s_rad), c_y + (x * s_rad + y * c_rad)]
end

# Gets used like this.
# coords returns an array of polygon points.
# center is the center of the polygon (an array like [x,y])
poly.coords(coords.collect{|x, y| rotate(deg, x, y, *center)})

Should I try to convert the rotate function to C and then see if it's
fast enough? I'm still making a gazillion function calls (say, 300
polygons and 30 points each and update twice a second, that's 18,000
function calls a second). No wonder it's slow. :frowning: Perhaps using
TkCanvas was a mistake for this.

···

On 7/12/05, David Brady <ruby_talk@shinybit.com> wrote:

Joe Van Dyk wrote:

>Hi,
>
>I think I tried that approach a while ago. It worked pretty well
>except with large amounts of Players. If Players get updated 2 times
>a second and there's 500 players, that would be at least 1000 update
>calls per second.
>
>

Ah. You're not trying to figure out how to setup an update structure.
You're trying to figure out how to pay for an 1KHz update rate. :slight_smile:

Eliminating function calls may save you some time, but the biggest thing
you can do to save time is to reduce needless duplication of drawing
routines. Don't take my word for it; check the profiler. My bet is
that the graphics routines take the most time.

If you have 500 discrete widgets on the GUI, one for each player, try to
make sure they each only update themselves when updated. Don't redraw
the entire UI on each cycle. This works if, for example, Player is a
thing that plays songs and has its own little display.

If each Player is drawn to the same area, for example if you have 500
sprites each running around the field doing their own thing, you'll want
to poll all the players then update the GUI area all at once. A classic
game programming technique is to divide each frame into separate
components: get input, process input/physics/collisions, move objects,
render. render() is done to an off-screen buffer, then blitted to the
display. The end result is that the poky 2D GUI drawing routines are
limited to the blit at the end of the frame.

Also, is there a way to "freeze" the canvas while I'm making these
updates? I thought there was one for gnomecanvas.

···

On 7/12/05, Joe Van Dyk <joevandyk@gmail.com> wrote:

On 7/12/05, David Brady <ruby_talk@shinybit.com> wrote:
> Joe Van Dyk wrote:
>
> >Hi,
> >
> >I think I tried that approach a while ago. It worked pretty well
> >except with large amounts of Players. If Players get updated 2 times
> >a second and there's 500 players, that would be at least 1000 update
> >calls per second.
> >
> >
>
> Ah. You're not trying to figure out how to setup an update structure.
> You're trying to figure out how to pay for an 1KHz update rate. :slight_smile:
>
> Eliminating function calls may save you some time, but the biggest thing
> you can do to save time is to reduce needless duplication of drawing
> routines. Don't take my word for it; check the profiler. My bet is
> that the graphics routines take the most time.
>
> If you have 500 discrete widgets on the GUI, one for each player, try to
> make sure they each only update themselves when updated. Don't redraw
> the entire UI on each cycle. This works if, for example, Player is a
> thing that plays songs and has its own little display.
>
> If each Player is drawn to the same area, for example if you have 500
> sprites each running around the field doing their own thing, you'll want
> to poll all the players then update the GUI area all at once. A classic
> game programming technique is to divide each frame into separate
> components: get input, process input/physics/collisions, move objects,
> render. render() is done to an off-screen buffer, then blitted to the
> display. The end result is that the poky 2D GUI drawing routines are
> limited to the blit at the end of the frame.

Thanks for the help.

I'm doing this on a TkCanvas. Each Player has a different polygon
(each polygon has about 30 points on it or so). The polygons have to
move around on the canvas and rotate depending on which direction they
are going. So, I'm calling a rotate function for every point on every
polygon. I think that's a source of the slowness. I could post the
code for that section if needed. Are there any techniques to make
that part faster? I was thinking that converting the section of the
code that does the math on every point to C would help a lot, but
perhaps I'm doing it completely the wrong way.

# rotate function
def rotate(deg, x, y, c_x = 0, c_y = 0)
rad = (deg * Math::PI)/180.0
s_rad = Math::sin(rad)
c_rad = Math::cos(rad)
x -= c_x
y -= c_y
[c_x + (x * c_rad - y * s_rad), c_y + (x * s_rad + y * c_rad)]
end

# Gets used like this.
# coords returns an array of polygon points.
# center is the center of the polygon (an array like [x,y])
poly.coords(coords.collect{|x, y| rotate(deg, x, y, *center)})

Should I try to convert the rotate function to C and then see if it's
fast enough? I'm still making a gazillion function calls (say, 300
polygons and 30 points each and update twice a second, that's 18,000
function calls a second). No wonder it's slow. :frowning: Perhaps using
TkCanvas was a mistake for this.

Hello Joe,

I'm doing this on a TkCanvas.

If you use TkCanvas it is very unlikely that the speed problem is
caused by the update call. This part is very optimized in TK.

Should I try to convert the rotate function to C and then see if it's
fast enough? I'm still making a gazillion function calls (say, 300
polygons and 30 points each and update twice a second, that's 18,000
function calls a second). No wonder it's slow. :frowning: Perhaps using
TkCanvas was a mistake for this.

Have you isolated your rotation code and calculated the values without
passing the parameters to TkCanvas. How fast is this ?

The only TK related problem is to pass 18000 floating point numbers to
the canvas widget. Don't know how heavyweight the RubyTK layer is but
it means converting them to strings and back to floating points. So
maybe this parameter setting is the reason for the problem, but you
must measure it not trying to guess it.

I've seen more complicated TkCanvas scenarios 7 years ago when
CPU speed was 400 MHz and TK did not show a problem.

···

--
Best regards, emailto: scholz at scriptolutions dot com
Lothar Scholz http://www.ruby-ide.com
CTO Scriptolutions Ruby, PHP, Python IDE 's

Message-ID: <c715e64050712141838df4054@mail.gmail.com>

I'm doing this on a TkCanvas. Each Player has a different polygon
(each polygon has about 30 points on it or so). The polygons have to
move around on the canvas and rotate depending on which direction they
are going. So, I'm calling a rotate function for every point on every

     (snip)

poly.coords(coords.collect{|x, y| rotate(deg, x, y, *center)})

How many steps do you need for rotation?
If that is integer order, you should create and hide all polygons
for all rotate steps at the beginning, and show (move) the one for
current degree on each update phase.

If you can stop updating widgets (e.g. by your own eventloop
with TclTkLib.do_one_event), please stop updating while moving
500 polygons and do update_idletasks after moving all.
Probably faster than the normal eventloop.

# I found a bug on TclTkLib.do_one_event and fixed it on CVS today.
# If you try to use TclTkLib.do_one_event, get the latest tcltklib.c
# from CVS.

And when an application calls Tcl/Tk interpreter very frequently,
the cost for conversion of arguments (Ruby object -> Tcl string)
is sometimes too large. In such case, TclTkIp._eval may make the
application faster.

# However, as you say, TkCanvas may not be proper. :wink:

···

From: Joe Van Dyk <joevandyk@gmail.com>
Subject: Re: Updating GUIs
Date: Wed, 13 Jul 2005 06:18:15 +0900
--
Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)

This is taking me about a third of a second to do. Seems a bit high
if I want to do this at least twice a second and leave room on the
machine to do other things:

# rotate function
def rotate(deg, x, y, c_x = 0, c_y = 0)
rad = (deg * Math::PI)/180.0
s_rad = Math::sin(rad)
c_rad = Math::cos(rad)
x -= c_x
y -= c_y
[c_x + (x * c_rad - y * s_rad), c_y + (x * s_rad + y * c_rad)]
end

class Player
  attr_accessor :x, :y, :polygon, :heading
  def initalize
    @x = @y = @heading = 0
    @polygon =
  end
end

players =
500.times do
  p = Player.new
  p.x = rand 1000000
  p.y = rand 1000000
  p.heading = rand 360
  p.polygon = # Why do I need this here? If it's not, p.polygon is nil. :frowning:
  40.times do
    p.polygon << [rand(10), rand(10)]
  end
  players << p
end

start_time = Time.now
players.each do |player|
  player.polygon = player.polygon.collect do |x,y|
    rotate(player.heading, x, y, player.x, player.y)
  end
end
end_time = Time.now
puts end_time - start_time

So what's the next step when trying to optimize this? Convert the
rotate function to C? Or can I use a different algorithm?

···

On 7/12/05, Lothar Scholz <mailinglists@scriptolutions.com> wrote:

Hello Joe,

> I'm doing this on a TkCanvas.

If you use TkCanvas it is very unlikely that the speed problem is
caused by the update call. This part is very optimized in TK.

> Should I try to convert the rotate function to C and then see if it's
> fast enough? I'm still making a gazillion function calls (say, 300
> polygons and 30 points each and update twice a second, that's 18,000
> function calls a second). No wonder it's slow. :frowning: Perhaps using
> TkCanvas was a mistake for this.

Have you isolated your rotation code and calculated the values without
passing the parameters to TkCanvas. How fast is this ?

The only TK related problem is to pass 18000 floating point numbers to
the canvas widget. Don't know how heavyweight the RubyTK layer is but
it means converting them to strings and back to floating points. So
maybe this parameter setting is the reason for the problem, but you
must measure it not trying to guess it.

I've seen more complicated TkCanvas scenarios 7 years ago when
CPU speed was 400 MHz and TK did not show a problem.

From: Joe Van Dyk <joevandyk@gmail.com>
Subject: Re: Updating GUIs
Date: Wed, 13 Jul 2005 06:18:15 +0900
Message-ID: <c715e64050712141838df4054@mail.gmail.com>
> I'm doing this on a TkCanvas. Each Player has a different polygon
> (each polygon has about 30 points on it or so). The polygons have to
> move around on the canvas and rotate depending on which direction they
> are going. So, I'm calling a rotate function for every point on every
     (snip)
> poly.coords(coords.collect{|x, y| rotate(deg, x, y, *center)})

How many steps do you need for rotation?
If that is integer order, you should create and hide all polygons
for all rotate steps at the beginning, and show (move) the one for
current degree on each update phase.

I'm not sure what you mean by "integer order". In fact, that whole
paragraph confused me. :slight_smile:

If you can stop updating widgets (e.g. by your own eventloop
with TclTkLib.do_one_event), please stop updating while moving
500 polygons and do update_idletasks after moving all.
Probably faster than the normal eventloop.

# I found a bug on TclTkLib.do_one_event and fixed it on CVS today.
# If you try to use TclTkLib.do_one_event, get the latest tcltklib.c
# from CVS.

And when an application calls Tcl/Tk interpreter very frequently,
the cost for conversion of arguments (Ruby object -> Tcl string)
is sometimes too large. In such case, TclTkIp._eval may make the
application faster.

How can I use TclTklp._eval?

# However, as you say, TkCanvas may not be proper. :wink:

Thanks for your help!

···

On 7/12/05, Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp> wrote:

On a very fast machine, the above code ran in 0.14 seconds. On a
pentium1 machine, it ran in 2.04 seconds.

When I used the following C function to do the rotation, it pretty
much halved the execution time to 0.06 seconds on the fast machine and
0.94 seconds on the Pentium1 machine.

I'm pretty new to C extensions, is there anything else I can do to
improve the speed? It would seem that moving the code that loops
around the Players and collects the polygon points to C would help
much more, but I don't know how to do that yet in C.

static VALUE rotate(VALUE self, /* Module */
                    VALUE _degree, /* Rotate Degree */
                    VALUE _x, VALUE _y, /* Point */
                    VALUE _c_x, VALUE _c_y) /* Center to rotate around */
{
  double degree = NUM2DBL(_degree);
  long x = NUM2LONG(_x);
  long y = NUM2LONG(_y);
  long c_x = NUM2LONG(_c_x);
  long c_y = NUM2LONG(_c_y);

  double rad = (degree * M_PI) / 180.0;
  double s_rad = sin(rad);
  double c_rad = cos(rad);
  x -= c_x;
  y -= c_y;

  VALUE return_array = rb_ary_new();
  rb_ary_push(return_array, rb_float_new(c_x + (x * c_rad - y * s_rad)));
  rb_ary_push(return_array, rb_float_new(c_y + (x * s_rad + y * c_rad)));
  return return_array;
}

···

On 7/12/05, Joe Van Dyk <joevandyk@gmail.com> wrote:

On 7/12/05, Lothar Scholz <mailinglists@scriptolutions.com> wrote:
> Hello Joe,
>
> > I'm doing this on a TkCanvas.
>
> If you use TkCanvas it is very unlikely that the speed problem is
> caused by the update call. This part is very optimized in TK.
>
> > Should I try to convert the rotate function to C and then see if it's
> > fast enough? I'm still making a gazillion function calls (say, 300
> > polygons and 30 points each and update twice a second, that's 18,000
> > function calls a second). No wonder it's slow. :frowning: Perhaps using
> > TkCanvas was a mistake for this.
>
> Have you isolated your rotation code and calculated the values without
> passing the parameters to TkCanvas. How fast is this ?
>
> The only TK related problem is to pass 18000 floating point numbers to
> the canvas widget. Don't know how heavyweight the RubyTK layer is but
> it means converting them to strings and back to floating points. So
> maybe this parameter setting is the reason for the problem, but you
> must measure it not trying to guess it.
>
> I've seen more complicated TkCanvas scenarios 7 years ago when
> CPU speed was 400 MHz and TK did not show a problem.

This is taking me about a third of a second to do. Seems a bit high
if I want to do this at least twice a second and leave room on the
machine to do other things:

# rotate function
def rotate(deg, x, y, c_x = 0, c_y = 0)
rad = (deg * Math::PI)/180.0
s_rad = Math::sin(rad)
c_rad = Math::cos(rad)
x -= c_x
y -= c_y
[c_x + (x * c_rad - y * s_rad), c_y + (x * s_rad + y * c_rad)]
end

class Player
  attr_accessor :x, :y, :polygon, :heading
  def initalize
    @x = @y = @heading = 0
    @polygon =
  end
end

players =
500.times do
  p = Player.new
  p.x = rand 1000000
  p.y = rand 1000000
  p.heading = rand 360
  p.polygon = # Why do I need this here? If it's not, p.polygon is nil. :frowning:
  40.times do
    p.polygon << [rand(10), rand(10)]
  end
  players << p
end

start_time = Time.now
players.each do |player|
  player.polygon = player.polygon.collect do |x,y|
    rotate(player.heading, x, y, player.x, player.y)
  end
end
end_time = Time.now
puts end_time - start_time

So what's the next step when trying to optimize this? Convert the
rotate function to C? Or can I use a different algorithm?

Joe Van Dyk <joevandyk@gmail.com> writes:

This is taking me about a third of a second to do. Seems a bit high
if I want to do this at least twice a second and leave room on the
machine to do other things:

[...]

  player.polygon = player.polygon.collect do |x,y|

How about this?

   player.polygon.collect! do |x,y|

So what's the next step when trying to optimize this? Convert the
rotate function to C? Or can I use a different algorithm?

I didn't look at your algorithm, but before starting to convert stuff
into C, I'd try eliminating the extra object creation. I don't know
how much time it'll buy you, but maybe even try making `rotate'
destructively change the array instead of returning a new one.

Actually, I haven't done any optimizing of Ruby code, so I'm mostly
talking out of my ass. But I think it's worth a try.

···

--
Daniel Brockman <daniel@brockman.se>

    So really, we all have to ask ourselves:
    Am I waiting for RMS to do this? --TTN.

Message-ID: <c715e6405071300231324eb65@mail.gmail.com>

> How many steps do you need for rotation?
> If that is integer order, you should create and hide all polygons
> for all rotate steps at the beginning, and show (move) the one for
> current degree on each update phase.

I'm not sure what you mean by "integer order". In fact, that whole
paragraph confused me. :slight_smile:

I'm very sorry.
If you need 5 degree step of rotation, you need only 72 polygons
for each player. So, instead of calculating, create 72 polygons at
first. And select one of them to show the player, and hide other
71 polygons. If the direction of the player is changed, select
another one from hidden 71 polygons and hide current polygon.

How can I use TclTklp._eval?

TclTkIp._eval("... Tcl/Tk script ...")

···

From: Joe Van Dyk <joevandyk@gmail.com>
Subject: Re: Updating GUIs
Date: Wed, 13 Jul 2005 16:23:58 +0900
--
Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)

Why do I need this here? If it's not, p.polygon is nil

Joe,

While I can't help with much else, I did, while playing with your
sample, figure this one out - you've misspelt "initialize"... (the
third "i" is missing).

Mike

I somehow figured out how to do the following in C:

player.polygon = player.polygon.collect do |x,y|
  rotate(player.heading, x, y, player.x, player.y)
end

So, now that Ruby code looks like
player.polygon = Rotate::rotate_points(player.polygon,
                                         player.heading,
                                         player.x, player.y)

And the C function for Rotate::rotate_points looks like:

static VALUE rotate_points(VALUE self,
                           VALUE points, VALUE heading, VALUE x, VALUE y)
{
  int i;
  VALUE return_array = rb_ary_new();
  for (i = 0; i < RARRAY(points)->len; i++)
  {
    rb_ary_push(
        return_array,
        rotate(self, heading,
               RARRAY(RARRAY(points)->ptr[i])->ptr[0],
               RARRAY(RARRAY(points)->ptr[i])->ptr[1],
               x, y));
  }
  return return_array;
}

Time is now down from 2 seconds in the original Ruby version to 0.66
seconds in the new C version (on a very slow computer). I'm still
iterating over each Player in Ruby though. Any other ideas on how I
can improve the speed?

···

On 7/12/05, Joe Van Dyk <joevandyk@gmail.com> wrote:

On 7/12/05, Joe Van Dyk <joevandyk@gmail.com> wrote:
> On 7/12/05, Lothar Scholz <mailinglists@scriptolutions.com> wrote:
> > Hello Joe,
> >
> > > I'm doing this on a TkCanvas.
> >
> > If you use TkCanvas it is very unlikely that the speed problem is
> > caused by the update call. This part is very optimized in TK.
> >
> > > Should I try to convert the rotate function to C and then see if it's
> > > fast enough? I'm still making a gazillion function calls (say, 300
> > > polygons and 30 points each and update twice a second, that's 18,000
> > > function calls a second). No wonder it's slow. :frowning: Perhaps using
> > > TkCanvas was a mistake for this.
> >
> > Have you isolated your rotation code and calculated the values without
> > passing the parameters to TkCanvas. How fast is this ?
> >
> > The only TK related problem is to pass 18000 floating point numbers to
> > the canvas widget. Don't know how heavyweight the RubyTK layer is but
> > it means converting them to strings and back to floating points. So
> > maybe this parameter setting is the reason for the problem, but you
> > must measure it not trying to guess it.
> >
> > I've seen more complicated TkCanvas scenarios 7 years ago when
> > CPU speed was 400 MHz and TK did not show a problem.
>
> This is taking me about a third of a second to do. Seems a bit high
> if I want to do this at least twice a second and leave room on the
> machine to do other things:
>
>
> # rotate function
> def rotate(deg, x, y, c_x = 0, c_y = 0)
> rad = (deg * Math::PI)/180.0
> s_rad = Math::sin(rad)
> c_rad = Math::cos(rad)
> x -= c_x
> y -= c_y
> [c_x + (x * c_rad - y * s_rad), c_y + (x * s_rad + y * c_rad)]
> end
>
> class Player
> attr_accessor :x, :y, :polygon, :heading
> def initalize
> @x = @y = @heading = 0
> @polygon =
> end
> end
>
> players =
> 500.times do
> p = Player.new
> p.x = rand 1000000
> p.y = rand 1000000
> p.heading = rand 360
> p.polygon = # Why do I need this here? If it's not, p.polygon is nil. :frowning:
> 40.times do
> p.polygon << [rand(10), rand(10)]
> end
> players << p
> end
>
> start_time = Time.now
> players.each do |player|
> player.polygon = player.polygon.collect do |x,y|
> rotate(player.heading, x, y, player.x, player.y)
> end
> end
> end_time = Time.now
> puts end_time - start_time
>
>
> So what's the next step when trying to optimize this? Convert the
> rotate function to C? Or can I use a different algorithm?

On a very fast machine, the above code ran in 0.14 seconds. On a
pentium1 machine, it ran in 2.04 seconds.

When I used the following C function to do the rotation, it pretty
much halved the execution time to 0.06 seconds on the fast machine and
0.94 seconds on the Pentium1 machine.

I'm pretty new to C extensions, is there anything else I can do to
improve the speed? It would seem that moving the code that loops
around the Players and collects the polygon points to C would help
much more, but I don't know how to do that yet in C.

static VALUE rotate(VALUE self, /* Module */
                    VALUE _degree, /* Rotate Degree */
                    VALUE _x, VALUE _y, /* Point */
                    VALUE _c_x, VALUE _c_y) /* Center to rotate around */
{
  double degree = NUM2DBL(_degree);
  long x = NUM2LONG(_x);
  long y = NUM2LONG(_y);
  long c_x = NUM2LONG(_c_x);
  long c_y = NUM2LONG(_c_y);

  double rad = (degree * M_PI) / 180.0;
  double s_rad = sin(rad);
  double c_rad = cos(rad);
  x -= c_x;
  y -= c_y;

  VALUE return_array = rb_ary_new();
  rb_ary_push(return_array, rb_float_new(c_x + (x * c_rad - y * s_rad)));
  rb_ary_push(return_array, rb_float_new(c_y + (x * s_rad + y * c_rad)));
  return return_array;
}

Joe Van Dyk <joevandyk@gmail.com> writes:

> This is taking me about a third of a second to do. Seems a bit high
> if I want to do this at least twice a second and leave room on the
> machine to do other things:

[...]

> player.polygon = player.polygon.collect do |x,y|

How about this?

   player.polygon.collect! do |x,y|

Didn't increase speed at all.

> So what's the next step when trying to optimize this? Convert the
> rotate function to C? Or can I use a different algorithm?

I didn't look at your algorithm, but before starting to convert stuff
into C, I'd try eliminating the extra object creation. I don't know
how much time it'll buy you, but maybe even try making `rotate'
destructively change the array instead of returning a new one.

I could try that. Is there some Ruby idiom for going through every
element in an array and destructively changing the values in it?
Using collect! didn't seem to help at all.

···

On 7/12/05, Daniel Brockman <daniel@brockman.se> wrote:

From: Joe Van Dyk <joevandyk@gmail.com>
Subject: Re: Updating GUIs
Date: Wed, 13 Jul 2005 16:23:58 +0900
Message-ID: <c715e6405071300231324eb65@mail.gmail.com>
> > How many steps do you need for rotation?
> > If that is integer order, you should create and hide all polygons
> > for all rotate steps at the beginning, and show (move) the one for
> > current degree on each update phase.
>
> I'm not sure what you mean by "integer order". In fact, that whole
> paragraph confused me. :slight_smile:

I'm very sorry.
If you need 5 degree step of rotation, you need only 72 polygons
for each player. So, instead of calculating, create 72 polygons at
first. And select one of them to show the player, and hide other
71 polygons. If the direction of the player is changed, select
another one from hidden 71 polygons and hide current polygon.

Oh! That's a great idea. I understand now. The cost of that is that
I need 72 times more memory for the TkcPolygons, right? Which
probably isn't too significant.

> How can I use TclTklp._eval?

TclTkIp._eval("... Tcl/Tk script ...")

Oh, I see. Thanks.

···

On 7/13/05, Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp> wrote:

Oh for god's sakes.

···

On 7/13/05, Mike Woodhouse <mikewoodhouse@gmail.com> wrote:

> Why do I need this here? If it's not, p.polygon is nil

Joe,

While I can't help with much else, I did, while playing with your
sample, figure this one out - you've misspelt "initialize"... (the
third "i" is missing).

Mike