[wxPython] FW: DrawPointList and DrawLineList

I'm forwarding this email to the list in the hopes that other people that
are interested in a higher performance method of drawing to a DC and that
know some C++ might be able to help.

Originally, I was thinking that it would be simplest to just provide a list
of points or lines. However, after looking at some other drawing APIs, it
probably makes sense to support a pen and brush as part of the argument
list. I could see wanting to draw a series of lines in a given color, with a
particular brush pattern, and copy mode (XOR...)

Other ideas? Any C++ coders here that could take this on?

ka

···

-----Original Message-----
From: cbarker@waves.hazmat.noaa.gov
[mailto:cbarker@waves.hazmat.noaa.gov]On Behalf Of Chris Barker
Sent: Monday, September 10, 2001 11:59 AM
To: Kevin Altis
Cc: Robin Dunn
Subject: Re: FW: Possible contribution for PythonCard

Kevin Altis wrote:

Robin,
Would it
be possible to provide two methods in wxPython 2.3.2 that would accept a
list of points (DrawPointList) and lines (DrawLinesList) that would then
call the appropriate C++ routines directly for each item in the list to
improve performance?

Kevin (and Robin),

Robin and I discussed this a while ago, and I think the short version is
(Robin, please correct and clarify as you see fit):

Yes, it could be done, and would probably help

Yes, it could be integrated into the wxPython code without too much
trouble

Yes, Robin would help with that part

But: someone would have to actually write the code!

I am interested in doing it, but I have no experience with either SWIG
or C++ (and only a little C) so it would be a lot for me to take on, so
I havn't started yet. I'm probably going to be using wxPython for a
project I'm working on were this would be a big help, and I have a
couple of experienced C++ progarmmeres helping me, so I may get to it.
However, we're on a tight schedule, so If I can get the performance I
need without it, it won't happen.

I wish I could contribute more, but we all have more things we'd like to
do than time to do them!

Chris probably has some other suggestions as well.

Yes, I do. both for performance and easier coding. Let's face it, how
often does someone have a single point to draw? THe data is probably
already in a sequence of some kind, so why not use it directly? I'm full
of ideas, if anyone else wants to impliment them!

-Chris

Hi Kevin, etc.

I could probably be persuaded to do this. I've mucked around in this area of
the wxPython internals before so that I expect that it should be relatively
straightforward (famous last words....). However, since this is just for fun
and I'm busy at the moment, it would probably be at least a couple of weeks
before I could get to it. (Offers of beer have been known to speed me up
however....)

As to syntax, has the Draw<Thing>List syntax been blessed by Robin? It seems
reasonable to me in that it would be extensible to DrawPolygonList, etc if
someone later gets the urge. As far as Pens and Brushes go, I think that
only the pen is used for lines and points, so need to worry about the brush.
I would propose something like the following:

dc.DrawPointsList(points, pens=None)

If 'pens' is None, the current pen is used, otherwise 'pens' would be a list
equal in length to 'points'. This would be equivalent to:

