News

( My personal website has gone to dust, any links thereto will fail. This site will be home for a while. )

Tuesday, 9 August 2011

If Inkscape could... wouldn't it be great?

I use Inkscape. A lot. In the Linux world there really is nothing to touch it.

I often wish.. I shake my head and regret that I can't contribute code because it's too complex for me. If I could only ...

A few blue-sky fantasies of what I would do with Inkscape. 

Take this as constructive and positive. There is no implied criticism at all. Without this amazing app I could not do what I do. Viva Inkscape!

Extending capability
In the blue-skies to come I mention many ideas that would require Inkscape to be a non-pure-svg beast of some kind. I realize that this is not part of the SVG standard that is being implemented.

I would add an 'enhanced' Inkscape format — one that contains the needed metadata and structures to manage it. Perhaps even a second file for every svg saved. Not too onerous given what it's doing. (Or maybe some database like Firefox uses.)

DRY
Don't Repeat Yourself.

Draw a logo once and use it in many other files. Alter the original and have it update everywhere.

I'd include a 'library' that would let you drill-into other svg files - a little like Blender does for linking-appending blend files.

Here's an example:
I am working on signs for a zoo. The motif is butterflies.
I have an svg with lots of drawings. I'd like to open a new svg and pull a few objects out, for the design of a poster.
I locate them in the library - it shows them visually - and drop them on my canvas.
While I'm at it, I fetch my standard insignia which I use to frame my work. Perhaps a frame with a little bit of text and a url at the bottom.

I notice some fixes I should make in a certain clone. I enter the 'clone-source edit mode' and alter the thing. It changes the original file!
Any other files using that clone when re-opened (imagine if it was all live! - that's another whole blue sky), will reflect the changes.

More than this. I imagine situations where I'd like to alter a clone, but not its source. I want bits of it to be in different colours, sizes and locations. I mark it as a fork and edit it as I please. This clone then draws its basics from the source and after that it applies my updates. Much like css, overrides come afterwards and it adapts.

I'd also like to take a certain group, turn it into a clone and place it into my 'cool shapes' library. I open that lib and drag the group into it.

The ability to fork or copy any library would be important. I may want to take something in a different direction, but not influence other users of those clones/symbols.
I could copy a library - making it unique (some kind of id hell I am sure) and then mess with it.
Forking would be similar, only I could later commit/merge and all instances would then update.

And why stop at local file paths? I'd bake-in the ability to follow any URL (be it local, network, ssh or web) to get to other files.
This would be over-arched by a kind of 'relative to X' setting: all links to other files are relative-to a path, a domain, a network share name, and any stack of them. Or just bung-on some scripted controller to re-write stuff on the fly.

At any point one can export to a normal SVG file. It would break all links and collapse whatever is needed into that ouput - providing a single svg that can move away from my hard drive.


Smart Ojects

Got a group (or a clone, or ...) that is destined to be an image on a website? Want it to output as a jpg 70% quality image? I would have more than the current 'properties' panel allows.

I would let you specify output scripts (and edit them) per object. I'd implement a cascade too, so that objects in a tree take their parent's output scripts over the children, with some flexibility - but that needs more thought.

Output scripts could cover things like path effects as well as the extensions we have now.
I could tell a path to jitter and then stretch a pattern along itself. I could change which pattern it uses dynamically!
I could tell it to duplicate itself at an offset and use another pattern along the duplicate.
I could say, hey, every 10 units place a duplicate of that clone.

And why stop at output scripts? How about input scripts, or pipes?

Say I want a clone that has text within it and I want that text to change depending on some outside influence - say a script that counts or shows the date. The entire contents of an object may be subject to a script (or a stack of them) that determines how it draws next.

Here's an example:
I have samples of logos for a client. I want a little label on each on. Logo A, logo B, logo C, etc.
I have set each logo to have an id. Why not drop a smart-label-clone into each logo and have it look-up the id and change the text! If I change the id, the label will change. Sorted.

How about objects that know where they are in relation to other objects? If one is touching another, above or below another, a percentage distance away from another - run certain scripts to change whatever is required. Perhaps a clone of a leaf can then change its tone the further back in a drawing of a tree it is placed. That kind of thing.

Entire drawings can have output and input scripts too.

Example:
Say I want this page to go out to a PDF, but I want it to collapse all the clones and convert all the filters to alpha png files etc.
I want the background colour to be different to the current one. I want it to output only what is on the page and not a whole bunch of stuff off to the side. This kind of master-control would be possible.
Say I wanted to send that PDF out to Scribus and open that app, or I want it to output a PNG of the page as well as a PDF. Etc.

Another example:
I want each object to follow its own scripts and make image files as set. I then want to herd all those files into different directories for a website I am building. I don't want to set each object's path, so I have a master script that moves all the little outputs.

Input example:
When I open this file, I want it to hit a website and fetch something. I want it to open a database and fetch something else.
I want to set variables that objects on the canvas will react to. I want to open a text file and get that into a text-area. I want to run some bash script and get its output into another script that turns into a list of colours that a bunch of other objects will use.

Another:
I have a path - let's say it's a swooping curve. What I really want is a 3D shape done in Blender. Can I not, from a script, open blender, send the path to it, run a script in Blender to extrude/colour/render and then get the result back in Inkscape? Blue sky remember. :)

Hey, why not drop a blend file into Inkscape - now it knows what to open at that position. Why not drop a text-file, why not an Open Office doc? Let's get those pipes flowing.

