Nitro wrote:
have you found the time to review the use case?

Finally! Sorry that took so long. Enclosed is a version with my interspersed comments, proceeded but "#chb:"

-Chris




--
Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R            (206) 526-6959   voice
7600 Sand Point Way NE   (206) 526-6329   fax
Seattle, WA  98115       (206) 526-6317   main reception

[EMAIL PROTECTED]
# FlowerBed example
# - groups (although a layer can be conceived a group)

#chb: Good point -- what is the difference between a layer and a group?


import FloatCanvas as fc

class ModelEventSender(object):
    def __setattr__(self, name, value):
        object.__setattr__(self, name, value)
        fc.sendEvent( fc.events.modelChanged, self )

#chb: Should there be a generic ModelEventSender class in FloatCanvas?

class FlowerBedModel(ModelEventSender, fc.IRectangleData)
    def __init__(self, name):
        self.name = name
        self.color = 'brown'
        self.flowers = []
        self.size = (100, 100)


class FlowerModel(ModelEventSender):
    def __init__(self, name, no_blades = 5, blade_colour = 'red', size = 10):
        self.name = name
        self.no_blades = no_blades
        self.blade_colour = blade_colour
        self.size = size


class SimpleFlowerRenderer(object):
    def Render(self, renderer, data):
        # draw the center part an dynamically generate and apply the look
        look = fc.DefaultLook( line_colour = 'black', fill_colour = 
data.blade_colour )
 #chb: This doesn't seem right -- now we have the look getting info from the 
data -- shouldn't the look and the data be orthogonal?
        renderer.ApplyLook( look )
#chb: what if there is more than one "look" -- like the center of the flower is 
black, the petals are red? Do you have multiple looks?
        renderer.DrawCircle( (0,0), data.size )
        # draw a line per blade
        for blade in range(data.no_blades):
            x, y = sin(no_blades / blade * 2 * PI) * size, cos(no_blades / 
blade * 2 * PI) * size
#chb: there should be a way to put this kind of math outside of the Render 
method, in case it is computationally expensive.
            renderer.DrawLine( (0,0), (x,y) )

    def GetBoundingBox(self, data):
        return (data.size, data.size)

#chb: what coordinate system is the BoundingBox in?
#chb: (by the way, let's use properties where we can, so .BoundingBox would be 
a property)


def createViewFromModel(flowerBed, canvas):
    flowerLayer = fc.BitmapLayerNode()              # this draws all its 
children into a bitmap, caches it and uses it for subsequent drawing, not sure 
if it should really be a node

#chb: Should a BitmapLayer be a different class than a regular layer? Or could
#chb: we just set a flag as to whether the layer gets cached as a bitmap --
#chb: it seems that would be more flexible.

    labelLayer = fc.WindowLayerNode()

    canvas.addChild( flowerLayer, where = 0 )
    canvas.addChild( labelLayer, where = fc.FRONT )

#chb: is fc.FRONT a constant like MaxInt -- or does it dynamically figure out
#chb: what the z-value is for the front? If it's not a number, then I prefer
#chb: text flags: where = "back"

    genericFlowerRenderer = SimpleFlowerRenderer()
    labelRenderer = fc.defaultRenderers.ScaledTextRenderer()
    labelLook = fc.TextLook( base_size = 10, fill_colour = 'black' )

    # create flowers and their labels and a row
    for i, flowerData in enumerate(flowerBed.flowers):
        pos_x, pos_y = i % 5, i // 5    # put 5 flowers in a row
        pos_x *= 25
        pos_y *= 25
        
        flowerNode = fc.RenderableNode( flowerData, genericFlowerRenderer, 
fc.NoLook )          # use fc.NoLook since the flower renderer sets its own one
        flowerNode.transformer.translation = (pos_x, pos_y)                     
                # the default transformer simply applies a 3x2 matrix
        labelLayer.addChild(flowerNode)

        labelNode = fc.RenderableNode( flowerData.name, labelRenderer, 
labelLook )
        labelNode.transformer.translation = (pos_x, pos_y + 15)                 
                  # put label a bit below the flower
