changeset 8719:b87070d83277 octave-forge

geometry. Adding @svg
author jpicarbajal
date Tue, 01 Nov 2011 10:45:54 +0000
parents 4abbcafd2dcd
children f80846665d3b
files main/geometry/devel/@svg/getpath.m main/geometry/devel/@svg/loadpaths.m main/geometry/devel/@svg/svg.m main/geometry/inst/io/@svg/display.m main/geometry/inst/io/@svg/getpath.m main/geometry/inst/io/@svg/inkex.py main/geometry/inst/io/@svg/loadpaths.m main/geometry/inst/io/@svg/parsePath.py main/geometry/inst/io/@svg/plot.m main/geometry/inst/io/@svg/simplepath.py main/geometry/inst/io/@svg/subsref.m main/geometry/inst/io/@svg/svg.m
diffstat 12 files changed, 889 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/main/geometry/devel/@svg/getpath.m	Tue Nov 01 10:16:55 2011 +0000
+++ b/main/geometry/devel/@svg/getpath.m	Tue Nov 01 10:45:54 2011 +0000
@@ -45,11 +45,11 @@
   paths = [];
   if any (tf)
     stuff = {ids{tf}};
-    %{
+    
     for i = 1: numel(stuff)
       paths{i} = obj.Path.(ids{i}).data;
     endfor
-    %}
+    
     
     % Variation
 %    paths = cellfun(@(s) obj.Path.(s).data, stuff,'UniformOutput',false);
@@ -61,8 +61,8 @@
 %    paths = cellfun(@(s) getfield(obj.Path,s).data, stuff,'UniformOutput',false);    
 
     % Yet yet another
-    dummy = @(s) obj.Path.(s).data;
-    paths = cellfun(dummy, stuff,'UniformOutput',false);    
+%    dummy = @(s) obj.Path.(s).data;
+%    paths = cellfun(dummy, stuff,'UniformOutput',false);    
     
     if numel(paths) == 1
       paths = paths{1};
--- a/main/geometry/devel/@svg/loadpaths.m	Tue Nov 01 10:16:55 2011 +0000
+++ b/main/geometry/devel/@svg/loadpaths.m	Tue Nov 01 10:45:54 2011 +0000
@@ -93,32 +93,3 @@
     Paths.(svgpathid).data = pathdata;
   end
 endfunction
-
-%!test
-%! figure(1)
-%! hold on
-%! paths = getSVGPaths_py ('../drawing.svg');
-%!
-%! % Get path ids
-%! ids = fieldnames(paths);
-%! npath = numel(ids);
-%!
-%! t = linspace (0, 1, 64);
-%!
-%! for i = 1:npath
-%!    x = []; y = [];
-%!    data = paths.(ids(i)).data;
-%!
-%!    for j = 1:numel(data)
-%!     x = cat (2, x, polyval (data{j}(1,:),t));
-%!     y = cat (2, y, polyval (data{j}(2,:),t));
-%!    end
-%!
-%!    plot(x,y,'-');
-%! end
-%! axis ij
-%! if strcmpi(input('You should see drawing.svg [y/n] ','s'),'n')
-%!  error ("didn't get what was expected.");
-%! end
-%! close 
-
--- a/main/geometry/devel/@svg/svg.m	Tue Nov 01 10:16:55 2011 +0000
+++ b/main/geometry/devel/@svg/svg.m	Tue Nov 01 10:45:54 2011 +0000
@@ -56,14 +56,12 @@
 endfunction
 
 %!test
-%!  dc = svg('../inst/drawing5.svg');
-%!  dc.path
-%!  dc.Path
+%!  dc = svg('/home/juanpi/Resources/3rdPartyCode/octave-forge/main/geometry/inst/io/drawing5.svg');
 %!  dc.getpath()
 %!  dc.pathid
 %!  dc.getpath('path3756')
 %!   
-%!  dc = svg('../inst/drawing.svg');
+%!  dc = svg('/home/juanpi/Resources/3rdPartyCode/octave-forge/main/geometry/inst/io/drawing.svg');
 %!  ids = dc.pathid;