I would also be sure to make Inkscape scriptable from outside. At the moment the command-line is broken. Besides a general Python/Ruby/etc. approach, just using the command-line switches from bash in order to employ Inkscape as a pipe would be super powerful.

In short, I see a kind of spreadsheet capacity in Inkscape. By allowing scripts to pipe in and out between any objects the capacity is unlimited.


Abstract the code
The engine
The svg drawing engine, make it a library.

I would use it from Python. Others would use it from other things.

Imagine Open Office or Blender being able to open and render Inkscape SVG files.

I don't mean just a few curves and weirdly deformed clones. I mean the whole works: layers, filters, clones, groups, all of it. Just imagine that!

Release the magic svg renderer. The world will love you. librsvg is just not cutting the mustard.

Abstract the tools
I would take the text, line, circle, polygon, node tool, select tool and all the others and get them into a library.

I would use them in different combinations from my own Python apps. I'd like to write a little app to do animations - with simple drawings on a canvas:

from Inkscape import canvas
from Inkscape.tools import pen, text, circle

You get the idea.



Professional interface
Nodes. Oh those huge nodes. Those nodes that are like herding elephants in a small room. I would add a section to the settings where I could set the size (and colour) of my nodes. I would make them 1 pixel red dots. Really.

I'd switch off tool icons on the cursor. I'd add more options for what the cursor can look like and what angle it takes to the point it operates from.

Switching from a tool to the last one used. I'd nominate space-bar for the job, but add a general keyboard shortcuts editor too. I often want to jump between select and node. Between the gradient tool and the dropper.

Drag to/from the layer dialog, or some kind of send-to context menu. Moving stuff between layers is really tricky at the moment.

Make the gui 'go away'
I don't need bars that cross the entire canvas just to hold file, edit, etc.

I don't need all the tools when I am drawing something.

I don't want that huge fill dialog as a window, I don't like it as a panel either. I want a little on-canvas colour widget (mypaint style) that comes at a keystroke and goes in a heartbeat.

In fact, nothing but the canvas.

Nothing. But. The. Canvas.

Imagine the tools as little icons that float in a cloud around the cursor when you call them up. Recently used tools are closer to the cursor.

Imagine the Path menu as a cloud of buttons that come and go - on the canvas, at the cursor.

Colour palette, same. Path effects editor, gradient editor. All citizens of the canvas.

General notes
Random thoughts as they happened:

Gimp files
Any reason why .xcf files can't be used directly in Inkscape? If there are layers, then bring them in as a group.

Small things
Dropping images onto the canvas — auto link (or embed as per settings). Don't ask me every time.

Importing PDF — don't ask me stuff every time. Let me open all the pages in a PDF. Let me control it from settings.

Multiple pages
I would *so* have them. No quibbles.
If I had managed to do that blue-sky DRY thing I spoke of earlier, then each page could be a little universe of its own svg tree.

Output a multiple page PDF document? For sure.

Clips
It seems to happen quite often. I find I have clipped some drawing which has other clips within it. I was aiming at a design, not an efficiency.

Now I sit with this monster that can barely be dragged around because it takes so long to draw.

I want to 'simplify' it. I want to 'collapse' it.

I want to tell Inkscape: "Look, you know this monster? Please start at the bottom and give me a bunch of paths that have no clips anymore. Anything that was invisible should simply not be there. Cut paths, do what you have to, yeah?"

I'd also employ some way to make this 'live' — your original object with all the clips (the monster) would remain so. It would always be editable. Any users downstream that want the 'simple' version are going through the algorithm on file open to produce the collapsed version for actual work.

Gradients
Along a path.
Along a 'web' - I imagine drawing a radiating set of paths and the gradient would be like a spider weaving a web along those paths - a kind of radial fill, but one that can better fit any shape I want to fill.

Blurs
In similar fashion to gradients. I'd like to control the blur all around the edge of a shape. Some edges I want sharp, others less and the far side I want very blurred.

I see a kind of 'blur control path' that starts with my original path (or lets me draw one) and then I am pushing/pulling the nodes to control the blur.

Display modes
I'd give each object the ability to display in a different way. Some stuff can be outlines. Some stuff a simple fill. Other stuff full quality effects.

I would also add some kind of mipmap system so that complex objects can be quickly represented in colour but without the lag of vectors - temporarily of course.

Rotation
Occasionally I want the bounding-box to rotate. Sometimes not.
Sometimes I want to skew the rotated group and then it's too late — the skewing is not acting local to the group.

I get confused by these things — global, local et al. I just know I want an effect and that the bounding box is not on my side.

Canvas
If you are like me, your hand and arm are kind of wired to draw stuff from a certain angle. I often want to rotate the canvas to other angles so I can draw naturally.

Bookmarks
At any point in my flow, I'd like to make a bookmark. This would capture the interface, the display modes, the angle of the canvas — everything — so that I can then switch into different work-modes.

Here I am working on the top-left, miles away from the page. My clone-zone. I am zoomed-in just so. I have the mode set to outline. I want to flip back and forth to the page, but in another zoom and another mode. Bookmarks.

Multiple views
Like Blender, I would give Inkscape the multiple, non-overlapping windows treatment. Open a split and work on a clone and its, uh, clone-target at once.

3D
The current 3d box tool is a good start. The fact that I simply cannot use it is a little sad, but it shows that faux 3D is certainly on the cards.

