<rdf:RDF
    xmlns:s='http://snipsnap.org/rdf/snip-schema#'
    xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
    xml:base='http://www.loadbang.net/rdf'>
    <s:Snip rdf:about='http://www.loadbang.net/rdf#Software/net.loadbang.shado/README.MANUAL'
         s:cUser='nick'
         s:oUser='nick'
         s:mUser='nick'>
        <s:name>Software/net.loadbang.shado/README.MANUAL</s:name>
        <s:content>{code:none}&#xA;S H A D O   M A N U A L&#xA;&#xA;This file will eventually be a manual; for now, it&apos;s a working&#xA;overview of the shado architecture and components, with some fragments&#xA;of Python code which show how it works. Read in conjunction with the&#xA;Python examples and the javadocs, it should be enough information to&#xA;start writing shado-based applications.&#xA;&#xA;- THE RENDERING MODEL&#xA;  -------------------&#xA;&#xA;shado is a compositing and sprite library for the monome, written in&#xA;Java. It is designed to be driven from a lightweight scripting&#xA;language such as Python, and it uses a Java OSC library to talk to the&#xA;monome via the MonomeSerial program.&#xA;&#xA;(The Java OSC library is not yet used for input, for hysterical&#xA;reasons; this will change in due course.)&#xA;&#xA;An application which uses shado will build structures of &quot;frames&quot;,&#xA;&quot;blocks&quot; and &quot;view ports&quot;. These objects know how to present&#xA;themselves to a rendering object, which generates the OSC messages for&#xA;MonomeSerial. The renderer is smart enough to send incremental updates&#xA;for a display, to minimise network traffic and load.&#xA;&#xA;The shado library doesn&apos;t have things called sprites per se, but any&#xA;structure of blocks, frames and view ports can be manipulated and&#xA;altered dynamically, in Python (say). If components are moved around&#xA;relative to one another, or if their layering order or visibility are&#xA;changed, the result is sprite-like animation.&#xA;&#xA;- BLOCKS&#xA;  ------&#xA;&#xA;A block is a two-dimensional matrix of data values representing monome&#xA;lights, or pixels. Each block pixel value (a type we refer to as a&#xA;&quot;lamp&quot;) can have the obvious value ON or OFF; a lamp can also have the&#xA;value THRU (which means &quot;transparent&quot;: show whatever is beneath) or&#xA;FLIP (invert whatever is beneath). It is possible to render a block&#xA;directly to a monome - the block is considered to be sitting on a&#xA;black base, so ON and FLIP cause the lamp to light, and OFF and THRU&#xA;turn it off.&#xA;&#xA;Here&apos;s a simple 4 x 4 square of lights (see the example code for all&#xA;the details of the required import statements):&#xA;&#xA;&#9;outputter = OSCOutputter(&apos;localhost&apos;, 8080, 8, &apos;/m64&apos;)&#xA;&#9;renderer = Renderer(8, 8, outputter)&#xA;&#9;block = Block(4, 4).fill(LampState.ON)&#xA;&#9;renderer.render(block)&#xA;&#xA;Block(...).fill(...) sets all pixels in the block. Alternatively,&#xA;single pixels can be set (via setLamp()). Block also has a&#xA;constructor which takes an ASCII string representing pixel&#xA;values (useful for quickly initialising those digital clock&#xA;demos):&#xA;&#xA;&#9;block = Block(&apos;111 101 101 101 111&apos;)&#xA;&#xA;&apos;0&apos; means OFF, &apos;1&apos; means ON, &apos;.&apos; means THRU and &apos;/&apos; means FLIP.  Each&#xA;token is a row; the tokens are separated by white space.&#xA;&#xA;- FRAMES&#xA;  ------&#xA;&#xA;A frame is a stack of blocks, where items at the &quot;top&quot; obscure, or&#xA;modify, items lower down. In fact, more generally, a frame is a stack&#xA;of &quot;renderables&quot;, which may be blocks, view ports (described later),&#xA;or other frames. This recursive structure allows a complex animation&#xA;(say) to be constructed in a frame, and then for this frame to be&#xA;moved around inside an enclosing frame. (This is how the &quot;UFO&quot;&#xA;animation sequence works.)&#xA;&#xA;Unlike blocks and view ports, frames do not have dimensions; a frame&apos;s&#xA;&quot;size&quot; can be considered to be defined by the extents of any blocks or&#xA;view ports it, or its sub-frames, contain. The renderer does not care&#xA;about the size of frames (or of blocks or view ports, for that&#xA;matter): it knows the size of the monome it&apos;s addressing, and scans an&#xA;area corresponding to the monome&apos;s width and height, asking&#xA;renderables to deliver their data. (So, an obvious way to hide a block&#xA;is to move it beyond the monome&apos;s coordinates; there are better ways,&#xA;outlined below.)&#xA;&#xA;A frame renders its pixel values from the bottom to the top of its&#xA;list; higher items modify what is below. Lamp values of OFF and ON&#xA;obscure underlying pixels; THRU passes pixels through unchanged, FLIP&#xA;inverts them. The resulting pixel values in a frame still have these&#xA;four lamp values, which are then interpreted by any parent frame as&#xA;part of its stack of renderables, and so on to the actual frame which&#xA;is rendered. The renderer asks this outermost frame or block to &quot;fold&quot;&#xA;lamp values to ON or OFF (i.e., render them against black) prior to&#xA;transmitting them.&#xA;&#xA;The frame constructor has no arguments, so:&#xA;&#xA;&#9;f = Frame()&#xA;&#9;renderer.render(f)&#xA;&#xA;is an easy way to clear a monome (an empty frame renders to black).&#xA;&#xA;Renderables are added using add():&#xA;&#xA;&#9;block = Block(.....)&#xA;&#9;Frame.add(block, 2, 3)&#xA;&#xA;This adds a renderable to the *top* of a frame, in this case&#xA;offset horizontally by two pixels and vertically by three.&#xA;&#xA;Frame.add() returns the frame itself, allowing cascading:&#xA;&#xA;&#9;Frame.add(b1).add(b2)&#xA;&#xA;A renderable can only be added to any frame once - this is because the&#xA;renderable value is used in subsequent operations like show(), hide()&#xA;and top(). It is possible to get replicated tiling effects by&#xA;creating multiple unique sub-frames (or view ports) over a single&#xA;block, and adding these to the main frame. (There&apos;s a simple demo&#xA;in &quot;Animate.py&quot; which does this.)&#xA;&#xA;A frame allows a few operation on its stack of renderables:&#xA;&#xA;&#9;frame.top(item):&#9;bring a renderable to the top&#xA;&#9;frame.bottom(item):&#9;send a renderable to the bottom&#xA;&#9;frame.hide(item):&#9;hide a renderable (make it transparent)&#xA;&#9;frame.show(item):&#9;show a renderable&#xA;&#9;frame.remove(item):&#9;remove a renderable from a frame&#xA;&#xA;And finally, some sprite action:&#xA;&#xA;&#9;frame.moveBy(item, dx, dy):&#xA;&#9;&#9;&#9;&#9;move a renderable by this distance&#xA;&#9;frame.moveTo(item, x, y):&#xA;&#9;&#9;&#9;    &#9;move a renderable to this location&#xA;&#9;&#9;&#9;&#9;relative to the frame&apos;s origin&#xA;&#xA;Since the renderables might themselves be frames, all sorts of nested&#xA;movement and animation is possible. In addition, hide() and show()&#xA;(or, depending on style, bottom() and top()) can be used for&#xA;animation: if you want to invert an entire monome, use a frame with&#xA;a large&#xA;&#xA;&#9;Block(...).fill(LampState.FLIP)&#xA;&#xA;at the top; hide() and show() calls on it will invert everything. If&#xA;you want to switch between a number of different patterns, create them&#xA;all in advance, making sure they are all the same size and are opaque&#xA;(ON or OFF values only, no THRU or FLIP) and them call frame.top() on&#xA;them in sequence. (See Counter.py for an example.)&#xA;&#xA;- VIEW PORTS&#xA;  ----------&#xA;&#xA;ViewPorts provide a simple way to crop blocks or (more likely) frames,&#xA;useful if animated sub-frames are being tiled into a larger&#xA;system. When a view port is built around a renderable (a block or&#xA;frame), the result is a port onto that renderable; anything outside&#xA;the port is rendered as LampState.THRU (transparent). There is no&#xA;change to the coordinate system of the contents of the port.&#xA;&#xA;ViewPorts are also renderables, and so may be incorporated into&#xA;frames, cropped in other ports, and so on.&#xA;&#xA;After:&#xA;&#xA;       p = ViewPort(renderable, x, y, width, height)&#xA;&#xA;the renderable &quot;p&quot; will be the same as `renderable&apos; for pixels whose&#xA;column is between x and x+width-1, and whose row is between y and&#xA;y+height-1. Outside those coordinates, the pixels of &quot;p&quot; will be&#xA;LampState.THRU.&#xA;&#xA;ViewPort objects also expose properties &quot;x&quot;, &quot;y&quot;, &quot;width&quot; and &quot;height&quot;,&#xA;so that the cropping dimensions can be changed dynamically:&#xA;&#xA;        p.x = 3&#xA;        p.height = p.height - 1&#xA;&#xA;- BUTTON INPUT&#xA;  ------------&#xA;&#xA;The machinery for dealing with button presses works with the same&#xA;structures as those used to drive the LEDs. Once a structure of&#xA;blocks, frames and view ports has been built to generate output,&#xA;button presses can be routed into those same blocks, frames and view&#xA;ports. The assumption is that an application which draws some kind of&#xA;animated widget with a bit of scripting code will also want to capture&#xA;button presses locally in that same portion of code, with sensible&#xA;local coordinates, regardless of what else might be going on in the&#xA;system at the time.&#xA;&#xA;Here&apos;s how it works: the Block and ViewPort classes both implement an&#xA;interface called IPressable in the Java world. This means they contain&#xA;a method as follows:&#xA;&#xA;&#9;public boolean press(int x, int y, int how);&#xA;&#xA;The Block and ViewPort classes provide a method which does nothing; in&#xA;order to respond to button presses, a Block or ViewPort must be&#xA;sub-classed and this method overridden. This can obviously be done in&#xA;Java, but it can also be done in Python.&#xA;&#xA;When a button is pressed, shado searches a tree of renderables in&#xA;order, until it finds one which handles the press; once the press is&#xA;handled, the search stops. If the renderable returns true from the&#xA;call to press(), then it is considered to have handled the event.&#xA;&#xA;A Block or ViewPort can only handle a button press which falls within&#xA;its coordinates; if the button press is outside the renderable&apos;s&#xA;dimensions then the renderable never sees it.&#xA;&#xA;(This, by the way, is why Frames cannot directly intercept button&#xA;presses (they are not IPressables): a Frame does not have an obvious&#xA;coordinate range.)&#xA;&#xA;If a Block measuring (width * height) is within range of a press, it&#xA;will be passed X and Y coordinates within (0, 0) and (width-1,&#xA;height-1). If a ViewPort receives a press, the coordinate (0, 0)&#xA;coincides with the top-left corner of the port, rather than (0, 0) in&#xA;the port&apos;s coordinate system.&#xA;&#xA;A button press can be routed into any renderable: Block, Frame or&#xA;ViewPort. (Even though a Frame cannot handle presses directly, it will&#xA;pass them on to its children.) There&apos;s a class called PressManager&#xA;which does this (and which also tracks on and off presses, as we&#xA;describe later):&#xA;&#xA;&#9;f = Frame()&#xA;&#9;...&#xA;&#9;manager = PressManager(f)&#xA;&#9;...&#xA;&#9;manager.press(x, y, how)&#xA;&#xA;(A PressManager can be built over a completely different structure to&#xA;the one being displayed - but in most cases you probably don&apos;t want to&#xA;do that.)&#xA;&#xA;If the PressManager is constructed around a Block, the routing is&#xA;simple: Block.press() gets called with the same coordinates that are&#xA;passed in to PressManager.press() - these are presumably coming&#xA;directly from the monome.&#xA;&#xA;Sending a press to a ViewPort is slightly more complicated. The&#xA;ViewPort might accept the event (by returning true from its press()&#xA;method) in which case the event is considered finished. If the&#xA;ViewPort returns false, the event is routed into the ViewPort&apos;s&#xA;*content* - another renderable - with the original coordinates - and&#xA;the result is whatever the content renderable returns.&#xA;&#xA;When a press is routed to a Frame, the Frame starts calling into its&#xA;stack of children in order, from top to bottom, mapping the&#xA;coordinates so that each child sees (0, 0) as top-left. As soon as a&#xA;child returns true, the event is over. If any child returns false, the&#xA;Frame tries the next, and so on. If all children return false (or if&#xA;the Frame is empty), the result is false.&#xA;&#xA;Note that the handling of button presses by objects bears no relation&#xA;to their visibility (but only to their coordinate range). An object&#xA;which has been hidden (by Frame.hide()) will still be sent press()&#xA;events. A Block which is completely transparent (all cell values are&#xA;LampState.THRU) will also receive press() events. There are situations&#xA;where this is useful: to capture the raw coordinates of a monome&apos;s&#xA;buttons regardless of the objects in a frame, just add a monome-sized&#xA;transparent layer to the top and use this to deal with the press()&#xA;events.&#xA;&#xA;Finally: a note about button presses and releases. If a button press&#xA;is routed to an object deep within a visual heirarchy, then that&#xA;structure can change dramatically before the button is released. For&#xA;example, suppose that a Block receives a button press, and its press()&#xA;method actually moves the Block within its enclosing Frame. The button&#xA;release could have coordinates different to those of the press; or the&#xA;release might be completely out of range of the new location of the&#xA;Block.&#xA;&#xA;We have implemented some machinery which guarantees a fundamental&#xA;property of button handling: if a renderable receives - and handles -&#xA;a button press at coordinates (x, y), then it will always receive the&#xA;corresponding release at the same coordinates. It does not matter if&#xA;the renderable has been moved out of range of the button - or even if&#xA;the renderable has been completely removed from the object heirarchy -&#xA;the PressManager keeps hold of it, purely so that the press(x, y, 0)&#xA;can be sent to the original recipient of press(x, y, 1).&#xA;&#xA;A side-effect of this is that, if an object chooses to ignore a press&#xA;(by returning false from a press(x, y, 1) call) then it will never get&#xA;the release call - that call will always go to the actual object which&#xA;dealt with the press (if any).&#xA;&#xA;Another side-effect is that an object might receive multiple button-on&#xA;presses at the same coordinates. If a press(0, 0, 1) event to a Block&#xA;causes it to move, another button on the monome might now map to the&#xA;Block&apos;s top-left, and might send a second press(0, 0, 1). In other&#xA;words, it&apos;s quite possible for button-on events to be duplicated in&#xA;the same location - and the release events will also be&#xA;duplicated. This makes perfect sense to the PressManager so it had&#xA;better make sense to your Python scripts.&#xA;{code}&#xA;</s:content>
        <s:mTime>2008-07-20 16:37:35.0</s:mTime>
        <s:cTime>2008-06-27 21:34:12.0</s:cTime>
        <s:comments
             rdf:type='http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag'/>
        <s:snipLinks>
            <rdf:Bag>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Software/net.loadbang.shado'/>
                <rdf:li rdf:resource='#snipsnap-index'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Software/net.loadbang.shado/Tutorials'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Software/net.loadbang.jython'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Software/net.loadbang.web'/>
                <rdf:li rdf:resource='#nick'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Software/net.loadbang.shado/README'/>
                <rdf:li rdf:resource='#snipsnap-search'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Software/net.loadbang-SQL/MySQL'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#SnipSnap/config'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Software/net.loadbang.groovy'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Software/Registry'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#start/2008-07-27/1'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Tech Notes/Eclipse and MXJ'/>
                <rdf:li rdf:resource='http://www.loadbang.net/rdf#Software/TextBrick'/>
            </rdf:Bag>
        </s:snipLinks>
        <s:attachments
             rdf:type='http://www.w3.org/1999/02/22-rdf-syntax-ns#Bag'/>
    </s:Snip>
</rdf:RDF>
