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