I'd allow extruding and box-modelling on the canvas. This would be controlled by a kind of stack (input/output scripts again) so that one is always editing the path (or simplest element) and the 3D is constructed afterwards.

I'd like to implement a kind of face-drawing system. You have a plane (a face) and you can draw into that. Then you can rotate that entire face/plane/group/clone in 3 dimensions.

Entire groups could exist in 3d. Things like perspective and rotation would take on a whole new meaning.

3D text goes without saying.

It's not that the SVG would have to be 3D, but that the output of Inkscape would generate SVG such that it looks like a normal 2D svg. I'd make Inkscape more general.

Colour/Style
I'd like to change all the red to dark-red in this file. Sure, I can text-edit (sed, ftw!) the svg, but what would be much better is some kind of variable colour and style override.

I want to 'edit' a 'colour' and have all users of that colour now reflect the new colour. So, 'colours' are merely slots which can change. Same for styles — outline, thickness etc.

The master slot tables would be libraries higher-up in the chain. If I wanted to change the colour of a logo in dozens of other files, I would change the slot in the library.

Font manager
I know that currently GTK and other voodoo requires a restart of apps before new fonts are recognized. That aside — it would rule if I could have something like Fonty Python (a font manager) built-into Inkscape.

I would like to associate 'projects' and individual svg files with a certain set of fonts that then get installed as-per.


Fin
You know, I really envisage an entire Operating System interface that is basically Ink-like in nature.

Why have the GIMP as a different app? Why MyPaint? Why can't I simply edit bitmaps (and all their fancy layers) right here on my canvas?

Why not just have all the apps living on a huge canvas with visual pipes between them all hooked-into in/out scripting stacks?

Why not have different 'worlds' where other canvases exist (like virtual desktops kinda) where you can pipe between all the worlds?

Why can't my server (out on distant Linode) not be a canvas of its own here on my machine? I could specify connections from my local canvas to it and when I output a bunch of images here -- it rsyncs in the background to the linode?

Why can't
I see my brother-in-law's FTP server as a plane in my drawing, so that I am updating objects there and pulling them down?

Why can't I route the ouptut of, say, Calibre or Lyx into a stack of scripts to automate all kinds of document processing - between my local and remote canvases?

I dunno. I guess I see something a lot more dynamic and simpler than a zoo of little boxes that never speak to one-another unless you happen to copy and paste — and that's hit and miss too — or hit the dirty bash scripting zone and lose hair.

I see a world of pipes negotiated by scripts such that the fractured experience we currently have is merged into a seamless flow — so that apps become tools that are more powerful than their parts.

Thanks for reading. Resist the urge to stick pitchforks into me, I am delicate :)

August 2011
\d

Wednesday, 17 February 2010

Clutter Mouse Events

Well, it has been a fairly good week in-between the segfaults and other scary things. I have a basic system to tame mouse events so that I can respond to 'click' and 'double-click' and stuff like 'dragging' and 'drag-over'.
This is all written in Python. The licence is GPLv3.
Save them both (states.py in a directory called helpers), then run two_squares.py -- you should be able to drag them around (with different mouse buttons. They dump info to the console.)
Module 1: helpers.states.py
import clutter
import gobject

class DragEnvelope(object):
 """Helps deal with drags."""
 def __init__(self, obj, event):
  self.event = event
  self.diffx = event.x - obj.get_x()
  self.diffy = event.y - obj.get_y()
 def calcPos(self, event):
  """Where to draw the Actor relative to cursor within it."""
  self.newx = event.x - self.diffx
  self.newy = event.y - self.diffy