N = len(points)
assert (pens is None or len(pens) == len(points), 'lengths of pens and
points must match')
for i in range(N):
   if pens:
      dc.SetPen(pens[i])
   dc.DrawPoint(points[i])

dc.DrawLines would be analogous.

-tim

···

----- Original Message -----
From: "Kevin Altis" <altis@semi-retired.com>
To: "Wxpython-Users" <wxpython-users@lists.wxwindows.org>
Sent: Monday, September 10, 2001 11:51 AM
Subject: [wxPython] FW: DrawPointList and DrawLineList

I'm forwarding this email to the list in the hopes that other people that
are interested in a higher performance method of drawing to a DC and that
know some C++ might be able to help.

Originally, I was thinking that it would be simplest to just provide a

list

of points or lines. However, after looking at some other drawing APIs, it
probably makes sense to support a pen and brush as part of the argument
list. I could see wanting to draw a series of lines in a given color, with

a

particular brush pattern, and copy mode (XOR...)

Other ideas? Any C++ coders here that could take this on?

ka

-----Original Message-----
From: cbarker@waves.hazmat.noaa.gov
[mailto:cbarker@waves.hazmat.noaa.gov]On Behalf Of Chris Barker
Sent: Monday, September 10, 2001 11:59 AM
To: Kevin Altis
Cc: Robin Dunn
Subject: Re: FW: Possible contribution for PythonCard

Kevin Altis wrote:
> Robin,
> Would it
> be possible to provide two methods in wxPython 2.3.2 that would accept a
> list of points (DrawPointList) and lines (DrawLinesList) that would then
> call the appropriate C++ routines directly for each item in the list to
> improve performance?

Kevin (and Robin),

Robin and I discussed this a while ago, and I think the short version is
(Robin, please correct and clarify as you see fit):

Yes, it could be done, and would probably help

Yes, it could be integrated into the wxPython code without too much
trouble

Yes, Robin would help with that part

But: someone would have to actually write the code!

I am interested in doing it, but I have no experience with either SWIG
or C++ (and only a little C) so it would be a lot for me to take on, so
I havn't started yet. I'm probably going to be using wxPython for a
project I'm working on were this would be a big help, and I have a
couple of experienced C++ progarmmeres helping me, so I may get to it.
However, we're on a tight schedule, so If I can get the performance I
need without it, it won't happen.

I wish I could contribute more, but we all have more things we'd like to
do than time to do them!

> Chris probably has some other suggestions as well.

Yes, I do. both for performance and easier coding. Let's face it, how
often does someone have a single point to draw? THe data is probably
already in a sequence of some kind, so why not use it directly? I'm full
of ideas, if anyone else wants to impliment them!

-Chris

_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwindows.org
http://lists.wxwindows.org/mailman/listinfo/wxpython-users

From: Tim Hochberg

I could probably be persuaded to do this. I've mucked around in
this area of
the wxPython internals before so that I expect that it should be
relatively
straightforward (famous last words....). However, since this is
just for fun
and I'm busy at the moment, it would probably be at least a
couple of weeks
before I could get to it. (Offers of beer have been known to speed me up
however....)

So where should the beer be sent? :slight_smile:

As to syntax, has the Draw<Thing>List syntax been blessed by
Robin? It seems
reasonable to me in that it would be extensible to DrawPolygonList, etc if
someone later gets the urge. As far as Pens and Brushes go, I think that
only the pen is used for lines and points, so need to worry about
the brush.
I would propose something like the following:

dc.DrawPointsList(points, pens=None)

If 'pens' is None, the current pen is used, otherwise 'pens'
would be a list
equal in length to 'points'. This would be equivalent to:

N = len(points)
assert (pens is None or len(pens) == len(points), 'lengths of pens and
points must match')
for i in range(N):
   if pens:
      dc.SetPen(pens[i])
   dc.DrawPoint(points[i])

dc.DrawLines would be analogous.

Actually, I don't think you want to accept a pen list, the overhead for
setting the pen with each draw operation is huge, so it is better to just
set it once prior to the loop. There could be another version of the drawing
that does accept a list of pens, but my guess is that the user code would be
better off chunking the operation to use a single pen for each group of
points or lines rather than have a pen set for every single point/line
regardless of whether it was necessary. SetOptimization should be used as
well.

The more I think about it, the more it seems like this should be a method in
wxWindows that Robin wraps to provide the list to C++ pointer array
translation or whatever is appropriate.

What do you think Robin?

ka

Hi Kevin,

So where should the beer be sent? :slight_smile:

You can worry about that later, if we ever decide on a syntax and I ever do
any work :wink:

[snip]

> dc.DrawPointsList(points, pens=None)
>
> If 'pens' is None, the current pen is used, otherwise 'pens'
> would be a list equal in length to 'points'.

[snip]

Actually, I don't think you want to accept a pen list, the overhead for
setting the pen with each draw operation is huge, so it is better to just
set it once prior to the loop.

That's quite possible, I don't know what the overhead of setting the pen
from C++ would be, but I don't think it matters. The overhead for doing the
check "if pens is None" is essentially zilch, so the default would be no
slower than what you want. And if someone (like me for instance) wants to
set the pen everytime, then that would work as well. I'd rather avoid having
a proliferation of similar functions (such as both DrawPointsList and
DrawPointsWithPensList) when one will do.

There could be another version of the drawing
that does accept a list of pens, but my guess is that the user code would

be

better off chunking the operation to use a single pen for each group of
points or lines rather than have a pen set for every single point/line
regardless of whether it was necessary. SetOptimization should be used as
well.

I assume you mean something equivalent to:

def drawPointsList(points)
   oldOpt = dc.GetOptimization()
   dc.SetOptimization(1)
   # draw all the points....
   dc.SetOptimization(oldOpt)

After looking at SetOptimization a bit, I have to disagree. It seems that it
would work perfectly well to set the optimization outside of the
DrawPointsList routine. And, reading the caveats on SetOptimization leaves
me feeling that we should not be messing with the optimization behind the
programmers back...

The more I think about it, the more it seems like this should be a method

in

wxWindows that Robin wraps to provide the list to C++ pointer array
translation or whatever is appropriate.

I suspect that is not necessary. Based on my experience optimizing
DrawLines, the bottleneck is going to be extracting the points from the
sequences you pass in. (Although, if you are using NumPy, this may not be
strictly true.) In any event, I'd guess we could get at least a 10x speedup
doing the 'simple' thing, and that should probably be what's tried first.
In any event there's really no need in wxWindows proper for this kind of
operation, since when writing in C++ the call overhead is much, much lower.

(Just in case this is confusing -- although I use Python psuedocode for all
my examples, this would all be written in C++ and would probably look a lot
like the code wxPoint_LIST_helper from helpers.cpp in the wxPython
distribution.)

-tim

Kevin Altis wrote:

> From: Tim Hochberg
>
> I've mucked around in
> this area of
> the wxPython internals before so that I expect that it should be
> relatively
> straightforward (famous last words....).

You did the work on wxPoint_LIST_Helper for NumPy arrays didn't you?
Nice work.

(Offers of beer have been known to speed me up however....)
So where should the beer be sent? :slight_smile:

I'll send some too...

> As to syntax, has the Draw<Thing>List syntax been blessed by
> Robin?

The idea, yes. the syntax: not yet, we need to work it out.

It seems

> reasonable to me in that it would be extensible to DrawPolygonList, etc if
> someone later gets the urge.

Absolutely. I would love that. It's a little less critical, however, as
it's unlikely that someone would draw thousands of polygons, where
thousands of points is pretty ordinary (for me anyway). Also, the time
it takes to draw a polygon is a LOT longer that a point, so you may not
see the same kind of perfomance improvement. For symetry and nice
syntax, however, I'd love to see it.

> As far as Pens and Brushes go, I think that
> only the pen is used for lines and points, so need to worry about
> the brush.

True, but if we want to extend to polygons, ellipses, etc. we will need
the brush.

> I would propose something like the following:
>
> dc.DrawPointsList(points, pens=None)
>
> If 'pens' is None, the current pen is used, otherwise 'pens'
> would be a list

I would prefer that you could pass a singleton pen, rather than use the
current one. Not a big deal, but I think it is a little more symetric.
So the code (in Python) would be something like:

N = len(points)
assert (type(pens) == type(wxPen) or len(pens) == len(points), 'lengths
of pens and points must match')
if type(pens) == type(wxPen):
    dc.SetPen(pens[i])
    for i in range(N):
       dc.DrawPoint(points[i])
else:
    for i in range(N):
        dc.SetPen(pens[i])
        dc.DrawPoint(points[i])

Pulling the "if" ouot of the loop probably doesn't buy much, but were
doing this for speed, right?

Actually, I don't think you want to accept a pen list, the overhead for
setting the pen with each draw operation is huge, so it is better to just
set it once prior to the loop. There could be another version of the drawing
that does accept a list of pens, but my guess is that the user code would be
better off chunking the operation to use a single pen for each group of
points or lines rather than have a pen set for every single point/line
regardless of whether it was necessary.

But this code (both Tim and my versions) does allow a single pen to be
used and set once.

SetOptimization should be used as well.

What's that?

The more I think about it, the more it seems like this should be a method in
wxWindows that Robin wraps to provide the list to C++ pointer array
translation or whatever is appropriate.

I think so too. THat way is could be put in wxDC, and it would
automagically find it's way into all the DCs. I'm guessing here, but if
it was not in the superclass that all the DCs derive from, there would
have to be a separate copy in all the various DCs. Robin?

Anyway, that is an implimentation decision, that I am not the least bit
qualified to make.

I'll work on a little Python prototype of what I'm thinking, and send in
in here.

-Chris

···

--
Christopher Barker,
Ph.D.
ChrisHBarker@home.net --- --- ---
http://members.home.net/barkerlohmann ---@@ -----@@ -----@@
                                   ------@@@ ------@@@ ------@@@
Oil Spill Modeling ------ @ ------ @ ------ @
Water Resources Engineering ------- --------- --------
Coastal and Fluvial Hydrodynamics --------------------------------------
------------------------------------------------------------------------

The more I think about it, the more it seems like this should be a method

in

wxWindows that Robin wraps to provide the list to C++ pointer array
translation or whatever is appropriate.

The one problem with this is that if it were added to the C++ wxDC then it
would be a wxArray or wxList that I would have to translate to from a Python
Sequence object before calling the wx C++ method, which would essentially
add another iteration of the data and a transformation or copy of each
element. It would be much better to add optimizations like this in Python
specific methods that get added to the real class, which SWIG makes very to
do.

I don't mind adding things like this to wxPython's DC, I just don't have
time to work out what the interface and functionality should be. So if you
guys want to take on that work and come up with at least some pseudo code
(or Python) then I can do the integration and get it into wxPython.

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!

> SetOptimization should be used as well.

What's that?

Actually, now that I went back and reread the docs, we probably don't need
to do anything since SetOptimization(true) is the default.

From the docs:

"wxDC::SetOptimization
void SetOptimization(bool optimize)

If optimize is TRUE (the default), this function sets optimization mode on.
This currently means that under X, the device context will not try to set a
pen or brush property if it is known to be set already. This approach can
fall down if non-wxWindows code is using the same device context or window,
for example when the window is a panel on which the windowing system draws
panel items. The wxWindows device context 'memory' will now be out of step
with reality.

Setting optimization off, drawing, then setting it back on again, is a trick
that must occasionally be employed."

ka

I think so too. THat way is could be put in wxDC, and it would
automagically find it's way into all the DCs. I'm guessing here, but if
it was not in the superclass that all the DCs derive from, there would
have to be a separate copy in all the various DCs. Robin?

Using SWIG's %addmethods directive I can add things to classes whereever I
see the need, and I have done a fair bit of it already in wxPython. In this
case from Python's perspective it would appear to be a method in the base
wxDC class and so all DCs would get it.

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!

Using SWIG's %addmethods directive I can add things to classes whereever I
see the need, and I have done a fair bit of it already in wxPython. In

this

case from Python's perspective it would appear to be a method in the base
wxDC class and so all DCs would get it.

Ah! That was the hint I needed, now I see how this would be done.

-tim

Robin Dunn wrote:

Using SWIG's %addmethods directive I can add things to classes whereever I
see the need, and I have done a fair bit of it already in wxPython. In this
case from Python's perspective it would appear to be a method in the base
wxDC class and so all DCs would get it.

Cool. That sounds perfect, and you're right, it would save the
translation to wxLists, etc.

-Chris

···

--
Christopher Barker,
Ph.D.
ChrisHBarker@home.net --- --- ---
http://members.home.net/barkerlohmann ---@@ -----@@ -----@@
                                   ------@@@ ------@@@ ------@@@
Oil Spill Modeling ------ @ ------ @ ------ @
Water Resources Engineering ------- --------- --------
Coastal and Fluvial Hydrodynamics --------------------------------------
------------------------------------------------------------------------

You did the work on wxPoint_LIST_Helper for NumPy arrays didn't you?
Nice work.

Thanks!

The idea, yes. the syntax: not yet, we need to work it out.

OK. Well like I said Draw<Thing>List seems fine to me at the moment.

It seems
> > reasonable to me in that it would be extensible to DrawPolygonList,

etc if

> > someone later gets the urge.

Absolutely. I would love that. It's a little less critical, however, as
it's unlikely that someone would draw thousands of polygons, where
thousands of points is pretty ordinary (for me anyway). Also, the time
it takes to draw a polygon is a LOT longer that a point, so you may not
see the same kind of perfomance improvement. For symetry and nice
syntax, however, I'd love to see it.

That's pretty much my position. I have no need for DrawPolygonList right
now, but if I did it would be a bummer if I had to name it in some way that
did not parallel the existing usage. [PONDER] Actually I take it back I
think it should actually be DrawLineList and DrawPointList (no plurals).
wxPython has both DrawLine and DrawLines, and since I can actually envision
a use for a list version of the latter, I'd lobby for just tacking List onto
the existing method names without pluralizing anything -- that should avoid
any future naming conflicts.

> > As far as Pens and Brushes go, I think that
> > only the pen is used for lines and points, so need to worry about
> > the brush.

True, but if we want to extend to polygons, ellipses, etc. we will need
the brush.

Agreed. But that doesn't mean we would have to include it for the interface
for DrawLineList, does it? (I don't think that's what you mean, but I'm just
checking).

> > I would propose something like the following:
> >
> > dc.DrawPointsList(points, pens=None)
> >
> > If 'pens' is None, the current pen is used, otherwise 'pens'
> > would be a list

I would prefer that you could pass a singleton pen, rather than use the
current one. Not a big deal, but I think it is a little more symetric.
So the code (in Python) would be something like:

That was actually my first thought, but since DrawPoint doesn't take a pen
argument, I thought it would be more consistent to not allow a singleton pen
argument. I don't feel strongly about it either way though.

> The more I think about it, the more it seems like this should be a

method in

> wxWindows that Robin wraps to provide the list to C++ pointer array
> translation or whatever is appropriate.

I think so too. THat way is could be put in wxDC, and it would
automagically find it's way into all the DCs. I'm guessing here, but if
it was not in the superclass that all the DCs derive from, there would
have to be a separate copy in all the various DCs. Robin?

I hadn't thought about that. And my brain has misplaced the understanding of
Swig I had briefly when working on wxPoint_LIST_Helper. From what Robin says
in a subsequent message, he doesn't seem to think this is a problem, but I
need to study his message more to get a better idea about what he has in
mind.

-tim

Hi folks,

As promised, I wrote up a little prototype of the proposed Draw-List-of
stuff code.

Tim Hochberg wrote:

OK. Well like I said Draw<Thing>List seems fine to me at the moment.

I decided to go with Draw<Thing>Set instead of "List", as I'm hoping any
sequence will do, not just a list (NumPy arrays come to mind, in
particular). I'm not wedded to the name, LIst would be fine too.

That's pretty much my position. I have no need for DrawPolygonList right
now,

I do have a need, so I'd really like to see it, while we are at it, even
if the performance improvement is small.

Actually I take it back I
think it should actually be DrawLineList and DrawPointList (no plurals).

I agree, no plurals

wxPython has both DrawLine and DrawLines,

Unfortunately, "DrawLines" really should be DrawPolyline, since that's
what it does. It's too bad, also, because if DrawLines didn't exist
already, I'd be tempted to go with the plural form, rather than all this
Set or List business.

I've named my function DrawPolylineSet, as DrawLinesSet and DrawLineSet
are just too close.

Agreed. But that doesn't mean we would have to include it for the interface
for DrawLineList, does it? (I don't think that's what you mean, but I'm just
checking).

Nope, we jsut need to keep it in mind.

That was actually my first thought, but since DrawPoint doesn't take a pen
argument, I thought it would be more consistent to not allow a singleton pen
argument. I don't feel strongly about it either way though.

I wasn't sure if I wanted symetry with DrawPoint, or with the passing of
a list, so I went with either. If you pass None (or nothing) you get
the current pwn. If you pass a single pen, it is used, if you pass a
sequence of pens, those get used. It's easy to do all that in Python, I
don't know about C++ !

From what Robin says
in a subsequent message, he doesn't seem to think this is a problem,

It sounds like he has a good idea of the best way to handle it, and he
should know!

So, here is my prototype: I've done:

DrawPointSet(points,Pens = None):

DrawLineSet(Lines,Pens = None):

DrawPolylineSet(Lines,Pens = None):

DrawPolygonSet(Polygons, Pens = None, Brushes = None):

I'd like to see the whole bunch done (DrawElipse, DrawRectangle,
DrawSpline, are there more?). I don't know if it's worth it for text or
icons.

Here is the code: (I've enclosed a little app that tests it). It is
intended to prototype the API, nothing more, let me know what you all
think.

class SetDC(wxPaintDC):
    def __init__(self,Panel):
        wxPaintDC.__init__(self,Panel)

        self.pentype = type(wxRED_PEN)
        self.brushtype = type(wxBLUE_BRUSH)

    def DrawPointSet(self,points,Pens = None):
        N = len(points)
        if Pens is None:
            Pens = self.GetPen()
        assert (type(Pens) is self.pentype or len(Pens) == len(points),
                'lengths of Pens and points must match')
        if type(Pens) == self.pentype:
            self.SetPen(Pens)
            for point in points:
               self.DrawPoint(point[0],point[1])
        else:
            for (point,pen) in zip(points,Pens):
                self.SetPen(pen)
                self.DrawPoint(point[0],point[1])

    def DrawLineSet(self,Lines,Pens = None):
        N = len(Lines)
        if Pens is None:
            Pens = self.GetPen()
        assert (type(Pens) is self.pentype or len(Pens) == len(Lines),
                'lengths of Pens and Lines must match')
        if type(Pens) == self.pentype:
            self.SetPen(Pens)
            for line in Lines:
               self.DrawLine(line[0],line[1],line[2],line[3])
        else:
            for (line,pen) in zip(Lines,Pens):
                self.SetPen(pen)
                self.DrawLine(line[0],line[1],line[2],line[3])

    def DrawPolylineSet(self,Lines,Pens = None):
        # I used "PolylineSet" here because it seemed that LinesSet
would be too close to LineSet, and
        # DC.DrawLines really draws a polyline anyway.
        N = len(Lines)
        if Pens is None:
            Pens = self.GetPen()
        assert (type(Pens) is self.pentype or len(Pens) == len(Lines),
                'lengths of Pens and Lines must match')
        if type(Pens) == self.pentype:
            self.SetPen(Pens)
            for line in Lines:
               self.DrawLines(line)
        else:
            for (line,pen) in zip(Lines,Pens):
                self.SetPen(pen)
                self.DrawLines(line)

    def DrawPolygonSet(self,Polygons, Pens = None, Brushes = None):
        N = len(Polygons)
        if Pens is None:
            Pens = self.GetPen()
        if Brushes is None:
            Brushes = self.GetBrush()
        assert (type(Pens) is self.pentype or len(Pens) ==
len(Polygons),
                'lengths of Pens and Polygons must match')
        assert (type(Brushes) is self.brushtype or len(Brushes) ==
len(Polygons),
                'lengths of Brushes and Polygons must match')

        # too many to special case
        for polygon, i in zip(Polygons,range(len(Polygons))):
            if not (type(Pens) == self.pentype):
                self.SetPen(Pens[i])
            if not (type(Brushes) == self.brushtype):
                self.SetBrush(Brushes[i])
            self.DrawPolygon(polygon)

draw_win.py (7.88 KB)

···

--
Christopher Barker,
Ph.D.
ChrisHBarker@home.net --- --- ---
http://members.home.net/barkerlohmann ---@@ -----@@ -----@@
                                   ------@@@ ------@@@ ------@@@
Oil Spill Modeling ------ @ ------ @ ------ @
Water Resources Engineering ------- --------- --------
Coastal and Fluvial Hydrodynamics --------------------------------------
------------------------------------------------------------------------