Mercurial > forge
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]}}) +