class Mouserizer(object):
 ## User vars to form ui mask
 HITABLE = 1<<0
 DRAGABLE = 1<<1
 CLICKABLE = 1<<2
 SCROLLABLE = 1<<3
 PICK_UNDER = 1<<4

 LEFT = 1<<0
 MIDDLE = 1<<1
 RIGHT = 1<<2

 ## Private class vars
 __FLAG_ENTERED = 1<<0 # Not used in this version.
 __FLAG_PRESSING = 1<<1
 __FLAG_RELEASING = 1<<2
 __FLAG_MOVING = 1<<3
 __FLAG_DRAGGING = 1<<4

 __PATTERN_CLICK =  __FLAG_PRESSING | __FLAG_RELEASING
 __PATTERN_DRAG_START = __FLAG_PRESSING| __FLAG_MOVING
 __PATTERN_DRAG = __PATTERN_DRAG_START | __FLAG_DRAGGING
 __PATTERN_DROP =  __FLAG_PRESSING| __FLAG_DRAGGING | __FLAG_RELEASING

 YES_CONTINUE_EMITTING = False
 NO_STOP_EMITTING = True
 
 __clutter_mouse_event_types=[
   clutter.MOTION,
   clutter.ENTER,
   clutter.LEAVE,
   clutter.BUTTON_PRESS,
   clutter.BUTTON_RELEASE,
   clutter.SCROLL
   ]

 def __init__(self, ui=None, buttons=None):
  if ui is None: 
   return # Not going to watch for any mouse events, so bug out.
  self.buttons = buttons
  
  ## If we want HIT kind of events, then just connect the usual suspects.
  if (ui & Mouserizer.HITABLE) !=0: # test bit
   self.connect('enter-event', self.on_enter_event)
   self.connect('leave-event', self.on_leave_event)

  self.__PICK_UNDER = False
  if (ui & Mouserizer.PICK_UNDER) !=0:
   self.__PICK_UNDER = True

  ## Keep a record of what we are going to listen to.
  self.ui = ui

  ## Enable the actor (self) to receive events.
  self.set_reactive(True)

  ## This is the state of our situation -- it will be masked bitwise.
  self.ui_state = 0
  
  ## Route all events (for this Actor) through one function:
  self.connect('captured-event', self.event_central)

 def event_central(self, obj, event):
  ## This routine runs many times. Once for every kind
  ## of event the actor is getting.

  ## filter out only the mouse events.
  if event.type not in Mouserizer.__clutter_mouse_event_types: 
    return Mouserizer.YES_CONTINUE_EMITTING

  ## filter out buttons we are NOT going to deal with
  if hasattr(event, "button"):
   b = 1 << (event.button - 1)
   #print bin(b)," vs ", bin(self.buttons)
   if not(b & self.buttons !=0 ):
    return Mouserizer.NO_STOP_EMITTING # is this wise?

  ## event_central ONLY runs when cursor is
  ## over the actor -- thus ENTER is implied.
  
  ## Make a note of PRESS/RELEASE
  if event.type==clutter.BUTTON_PRESS:
   self.ui_state = self.ui_state | Mouserizer.__FLAG_PRESSING # set bit
  if event.type==clutter.BUTTON_RELEASE:
   self.ui_state = self.ui_state | Mouserizer.__FLAG_RELEASING # set bit

  ## Make a note of MOTION
  ## First, clear it.
  self.ui_state = self.ui_state & ~Mouserizer.__FLAG_MOVING # clear bit
  if event.type==clutter.MOTION:
   self.ui_state = self.ui_state | Mouserizer.__FLAG_MOVING # set bit

  ## Now, what kinds of stuff is this actor interested in?

  ## DO META EVENTS - "More than" events. e.g. 'Click' is press, then release.
  if (self.ui & Mouserizer.CLICKABLE) != 0: # test bit
   if self.ui_state == Mouserizer.__PATTERN_CLICK:
    if event.click_count > 1:
     self.emit('double-click', event)
    else:
     ## A single click is fired just before double-click...!
     self.emit('single-click', event)

  if (self.ui & Mouserizer.DRAGABLE) !=0: # test bit
   if self.ui_state == Mouserizer.__PATTERN_DRAG_START:
    self.ui_state=self.ui_state | Mouserizer.__FLAG_DRAGGING # set bit
    self.draglet = DragEnvelope( self, event )
    ## Phew! I thought I was fcuked! In order to get dragging to 
    ## work when the pointer is NOT ON the Actor, I had to revert
    ## to grab_pointer* -- and that needs connecting. I connected the
    ## two appropriate event to *this* same function! And it works :D
    ##
    ## * grab_pointer causes the entire window (stage?) to focus on the
    ##   Actor passed -- so I get all motion and release events even where
    ##   the Actor aint.
    ##   ! Not sure what kind of recursive issues this may throw at me :(
    clutter.grab_pointer( self )
    self.connect('motion-event', self.event_central)
    self.connect('button-release-event', self.event_central)
    self.emit('drag-start', self.draglet )
   elif self.ui_state == Mouserizer.__PATTERN_DRAG:
    self.draglet.calcPos( event ) # A 'draglet' is a little wrapper containing the event and some tricks.
    ## Who is under me? Only do if PICK_UNDER flag is set.
    if self.__PICK_UNDER:
     self.hide()
     a = self.stage.get_actor_at_pos(clutter.PICK_REACTIVE, int(event.x),int(event.y))
     self.show()    
     ## a is!
     ## Only emit if a has a drag-over signal:
     if gobject.signal_lookup('drag-over', a ):
      print a, " under me"
      a.emit('drag-over', self.draglet)
    self.emit('dragging', self.draglet)
    
   elif self.ui_state == Mouserizer.__PATTERN_DROP:
    self.draglet.calcPos( event )
    self.ui_state= self.ui_state & ~Mouserizer.__FLAG_DRAGGING # clear bit
    clutter.ungrab_pointer()
    self.emit("drop", self.draglet)
    del(self.draglet)
   
  ## META EVENTS are done.

  ## Flip opposites off.
  if event.type==clutter.BUTTON_PRESS:
   self.ui_state = self.ui_state & ~Mouserizer.__FLAG_RELEASING # clear bit
  if event.type==clutter.BUTTON_RELEASE:
   self.ui_state = self.ui_state & ~Mouserizer.__FLAG_PRESSING # clear bit
   self.ui_state = self.ui_state & ~Mouserizer.__FLAG_RELEASING # clear bit
   self.ui_state = self.ui_state & ~Mouserizer.__FLAG_DRAGGING # clear bit

  return Mouserizer.YES_CONTINUE_EMITTING

(Always with the rectangles eh? :D)
import clutter
from helpers.states import Mouserizer as M
import gobject

meta_signals = {
 'single-click' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,) ),
 'double-click' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,) ),
 'drag-start' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,) ),
 'dragging' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,) ),
 'drop' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,) ),
 'drag-over' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,) ),
}