#chb: What unit is that in? You may want a relative position in world coords, 
or in pixel or paper coords.

        flowerLayer.addChild( labelNode )

    bed = fc.Rectangle( flowerBed )
    bed.enable = True       # just show the attribute, not strictly needed here
    flowerLayer.addChild( bed, where = fc.BACK )

#chb: Overall, thius looks like too much work -- perhaps some of those calls 
could get rolled into a flowNode factory function.    
    def customTransformer(node, coordinates):
        ''' square the coordinates for lack of imagniation of something more 
sensible '''
        return coordinates.x ** 2, coordinates.y ** 2
        
    bed.transformer = customTransformer

    def onClickFlower1(node, evt):
        node.data.size *= 2                                                   # 
change the data which will in turn change the view

    flower1 = flowerLayer.getChildren()[0]
    flower1.events.Bind( fc.events.LEFT_CLICK )                               # 
is flower1.Bind better here instead

    return flower1

   

flowerBed = loadFlowerbedModel()

renderer = fc.GCRenderer()
canvas = fc.Canvas()

flower1 = createViewFromModel( flowerBed, canvas )

# the default cam, looking at 500, 500
myCamera = fc.DefaultCamera( target = (500, 500), zoom = (1.0, 1.0) )
# a small inset in the upper left shows a zoomed area of the default cam
detailCamera = fc.DefaultCamera( target = (500, 500), zoom = (5.0, 5.0), 
viewport_rect = ( (0,0), (200,200) ) )

#chb: this feels a bit too OO -- couldn't there be a Canvas method 
fc.Canvas.SetView(). We may want to use some sort of camera object internally, 
but the user shouldn't need to deal with them. Also, I think the viewport_rect 
pretty much defines view -- the actual pixel size of the view should be set 
elsewhere.


# this will be triggered whenever the canvas wants to redraw itself
def Render(canvas, evt):
    canvas.Render( renderer, myCamera )
    canvas.Render( renderer, detailCamera )

#chb: I'm confused a bit now what is a canvas? I was imaging a canvas would 
hvae one view, but maybe that's what you're calling a renderer.

canvas.Bind( fc.events.renderCanvas, Render )

#chb: shouldn't this be built-in to the canvas?

# now change the model a bit and see if the view catches it :-)
flowerBed.flowers[0].size *= 2

# move one of the objects, this should cause the object to send a 
fc.events.viewChanged message. This can then be caught by some controller to 
change the model.
flower1.transformer.Rotate( PI/4 )

#chb; keep in mind the "Law of Demeter" here -- the user now needs to know that 
the flower object has a transformer object as an attribute, tnat that has a 
Rotate method. -- I think you should be able to call flower1.Rotate directly -- 
keep the implimentation hidden.

#chb: I've been thinking about all this a bit, and I think there are a couple 
levels at which to look at the structure -- the Canvas level and the DrawObject 
level. I like where you're going with breaking down the DrawObjects into Data, 
Look, Transform, etc. However, how that breakdown should be done is very 
dependent on the paticular draw object, and how it gets used. Hoever, there is 
no need to lock that in place. What we can do is define the API for a 
DrawObject (or node, or..) that the Cavnas needs, and it's up to the individual 
object to create that API as it wishes. For example, a Circle object may only 
need a diameter for its data model -- that could be a simple attribute, wheras 
a more complex object may need a more complex data model.

#chb: The one exception may be the "look" object -- we may want all DrawObjects 
to have a "look", so that you could, for instance, apply a look to a group of 
arbitrary objects, copy the look from an object, etc.

#chb: Anyway, it's time to move on, and get some prototype code working, and 
we'll see how more of this shakes out. 

#chb: Do keep in mind that one of the goals of FloatCanvas is to provide a 
simle API -- if people want complete control over eveything, then they can jsut 
use a GC. For the most part, regular users shoudln;t hve to think in term so 
transforms, etc. -- they should be able to just place an object with a few 
properties on a canvas and have it drawn for them.









_______________________________________________
FloatCanvas mailing list
[email protected]
http://mail.mithis.com/cgi-bin/mailman/listinfo/floatcanvas

Reply via email to