-%!  dc.path({ids{[1 3]}})
+%!  dc.getpath({ids{[1 3]}})
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/geometry/inst/io/@svg/display.m	Tue Nov 01 10:45:54 2011 +0000
@@ -0,0 +1,25 @@
+## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (c) 2011 Juan Pablo Carbajal <carbajal@ifi.uzh.ch>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+function display (obj)
+
+  fields = fieldnames (obj);
+  for i = 1 : numel(fields)
+    printf ("%s\n", fields{i});
+    obj.(fields{i})
+  end
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/geometry/inst/io/@svg/getpath.m	Tue Nov 01 10:45:54 2011 +0000
@@ -0,0 +1,73 @@
+## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (c) 2011 Juan Pablo Carbajal <carbajal@ifi.uzh.ch>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {} function_name ()
+## @end deftypefn
+
+function paths = getpath(obj, ids={})
+
+  if !isempty(ids)
+  
+    if iscell (ids) && iscell(ids{1}) % dealing with ids given as cell
+      ids = ids{1};
+
+      if !all ( cellfun (@ischar, ids) )
+       print_usage
+      end
+
+    elseif !all ( cellfun (@ischar, ids) )
+     print_usage
+    end
+    
+  else
+    paths = obj.Path;
+    return
+  end
+
+  tf = ismember (ids, fieldnames (obj.Path));
+
+  cellfun (@(s) printf("'%s' is not a valid path id.\n", s) , {ids{!tf}});
+
+  paths = [];
+  if any (tf)
+    stuff = {ids{tf}};
+    
+    for i = 1: numel(stuff)
+      paths{i} = obj.Path.(ids{i}).data;
+    endfor
+    
+    
+    % Variation
+%    paths = cellfun(@(s) obj.Path.(s).data, stuff,'UniformOutput',false);
+    
+    % Another variation
+%    paths = cellfun(@(s) getfield(obj,'Path').(s).data, stuff,'UniformOutput',false);
+    
+    % Yet another
+%    paths = cellfun(@(s) getfield(obj.Path,s).data, stuff,'UniformOutput',false);    
+
+    % Yet yet another
+%    dummy = @(s) obj.Path.(s).data;
+%    paths = cellfun(dummy, stuff,'UniformOutput',false);    
+    
+    if numel(paths) == 1
+      paths = paths{1};
+    end
+  end
+
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/geometry/inst/io/@svg/inkex.py	Tue Nov 01 10:45:54 2011 +0000
@@ -0,0 +1,235 @@
+#!/usr/bin/env python
+"""
+inkex.py
+A helper module for creating Inkscape extensions
+
+Copyright (C) 2005,2007 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+"""
+import sys, copy, optparse, random, re
+import gettext
+from math import *
+_ = gettext.gettext
+
+#a dictionary of all of the xmlns prefixes in a standard inkscape doc
+NSS = {
+u'sodipodi' :u'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',
+u'cc'       :u'http://creativecommons.org/ns#',
+u'ccOLD'    :u'http://web.resource.org/cc/',
+u'svg'      :u'http://www.w3.org/2000/svg',
+u'dc'       :u'http://purl.org/dc/elements/1.1/',
+u'rdf'      :u'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+u'inkscape' :u'http://www.inkscape.org/namespaces/inkscape',
+u'xlink'    :u'http://www.w3.org/1999/xlink',
+u'xml'      :u'http://www.w3.org/XML/1998/namespace'
+}
+
+#a dictionary of unit to user unit conversion factors
+uuconv = {'in':90.0, 'pt':1.25, 'px':1, 'mm':3.5433070866, 'cm':35.433070866, 'm':3543.3070866,
+          'km':3543307.0866, 'pc':15.0, 'yd':3240 , 'ft':1080}
+def unittouu(string):
+    '''Returns userunits given a string representation of units in another system'''
+    unit = re.compile('(%s)$' % '|'.join(uuconv.keys()))
+    param = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
+
+    p = param.match(string)
+    u = unit.search(string)    
+    if p:
+        retval = float(p.string[p.start():p.end()])
+    else:
+        retval = 0.0
+    if u:
+        try:
+            return retval * uuconv[u.string[u.start():u.end()]]
+        except KeyError:
+            pass
+    return retval
+
+def uutounit(val, unit):
+    return val/uuconv[unit]
+
+try:
+    from lxml import etree
+except:
+    sys.exit(_('The fantastic lxml wrapper for libxml2 is required by inkex.py and therefore this extension. Please download and install the latest version from http://cheeseshop.python.org/pypi/lxml/, or install it through your package manager by a command like: sudo apt-get install python-lxml'))
+
+def debug(what):
+    sys.stderr.write(str(what) + "\n")
+    return what
+
+def errormsg(msg):
+    """Intended for end-user-visible error messages.
+    
+       (Currently just writes to stderr with an appended newline, but could do
+       something better in future: e.g. could add markup to distinguish error
+       messages from status messages or debugging output.)
+      
+       Note that this should always be combined with translation:
+
+         import gettext
+         _ = gettext.gettext
+         ...
+         inkex.errormsg(_("This extension requires two selected paths."))
+    """
+    sys.stderr.write((unicode(msg) + "\n").encode("UTF-8"))
+
+def check_inkbool(option, opt, value):
+    if str(value).capitalize() == 'True':
+        return True
+    elif str(value).capitalize() == 'False':
+        return False
+    else:
+        raise optparse.OptionValueError("option %s: invalid inkbool value: %s" % (opt, value))
+
+def addNS(tag, ns=None):
+    val = tag
+    if ns!=None and len(ns)>0 and NSS.has_key(ns) and len(tag)>0 and tag[0]!='{':
+        val = "{%s}%s" % (NSS[ns], tag)
+    return val
+
+class InkOption(optparse.Option):
+    TYPES = optparse.Option.TYPES + ("inkbool",)
+    TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
+    TYPE_CHECKER["inkbool"] = check_inkbool
+
+class Effect:
+    """A class for creating Inkscape SVG Effects"""
+
+    def __init__(self, *args, **kwargs):
+        self.document=None
+        self.ctx=None
+        self.selected={}
+        self.doc_ids={}
+        self.options=None
+        self.args=None
+        self.OptionParser = optparse.OptionParser(usage="usage: %prog [options] SVGfile",option_class=InkOption)
+        self.OptionParser.add_option("--id",
+                        action="append", type="string", dest="ids", default=[], 
+                        help="id attribute of object to manipulate")
+
+    def effect(self):
+        pass
+
+    def getoptions(self,args=sys.argv[1:]):
+        """Collect command line arguments"""
+        self.options, self.args = self.OptionParser.parse_args(args)
+
+    def parse(self,file=None):
+        """Parse document in specified file or on stdin"""
+        try:
+            try:
+                stream = open(file,'r')
+            except:
+                stream = open(self.svg_file,'r')
+        except:
+            stream = sys.stdin
+        self.document = etree.parse(stream)
+        stream.close()
+
+    def getposinlayer(self):
+        #defaults
+        self.current_layer = self.document.getroot()
+        self.view_center = (0.0,0.0)
+
+        layerattr = self.document.xpath('//sodipodi:namedview/@inkscape:current-layer', namespaces=NSS)
+        if layerattr:
+            layername = layerattr[0]
+            layer = self.document.xpath('//svg:g[@id="%s"]' % layername, namespaces=NSS)
+            if layer:
+                self.current_layer = layer[0]
+
+        xattr = self.document.xpath('//sodipodi:namedview/@inkscape:cx', namespaces=NSS)
+        yattr = self.document.xpath('//sodipodi:namedview/@inkscape:cy', namespaces=NSS)
+        doc_height = unittouu(self.document.getroot().get('height'))
+        if xattr and yattr:
+            x = xattr[0]
+            y = yattr[0]
+            if x and y:
+                self.view_center = (float(x), doc_height - float(y)) # FIXME: y-coordinate flip, eliminate it when it's gone in Inkscape
+
+    def getselected(self):
+        """Collect selected nodes"""
+        for i in self.options.ids:
+            path = '//*[@id="%s"]' % i
+            for node in self.document.xpath(path, namespaces=NSS):
+                self.selected[i] = node
+
+    def getElementById(self, id):
+        path = '//*[@id="%s"]' % id
+        el_list = self.document.xpath(path, namespaces=NSS)
+        if el_list:
+          return el_list[0]
+        else:
+          return None
+
+    def getParentNode(self, node):
+        for parent in self.document.getiterator():
+            if node in parent.getchildren():
+                return parent
+                break
+
+
+    def getdocids(self):
+        docIdNodes = self.document.xpath('//@id', namespaces=NSS)
+        for m in docIdNodes:
+            self.doc_ids[m] = 1
+
+    def getNamedView(self):
+        return self.document.xpath('//sodipodi:namedview', namespaces=NSS)[0]
+
+    def createGuide(self, posX, posY, angle):
+        atts = {
+          'position': str(posX)+','+str(posY),
+          'orientation': str(sin(radians(angle)))+','+str(-cos(radians(angle)))
+          }
+        guide = etree.SubElement(
+                  self.getNamedView(),
+                  addNS('guide','sodipodi'), atts )
+        return guide
+
+    def output(self):
+        """Serialize document into XML on stdout"""
+        self.document.write(sys.stdout)
+
+    def affect(self, args=sys.argv[1:], output=True):
+        """Affect an SVG document with a callback effect"""
+        self.svg_file = args[-1]
+        self.getoptions(args)
+        self.parse()
+        self.getposinlayer()
+        self.getselected()
+        self.getdocids()
+        self.effect()
+        if output: self.output()
+
+    def uniqueId(self, old_id, make_new_id = True):
+        new_id = old_id
+        if make_new_id:
+            while new_id in self.doc_ids:
+                new_id += random.choice('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
+            self.doc_ids[new_id] = 1
+        return new_id
+
+    def xpathSingle(self, path):
+        try:
+            retval = self.document.xpath(path, namespaces=NSS)[0]
+        except:
+            errormsg(_("No matching node for expression: %s") % path)
+            retval = None
+        return retval
+            
+
+# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/geometry/inst/io/@svg/loadpaths.m	Tue Nov 01 10:45:54 2011 +0000
@@ -0,0 +1,95 @@
+%% Copyright (c) 2011 Juan Pablo Carbajal <carbajal@ifi.uzh.ch>
+%% 
+%%    This program is free software: you can redistribute it and/or modify
+%%    it under the terms of the GNU General Public License as published by
+%%    the Free Software Foundation, either version 3 of the License, or
+%%    any later version.
+%%
+%%    This program is distributed in the hope that it will be useful,
+%%    but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%%    GNU General Public License for more details.
+%%
+%%    You should have received a copy of the GNU General Public License
+%%    along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+function Paths = loadpaths (obj, svg, varargin)
+
+  here   = which ("@svg/loadpaths");
+  here   = fileparts (here);
+  script = fullfile (here, 'parsePath.py');
+
+  %% Call python script
+  if exist (svg,'file')
+  % read from file  
+    [st str]=system (sprintf ('python %s %s', script, svg));
+    
+  else
+  % inline SVG  
+    [st str]=system (sprintf ('python %s < %s', script, svg));
+  end
+  
+  %% Parse ouput
+  strpath = strsplit (str(1:end-1), '$', true);
+
+  npaths = numel (strpath);
+
+  %% Convert path data to polygons
+  for ip = 1:npaths
+
+    eval (strpath{ip});
+    %% FIXME: intialize struct with cell field
+    svgpath2.cmd = svgpath(1).cmd;
+    svgpath2.data = {svgpath.data};
+    
+    nD = length(svgpath2.cmd);
+    pathdata = cell (nD-1,1);
+    
+    point_end=[];
+    %% If the path is closed, last command is Z and we set initial point == final
+    if svgpath2.cmd(end) == 'Z'
+      nD -= 1;
+      point_end = svgpath2.data{1};
+      svgpath2.data(end) = [];
+    end
+    
+    %% Initial point
+    points(1,:) = svgpath2.data{1};
+    
+    for jp = 2:nD
+      switch svgpath2.cmd(jp)
+        case 'L'
+          %% Straigth segment to polygon
+          points(2,:) = svgpath2.data{jp};
+          pp = [(points(2,:)-points(1,:))' points(1,:)'];
+          clear points
+          points(1,:) = [polyval(pp(1,:),1) polyval(pp(2,:),1)];
+          
+        case 'C'
+          %% Cubic bezier to polygon
+          points(2:4,:) = reshape (svgpath2.data{jp}, 2, 3).';
+          pp = cbezier2poly (points);
+          clear points
+          points(1,:) = [polyval(pp(1,:),1) polyval(pp(2,:),1)];
+      end
+      
+      pathdata{jp-1} = pp;
+    end
+    
+    if ~isempty(point_end)
+      %% Straight segmet to close the path
+      points(2,:) = point_end;
+      pp = [(points(2,:)-points(1,:))' points(1,:)'];
+      
+      if all ( abs(pp(:,1)) < sqrt(eps) )
+      % Final point of last segment is already initial point
+        pathdata(end) = [];
+      else
+        pathdata{end} = pp;
+      end
+      
+    end
+    
+    Paths.(svgpathid).data = pathdata;
+  end
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/geometry/inst/io/@svg/parsePath.py	Tue Nov 01 10:45:54 2011 +0000
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+import inkex, simplepath
+import sys
+#import getopt
+
+def parsePaths (filen=None):
+
+  svg = inkex.Effect ()
+  svg.parse (filen)
+  
+  paths = svg.document.xpath ('//svg:path', namespaces=inkex.NSS)
+  for path in paths:
+    D = simplepath.parsePath (path.attrib['d'])
+    cmdlst = [];
+    parlst = [];
+    for cmd,params in D:
+      cmdlst.append(cmd)
+      parlst.append(params)
+    
+    print 'svgpath = struct("cmd","{0}","data",{{{1}}});' \
+        .format(''.join(cmdlst),str(parlst).replace('[[','[').replace(']]',']'))
+
+    print 'svgpathid = "{0}"; $'.format(path.attrib['id'])
+
+  
+  
+# ----------------------------
+
+if __name__=="__main__":
+  '''
+    try:
+        optlist,args = getopt.getopt(sys.argv[1:],"thdp")
+    except getopt.GetoptError:
+        usage()
+        sys.exit(2)
+      
+    doHelp = 0
+    c = Context()
+    c.doPrint = 1
+    for opt in optlist:
+        if opt[0] == "-d":  c.debug = 1
+        if opt[0] == "-p":  c.plot  = 1
+        if opt[0] == "-t":  c.triangulate = 1
+        if opt[0] == "-h":  doHelp = 1
+
+    if not doHelp:
+        pts = []
+        fp = sys.stdin
+        if len(args) > 0:
+            fp = open(args[0],'r')
+        for line in fp:
+            fld = line.split()
+            x = float(fld[0])
+            y = float(fld[1])
+            pts.append(Site(x,y))
+        if len(args) > 0: fp.close()
+
+    if doHelp or len(pts) == 0:
+        usage()
+        sys.exit(2)
+  '''
+  svg = sys.argv[1]
+  parsePaths(svg)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/geometry/inst/io/@svg/plot.m	Tue Nov 01 10:45:54 2011 +0000
@@ -0,0 +1,45 @@
+## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (c) 2011 Juan Pablo Carbajal <carbajal@ifi.uzh.ch>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+
+function h = plot(obj, varargin)
+
+  % Get path ids
+  ids = fieldnames(obj.Path);
+  npath = numel(ids);
+
+  t = linspace (0, 1, 64);
+
+  for i = 1:npath
+    x = []; y = [];
+    data = obj.Path.(ids(i)).data;
+
+    for j = 1:numel(data)
+      x = cat (2, x, polyval (data{j}(1,:),t));
+      y = cat (2, y, polyval (data{j}(2,:),t));
+    end
+
+    h = plot(x,y,'-');
+    if i == 1
+      hold on
+    end
+ end
+ hold off
+ axis ij
+ axis equal
+
+endfunction
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/geometry/inst/io/@svg/simplepath.py	Tue Nov 01 10:45:54 2011 +0000
@@ -0,0 +1,212 @@
+#!/usr/bin/env python
+"""
+simplepath.py
+functions for digesting paths into a simple list structure
+
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""
+import re, math
+
+def lexPath(d):
+    """
+    returns and iterator that breaks path data 
+    identifies command and parameter tokens
+    """
+    offset = 0
+    length = len(d)
+    delim = re.compile(r'[ \t\r\n,]+')
+    command = re.compile(r'[MLHVCSQTAZmlhvcsqtaz]')
+    parameter = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
+    while 1:
+        m = delim.match(d, offset)
+        if m:
+            offset = m.end()
+        if offset >= length:
+            break
+        m = command.match(d, offset)
+        if m:
+            yield [d[offset:m.end()], True]
+            offset = m.end()
+            continue
+        m = parameter.match(d, offset)
+        if m:
+            yield [d[offset:m.end()], False]
+            offset = m.end()
+            continue
+        #TODO: create new exception
+        raise Exception, 'Invalid path data!'
+'''
+pathdefs = {commandfamily:
+    [
+    implicitnext,
+    #params,
+    [casts,cast,cast],
+    [coord type,x,y,0]
+    ]}
+'''
+pathdefs = {
+    'M':['L', 2, [float, float], ['x','y']], 
+    'L':['L', 2, [float, float], ['x','y']], 
+    'H':['H', 1, [float], ['x']], 
+    'V':['V', 1, [float], ['y']], 
+    'C':['C', 6, [float, float, float, float, float, float], ['x','y','x','y','x','y']], 
+    'S':['S', 4, [float, float, float, float], ['x','y','x','y']], 
+    'Q':['Q', 4, [float, float, float, float], ['x','y','x','y']], 
+    'T':['T', 2, [float, float], ['x','y']], 
+    'A':['A', 7, [float, float, float, int, int, float, float], ['r','r','a',0,'s','x','y']], 
+    'Z':['L', 0, [], []]
+    }
+def parsePath(d):
+    """
+    Parse SVG path and return an array of segments.
+    Removes all shorthand notation.
+    Converts coordinates to absolute.
+    """
+    retval = []
+    lexer = lexPath(d)
+
+    pen = (0.0,0.0)
+    subPathStart = pen
+    lastControl = pen
+    lastCommand = ''
+    
+    while 1:
+        try:
+            token, isCommand = lexer.next()
+        except StopIteration:
+            break
+        params = []
+        needParam = True
+        if isCommand:
+            if not lastCommand and token.upper() != 'M':
+                raise Exception, 'Invalid path, must begin with moveto.'    
+            else:                
+                command = token
+        else:
+            #command was omited
+            #use last command's implicit next command
+            needParam = False
+            if lastCommand:
+                if lastCommand.isupper():
+                    command = pathdefs[lastCommand][0]
+                else:
+                    command = pathdefs[lastCommand.upper()][0].lower()
+            else:
+                raise Exception, 'Invalid path, no initial command.'    
+        numParams = pathdefs[command.upper()][1]
+        while numParams > 0:
+            if needParam:
+                try: 
+                    token, isCommand = lexer.next()
+                    if isCommand:
+                        raise Exception, 'Invalid number of parameters'
+                except StopIteration:
+                    raise Exception, 'Unexpected end of path'
+            cast = pathdefs[command.upper()][2][-numParams]
+            param = cast(token)
+            if command.islower():
+                if pathdefs[command.upper()][3][-numParams]=='x':
+                    param += pen[0]
+                elif pathdefs[command.upper()][3][-numParams]=='y':
+                    param += pen[1]
+            params.append(param)
+            needParam = True
+            numParams -= 1
+        #segment is now absolute so
+        outputCommand = command.upper()
+    
+        #Flesh out shortcut notation    
+        if outputCommand in ('H','V'):
+            if outputCommand == 'H':
+                params.append(pen[1])
+            if outputCommand == 'V':
+                params.insert(0,pen[0])
+            outputCommand = 'L'
+        if outputCommand in ('S','T'):
+            params.insert(0,pen[1]+(pen[1]-lastControl[1]))
+            params.insert(0,pen[0]+(pen[0]-lastControl[0]))
+            if outputCommand == 'S':
+                outputCommand = 'C'
+            if outputCommand == 'T':
+                outputCommand = 'Q'
+
+        #current values become "last" values
+        if outputCommand == 'M':
+            subPathStart = tuple(params[0:2])
+            pen = subPathStart
+        if outputCommand == 'Z':
+            pen = subPathStart
+        else:
+            pen = tuple(params[-2:])
+
+        if outputCommand in ('Q','C'):
+            lastControl = tuple(params[-4:-2])
+        else:
+            lastControl = pen
+        lastCommand = command
+
+        retval.append([outputCommand,params])
+    return retval
+
+def formatPath(a):
+    """Format SVG path data from an array"""
+    return "".join([cmd + " ".join([str(p) for p in params]) for cmd, params in a])
+
+def translatePath(p, x, y):
+    for cmd,params in p:
+        defs = pathdefs[cmd]
+        for i in range(defs[1]):
+            if defs[3][i] == 'x':
+                params[i] += x
+            elif defs[3][i] == 'y':
+                params[i] += y
+
+def scalePath(p, x, y):
+    for cmd,params in p:
+        defs = pathdefs[cmd]
+        for i in range(defs[1]):
+            if defs[3][i] == 'x':
+                params[i] *= x
+            elif defs[3][i] == 'y':
+                params[i] *= y
+            elif defs[3][i] == 'r':         # radius parameter
+                params[i] *= x
+            elif defs[3][i] == 's':         # sweep-flag parameter
+                if x*y < 0:
+                    params[i] = 1 - params[i]
+            elif defs[3][i] == 'a':         # x-axis-rotation angle
+                if y < 0:
+                    params[i] = - params[i]
+
+def rotatePath(p, a, cx = 0, cy = 0):
+    if a == 0:
+        return p
+    for cmd,params in p:
+        defs = pathdefs[cmd]
+        for i in range(defs[1]):
+            if defs[3][i] == 'x':
+                x = params[i] - cx
+                y = params[i + 1] - cy
+                r = math.sqrt((x**2) + (y**2))
+                if r != 0:
+                    theta = math.atan2(y, x) + a
+                    params[i] = (r * math.cos(theta)) + cx
+                    params[i + 1] = (r * math.sin(theta)) + cy
+
+
+# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/geometry/inst/io/@svg/subsref.m	Tue Nov 01 10:45:54 2011 +0000
@@ -0,0 +1,66 @@
+## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (c) 2011 Juan Pablo Carbajal <carbajal@ifi.uzh.ch>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {} function_name ()
+## @end deftypefn
+
+function out = subsref (obj, idx)
+  if ( !strcmp (class (obj), 'svg') )
+    error ("object must be of the svg class but '%s' was used", class (obj) );
+  elseif ( idx(1).type != '.' )
+    error ("invalid index for class %s", class (obj) );
+  endif
+
+  ## the following at the end may allow to use the obj.method notation one day
+#  ori = inputname(1);
+#  assignin('caller', ori, inPar);
+
+# Error strings
+  method4field = "Class %s has no field %s. Use %s() for the method.";
+  typeNotImplemented = "%s no implemented for Class %s.";
+  
+  method = idx(1).subs;
+
+  switch method
+    case 'plot'
+    
+     if numel (idx) == 1 % obj.plot doesn't exists
+       error (method4field, class (obj), method, method);
+     elseif strcmp (idx(2).type, '()')
+        out = plot (obj, idx(2).subs);
+     else 
+       error (typeNotImplemented,[method idx(2).type], class (obj));
+     end
+      
+    case 'getpath'
+
+     if numel (idx) == 1 % obj.getpath doesn't exists
+       error (method4field, class (obj), method, method);
+     elseif strcmp (idx(2).type, '()')
+        out = getpath (obj, idx(2).subs);
+     else 
+       error (typeNotImplemented,[method idx(2).type], class (obj));
+     end
+
+    case 'pathid'
+      out = fieldnames(obj.Path);
+
+    otherwise
+      error ("invalid index for reference of class %s", class (obj) );
+  endswitch
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/geometry/inst/io/@svg/svg.m	Tue Nov 01 10:45:54 2011 +0000
@@ -0,0 +1,67 @@
+## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (c) 2011 Juan Pablo Carbajal <carbajal@ifi.uzh.ch>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{obj} =} svg ()
+## @deftypefnx {Function File} {@var{obj} =} svg (@var{str})
+## Create object @var{obj} of the svn class.
+##
+## If no input argument is provided the object is empty. @var{str} can be a filename
+## or a string defining an inline SVG.
+##
+## @seealso{@svn/parsePaths}
+## @end deftypefn
+
+function svg = svg(name='')
+
+  svg = struct;
+
+  ## SVG data. All the attributes of the <svg> node.
+  ## The field unparsed contains all the attributes that are not being parsed.
+  svg.Data = struct('height',[],'width',[],'id','null','unparsed',' ');
+  
+  ## SVG metadata. All the attributes of the <metadata> node. 
+  ## The field unparsed contains all the attributes that are not being parsed.
+  svg.Metadata = struct('unparsed',' ');
+
+  ## SVG paths. It is a vector of path structs. Maybe path can be a object too?
+  ## Order of Path.Data is important so we store in a cell (could be a matrix padded with zeros). 
+  ## All the paths stored in polyval compatible format. Straigth segments are also stored as a polynomial.
+  svg.Path = struct();
+  
+  ## SVG paths. All the paths of the svg
+  svg = class (svg, 'svg');
+
+  if !isempty (name)
+    paths = loadpaths(svg, name);
+    svg.Path = paths;
+  elseif !ischar(name)
+    print_usage ;
+  endif
+
+
+endfunction
+
+%!test
+%!  dc = svg('../drawing5.svg');
+%!  dc.getpath()
+%!  dc.pathid
+%!  dc.getpath('path3756')
+%!   
+%!  dc = svg('../drawing.svg');
+%!  ids = dc.pathid;
+%!  dc.getpath({ids{[1 3]}})
+