class DragHandle(clutter.Rectangle, M):
 ## Make this class a GObject type
 __gtype_name__ = 'DragHandle'
 ## Specify which signals it can receieve.
 __gsignals__ =  meta_signals

 def __init__(self, stage, col, ui=None, buttons=None):
  """
  You don't have to connect: enter-event, leave-event, single-click or double-click
  """
  if ui is None:
   ui = M.HITABLE
  if buttons is None:
   buttons = M.LEFT
  clutter.Actor.__init__(self)
  M.__init__(self, ui=ui, buttons=buttons)
  self.stage = stage
  
  self.set_color( clutter.color_from_string(col))
  self.set_size(50,50)

 def do_drag_over(self, draglet):
  print "DRAG OVER ", draglet
  return M.NO_STOP_EMITTING 

 def do_single_click(self, *args):
  print "HANDLER CLICK"
  return M.NO_STOP_EMITTING

 def do_double_click(self, *args):
  print "HANDLER DCLICK"
  clutter.main_quit()

 def do_drag_start(self, draglet):
  print "DRAG STARTS:"
  return True

 def do_dragging(self, draglet):
  self.set_position(draglet.newx, draglet.newy)
  return True

 def do_drop(self, *args):
  print "DROP!"
  return True

 def on_enter_event(self, obj, evt):
  print "ENTERING ",obj
  return True

 def on_leave_event(self,obj,evt):
  print "LEAVING ", obj
  return True
 
if __name__ == '__main__':
 stage = clutter.Stage()
 stage.set_size(640, 480)
 stage.set_color(clutter.color_from_string('Black'))

 stage.connect('destroy', clutter.main_quit)

 test=DragHandle( stage, "Green",  ui=M.HITABLE | M.CLICKABLE | M.DRAGABLE, buttons= M.RIGHT )
 stage.add(test)
 test.set_position(100,100)
 test.show()

 test2=DragHandle(stage, "Red",  ui=M.HITABLE | M.DRAGABLE | M.PICK_UNDER , buttons= M.LEFT )
 stage.add(test2)
 test2.show()
 
 stage.show()
 clutter.main()

Well, I hope that helps someone.
\d

Tuesday, 9 February 2010

PyClutter recipes.

Here are a few scripts using pyClutter 1.0 that I have recently finished. They are only for demonstrating some of the abilities of Clutter -- which I must say is just awesome!

Clipping actors (and groups of groups of... etc.) into a path shape
So, you have a shape and you want to force all the children in that Group (groups are Actors) to show within that path? Here you go:
import clutter
from clutter import cogl

"""
pyClutter 1.0
Demonstration of how to clip groups to a path.
"""

zoom = 1
def zoomify(obj,evt):
global zoom
if evt.direction == clutter.SCROLL_UP:
zoom += .1
else:
zoom -= .1
# When the zoom gets small, the cogl clip goes all wonky...
obj.set_scale(zoom, zoom)

class ClipGroup(clutter.Group):
""" Custom Group to perform clipping from a path."""
# Vital line.
# Registers the specified Python class as a PyGTK type.
# Also enables the do_paint method. I am not sure why...
__gtype_name__ = 'ClipGroup'

def __init__(self,w,h):
clutter.Group.__init__(self)#,*args)
self.width, self.height = w, h

def do_paint(self):
# Draw a triangle.
cogl.path_move_to(self.width / 2, 0)
cogl.path_line_to(self.width, self.height)
cogl.path_line_to(0, self.height)
cogl.path_line_to(self.width / 2, 0)
cogl.path_close()
# Start the clip
cogl.clip_push_from_path()

# errr.. the idea is to have the other stuff in the group
# get painted -- I assume Group knows what to do.
clutter.Group.do_paint(self)

# Finish the clip
cogl.clip_pop()

def main():
stage = clutter.Stage()
stage.set_size(500, 500)
stage.set_color(clutter.color_from_string("#000"))

rect_red, rect_green, rect_blue, rect_bounce = \
clutter.Rectangle(), clutter.Rectangle(), clutter.Rectangle(), clutter.Rectangle()

# We make two of our special groups.
clipped_group = ClipGroup(300,300)
inner_group = ClipGroup(100,100)

rect_red.set_position(0, 0)
rect_red.set_size(550, 550) #Big to be backdrop.
rect_red.set_color(clutter.color_from_string("#FF0000FF"))

rect_green.set_position(120, 10)
rect_green.set_size(50, 50)
rect_green.set_color(clutter.color_from_string("#00FF0090"))

rect_bounce.set_position(0,0)
rect_bounce.set_size(500,20)
rect_bounce.set_color(clutter.color_from_string("#AABBCCFF"))

clipped_group.add(rect_red, rect_green)
clipped_group.add(inner_group) # Add entire inner_group into clipped_group

rect_blue.set_color(clutter.color_from_string("#0000FF90"))
rect_blue.set_size(50, 50)
rect_blue.set_position(50,50) #relative to inner_group...
inner_group.add(rect_blue) # even though this comes after set_position...

clipped_group.set_position(100, 100)
inner_group.set_position(100,50)

stage.add(rect_bounce)
stage.add(clipped_group)

# Make rect_blue move around somewhat.
path = clutter.Path('M 0 0 L 40 0 L 40 40 L 0 40 Z')
timeline = clutter.Timeline(4000)
timeline.set_loop(True)
alpha = clutter.Alpha(timeline,clutter.EASE_OUT_SINE)
p_behaviour = clutter.BehaviourPath(alpha, path)
p_behaviour.apply(rect_blue)
timeline.start()

# Make rect_bounce go up and down
animation = rect_bounce.animate(clutter.EASE_IN_OUT_BOUNCE, 2000, "y", 500)
animation.set_loop(True)

# Start the main clipped_group rotating around the Y axis. For giggles.
timeline = clutter.Timeline(15000)
timeline.set_loop(True)
alpha = clutter.Alpha(timeline, clutter.LINEAR)
r_behave = clutter.BehaviourRotate(clutter.Y_AXIS, 0.0, 360.0, alpha=alpha)
r_behave.set_center(150, 0, 0)
r_behave.apply(clipped_group)
timeline.start()

stage.show_all()

stage.connect('key-press-event', clutter.main_quit)
stage.connect('destroy', clutter.main_quit)
stage.connect('scroll-event', zoomify)

clutter.main()

if __name__ == '__main__':
main()
Adventures in Time(line)
I wanted to see for myself how Timelines could be controlled from a Score. This is very cool. Here, three rectangles fire in sequence given markers in their timelines. It sounds complicated, but the code should be easy to follow:
import clutter
from clutter import cogl

def main():
stage = clutter.Stage()
stage.set_size(500, 500)
stage.set_color(clutter.color_from_string("#000"))

rect_red, rect_green, rect_blue = \
clutter.Rectangle(), clutter.Rectangle(), clutter.Rectangle()

rect_red.set_position(30, 30)
rect_red.set_size(20, 20)
rect_red.set_color(clutter.color_from_string("#FF0000FF"))

rect_green.set_position(100, 100)
rect_green.set_size(50, 50)
rect_green.set_color(clutter.color_from_string("#00FF0090"))

rect_blue.set_color(clutter.color_from_string("#0000FF90"))
rect_blue.set_size(80, 80)
rect_blue.set_position(200,200)

stage.add(rect_red, rect_green, rect_blue)

t_red=clutter.Timeline(10000)
t_green=clutter.Timeline(5000)
t_blue=clutter.Timeline(5000)

a_blue=clutter.Alpha(t_blue,clutter.EASE_OUT_SINE)
a_green=clutter.Alpha(t_green,clutter.EASE_OUT_SINE)
a_red=clutter.Alpha(t_red,clutter.EASE_OUT_SINE)

b_red = clutter.BehaviourRotate(clutter.Z_AXIS, 0.0, 360.0, alpha=a_red)
b_green = clutter.BehaviourRotate(clutter.Z_AXIS, 0.0, 360.0, alpha=a_green)
b_blue = clutter.BehaviourRotate(clutter.Z_AXIS, 0.0, 360.0, alpha=a_blue)

b_blue.apply(rect_blue)
b_green.apply(rect_green)
b_red.apply(rect_red)

s=clutter.Score()

t_red.set_loop(True)
s.append(t_red)
t_red.add_marker_at_time("go_green",2000)
s.append_at_marker(t_red,"go_green",t_green)
t_green.add_marker_at_time("go_blue",5000)
s.append_at_marker(t_green,"go_blue", t_blue)

stage.show_all()
s.start()

stage.connect('key-press-event', clutter.main_quit)
stage.connect('destroy', clutter.main_quit)

clutter.main()

if __name__ == '__main__':
main()

Sunday, 4 October 2009

Fonty 0.4.3 Test it please!

I have worked hard for about two weeks now and I am quite happy with things. There are still problems (keyboard things mostly) but it's quite usable.

Get it here:
svn co svn://svn.sv.gnu.org/fontypython/trunk


Screenshot - a little out of date, but shows the general idea.

Stuff I done did
  1. Fonts that have weird spacing to the left and top are now cropped to fit. This is optional (see Settings) because it adds work per font and is slightly slower.
  2. After much experimentation I finally got Fonty to spawn either gucharmap or kfontview for any given font — and you can open multiple windows. There is a new button on each font item (bottom left). Only valid fonts can be viewed.
    I chose those two because:
    • Doing a character map of my own is just insane.
    • I searched around in the Debian repos and online for other viewers and those are the two that seem prevalent. If there are others, let me know so I can attempt to wire them in.
    On startup, Fonty will look for them and only offer what is installed (see
    Settings).
  3. I flipped the tabs for Source to the bottom. My thinking is that most work happens at the bottom of the app: paging, filtering and when you scroll down in the view, you end-up at the bottom anyway. So, the tabs are closer now. This might suck...
  4. I added some quick filter toggle buttons for Bold, Italic and
    Regular/Normal.
  5. I did a lot of work on the appearance of the font view. There are lots of tweaks and icons/colours to represent bad fonts.
  6. When more than 20 fonts are in any page a wx.Yield() is done so that the entire GUI does not lock-up.
  7. Settings got a notebook (tabs) to better control the new options.
  8. The help file has been up-upgraded.
  9. Many strings have been altered (pog to Pog, for example) and there are some new ones too. Translators alert!
  10. The ability to zip a Pog — was added in 0.4.2 but it's still shiny and new.
  11. Also since 0.4.2 — Font counting. To prevent 'overlaps' when Pogs share common font files and you don't want them uninstalling.
  12. The man page is updated. man -l fontypython.1 will show it.
There are probably more things I have forgotten now. The code has been factored and cleaned (a lot) but there are still areas that are a mess.

Right, I believe that barring any big errors this will be the latest Fonty for some time. I have to move-on to other projects now.

\d

Friday, 2 October 2009

gucharmap command line

Aaaargh!
I spent an hour on google and on the command-line trying to start gucharmap with a given font name at the command line. No dice. (And don't speak to me about man pages....)

It took the source code for "Font Manager" (A rival to Fonty ;) ) and some small hacking to get it to reveal the secret sauce. Here, then, is the way to do it:

gucharmap --font="SOME DAMN FONT NAME, 32"

The "SOME DAMN FONT NAME" can be obtained by:
fc-list : family | grep "SOME"
(IOW: You grep through the results with some small snippet of the name you already know, or just scroll through the output.)

The comma is the secret. The number after that is the point size.

Sorted,
\d

Tuesday, 29 September 2009

Fonty 0.4.3 cooking in SVN

Fonts sometimes render in odd positions - seen from a top-left point of view. (I have no skill or experience in right-to-left languages, any help would be appreciated!).

To correct this random affliction, I have had to scan each font image and calculate the x,y of the first actual data therein (anything that is not alpha zero). This works quite well and fonts now draw on a sharp left-top edge, however it is a little slow. I have employed some caching, but resist the idea of storing thousands of bitmaps in a directory for "quick access". That just strikes me as crazy. I may change my mind one day :)

To offset the slowness, there is a new option in the settings dialogue that lets users disable the adjustment; so, it's fair all-round.

This code has been written with an eye on creating a new dialogue that will be a character map of a given font — I will need a way to isolate a single character (or is the correct world glyph?) and control its top-left coordinate — therefore the code is being abstracted to allow a new kind of 'fitmap' (font bitmap) for that purpose.

Version 0.4.3 is underway on the SVN.
\d

Sunday, 27 September 2009

Fonty News

September has been a busy month for Fonty. I have a new version ready to go in the svn. If anyone wants to give it a test run, please head over to https://savannah.nongnu.org/projects/fontypython/ and get it while it's hot.

I will be making a tarball in the days to come, so any bugs before then would be nice to know about.

New features:
  • You can zip Pogs into zip files. When you need someone else to have the fonts you used, this is a quick way to do it.
  • Installed fonts are now counted and should the same fonts be referred-to in many Pogs, the font will not be uninstalled until it's absolutely not needed. This fixes an irksome bug with a long pedigree.
  • Lots of little visual improvements have been made. I hope they look as good on other systems and themes...
  • The help file was overhauled.

Right, I hope you like. Send me screen shots if you have the time.
\d

Tuesday, 18 August 2009

Flowcharting Faith

Flowcharting Faith
Over on my personal site I have a new page:

"The idea is to test your beliefs against the flow of the diagram.
Take some proposition and start anywhere (big orange headings are best). Then honestly follow the flow and observe what happens. If you are honest with yourself (and value truth) you should find your belief getting more and more honed and simple — eventually you should end-up with a very small set of things that you have no reason to believe — and a large set of things that you have good reason to."

Might help someone out of Woo, or just confuse the world a little more ;)

\d

Tuesday, 19 May 2009

Things gets a tut.

I just finished writing a short tutorial for the Things API. You can get it on my wiki:
otherwise.relics.co.za or from the CVS on the official website.

The tarball contains a PDF, an SVG file and a bunch of python files. You will still need to fetch the actual API and make a link to it where you want to run the tutorial samples.

It should all make a kind of sense. If it does not, then you need to panic! Run around yelling! Or something.

Have fun with Things.

\d

Saturday, 9 May 2009

Things! An easy animation API for Python.

Announcing "Things" - Vector animation in Python
I just got my Savannah project approved and have uploaded the code to CVS. I have never used a cvs before, so I don't really know what I am doing.

You can visit the project here:
http://savannah.nongnu.org/projects/things/

"Things" is a way to make quick visual metaphors and animate them. Things can contain other things -- in this way you can have one Thing moving left-right and then put that into another Thing moving up-down and you'd get an animation doing both.

There's more to it than that; you can tell individual things when to stop and start; what to do and when to do it; how to move and so on.

There is a primitive event-system going; so you can produce buttons and hit areas and so forth.

Inkscape and SVGs
Things was written with Inkscape in mind. You draw your graphics there, give stuff id's and then in your Things Python code, you can refer to those ids and use the SVG directly. The whole idea was to use Inkscape as the "IDE" and Things as the animation engine.

There are ways to create:
1. "Sprites" - Single frame drawings.
2. "Loops" - Drawings that will change over time -- like a walk cycle.
3. "Paths" - Simple paths for things like line drawing or following.
4. "Masks" - Also simple paths, but intended for clipping and hit-detection etc.

Each of these is drawn in a layer within Inkscape (and there are a few simple rules to follow) after which you simply pull them out in Python and use 'em!

Help code! Take it away!
So, come on over and give it a whirl. Perhaps you can improve it and code it into some fast C/assembler. It sure needs more love and skill than I can bring :)

\d

Sunday, 1 February 2009

My new website!

I have put together a Django-powered website of my own -- come visit it at otherwise.relics.co.za -- I have a bunch of comics, tutorials and some writing there.

\d

Friday, 25 July 2008

Email attachment limits

Why is this not a done deed?

How many of you out there know someone who simply wants to send pictures by email but has no clue about how big the darn things are? Attach, attach, clickety-click and then off goes a 10mb email... Man this is crazytown. Why is there no thought given to this for email clients like kmail and evolution?

This is a cry-out to the boffins to please (pretty, pretty please) create some kind of layer between the images attached and the email sent that will:
  1. Compress them in scale and size to a target size (see Phatch if you think this can't be done!)
  2. Limit the total Kb size of all the attachments so that it tells Aunt Sue, "Please remove a few pictures because this email is too big to send."
This kind of solution should involve both routes of the attachment-equation: Sending-to-email from a file browser, and attaching from a new mail message.

I have crafted two (horrible) solutions to the second route, one for kmail and another for evolution. I can't get my brain around how to do this from the first route -- the answer for evolution is some kind of plugin, but that's a step too far for me right now.

My solutions for kmail and evolution can be a subject for another blog if anyone is interested...

\d

Otherwise Poetry

Me again,
I have delayed putting a link to my little book of poems (available on Lulu.com) for many, many months now; feeling a little shy about such marketing, but today I felt a rush of optimism and I just did it.

So, this is a quick post to mention it. I hope someone out there enjoys a few of the poems in my book, but I have never had any feedback so I really don't know if they are any good. The hardcover is priced as low as I could go and the ebook is free.

There are a few new ones towards the end that came from my admiration of Prof. Dawkins' work and what I have learned from him and others (a nod to James Randi) about reality and how deluded we humans really are.

Well, there, it's all said now. Gulp!

Note: I created the entire book (printed version and ebook) using 100% free software on Kubuntu. I used Lyx to do the writing (and produce the PDF and the PS) and Inkscape & Scribus to hack the cover. Thank the FSM for such capable and fantastic software. Oh, I used Fonty Python to manage my fonts too ;) (But the quality of that software is up for debate.)

\d

Notes from July

Greetings,
I have one more operation to go (I hope that's all) and I should be all repaired. Man I wish I was as simple as a motherboard and I could get opened-up with a screwdriver and have bits swapped-out.

Otherwise July has been good to me; we have moved into our house and I have a reliable Internet connection (the most important thing :D ) and I am starting to edge towards coding again.

Plans:
  1. Learn Django!
  2. Finish my Python & Cairo animation app. This is very cool, but I need time to polish it.
  3. Get a web-server of my own (perhaps Linode) and muck about with some web apps.
  4. Polish Fonty Python some more (Nautilus & Konqueror integration for one).
So, lots to do with little time!

Ciao world,
\d

Adding folders of fonts to a Pog

Hello world,
I was asked in an email the other day if I could add a function to gather all the fonts in a given folder into a Pog. While this is a cool idea for a future release, it's possible right now from the command-line. Lemme show you how.

Open a console/terminal (like konsole or gnome-terminal). I will use square brackets [ ] to surround the command you can type but don't type the brackets. I will use ~/somefonts as the example folder you want to use.

The idea is to build a pog, which is a plain text file containing full paths to each font file. Okay, let's start:
  1. Use [cd ~/.fontypython] to go where the pogs live.
  2. [echo "not installed" > newpog.pog] You can use any name you want. Just make *sure* you don't overwrite some other pog!
  3. [find ~/somefonts -iname "*.ttf" >> newpog.pog] That will go and find the ttfs (you can change that to say "*.otf" but Type1 fonts might be more tricky) and then it will append them to the 'newpog.pog' file you just started.
  4. If you want to add other fonts from other folders, just use the same find command, but change the path. Make sure you use 2 ">" symbols (>>) because that means append-to.
Right, that's it. Now open fonty and go look at newpog:
[fontypython newpog]

You can also install those fonts with:
[fontypython -i newpogs]

Have a look at [fontypython --help]

Simple, I hope ;)
\d

Saturday, 14 June 2008

Damn the body

May has been, and June is shaping-up to be, a month of being sick and in hospital. Man I hate hospitals! They remove your spirit and leave you at your most basic: a body in a bed with nothing but hours between you and sanity.

I wonder when I will get back into a productive phase again -- sometimes I think it will be never.

I be blue.

\d

Monday, 14 April 2008

I been tired

Ah well, no code and no updates. Hoping to get back in the swing of things in a few months, life's too strange right now.

\d

Wednesday, 30 January 2008

Fui update

I made a few tweaks and there is the slim chance that fui might become a Debian unstable package in the future.

Latest source here.

Would appreciate any feedback about it's use in the digital wild.

\d

Friday, 25 January 2008

FUI - a select copy/move command line tool

Have you ever wanted to select various files from a console: this one and those two and not this one? Have you ever wanted to move over to another console (or tab) and go to some directory and then copy all those files in one go?

If so, then fui might be for you. The name means 'fake user interface'. Yeah, well, I had to call it something.

The idea was to create some of the elegance of the select, click, cut/copy and paste paradigm in common use in Gui's. With fui you can do this kind of thing:

fui `ls *.jp` blob blimp sandwich
(That will select all those files.)

fui --exclude bobo.jpg muffin.jpg
(I didn't want those two.)

cd /some/other/place
fui --copy
(That will copy all those files to here.)

Simple and easy. You can move files (and directories) as well as copy them.

There's a --help option and it's localized too (but has no po/mo files yet.)

You can get it from the Cheese Shop.

\d

Wednesday, 23 January 2008

Fonty Python - 0.3.6 released

I fixed the bug mentioned in the last post - it was caused when a font rendered with zero width (I think because it could not draw any of the letters in the text string).

I also improved the Check Fonts dialogue - to give a little feedback.

Version 0.3.6 is on Savannah.

\d