From: mental Date: Sat, 12 Aug 2006 06:31:15 +0000 (+0000) Subject: Ruby port of simplepath.py X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=facee090e6a4eb876ad2dc0b99ea8b4d06a71536;p=inkscape.git Ruby port of simplepath.py --- diff --git a/share/extensions/Makefile.am b/share/extensions/Makefile.am index fc4297abc..13f6e0f2e 100644 --- a/share/extensions/Makefile.am +++ b/share/extensions/Makefile.am @@ -36,6 +36,7 @@ extensions = \ radiusrand.py \ rtree.py \ simplepath.py \ + simplepath.rb \ simplestyle.py \ straightseg.py \ wavy.py \ diff --git a/share/extensions/simplepath.rb b/share/extensions/simplepath.rb new file mode 100755 index 000000000..b8f91232f --- /dev/null +++ b/share/extensions/simplepath.rb @@ -0,0 +1,208 @@ +#!/usr/bin/env ruby + +# simplepath.rb +# functions for digesting paths into a simple list structure +# +# Ruby port by MenTaLguY +# +# Copyright (C) 2005 Aaron Spike +# Copyright (C) 2006 MenTaLguY +# +# 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 + +require 'strscan' + +def lexPath(d) + # iterator which breaks path data + # identifies command and parameter tokens + + scanner = StringScanner.new(d) + + delim = /[ \t\r\n,]+/ + command = /[MLHVCSQTAZmlhvcsqtaz]/ + parameter = /(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/ + + until scanner.eos? + scanner.skip(delim) + if m = scanner.scan(command) + yield m, true + elsif m = scanner.scan(parameter) + yield m, false + else + raise 'Invalid path data!' + end + end +end + +PathDef = Struct.new :implicit_next, :param_count, :casts, :coord_types +PATHDEFS = { + 'M' => PathDef['L', 2, [:to_f, :to_f], [:x,:y]], + 'L' => PathDef['L', 2, [:to_f, :to_f], [:x,:y]], + 'H' => PathDef['H', 1, [:to_f], [:x]], + 'V' => PathDef['V', 1, [:to_f], [:y]], + 'C' => PathDef['C', 6, [:to_f, :to_f, :to_f, :to_f, :to_f, :to_f], [:x,:y,:x,:y,:x,:y]], + 'S' => PathDef['S', 4, [:to_f, :to_f, :to_f, :to_f], [:x,:y,:x,:y]], + 'Q' => PathDef['Q', 4, [:to_f, :to_f, :to_f, :to_f], [:x,:y,:x,:y]], + 'T' => PathDef['T', 2, [:to_f, :to_f], [:x,:y]], + 'A' => PathDef['A', 7, [:to_f, :to_f, :to_f, :to_i, :to_i, :to_f, :to_f], [0,0,0,0,0,:x,:y]], + 'Z' => PathDef['L', 0, [], []] +} + +def parsePath(d) + # Parse SVG path and return an array of segments. + # Removes all shorthand notation. + # Converts coordinates to absolute. + + retval = [] + + command = nil + outputCommand = nil + params = [] + + pen = [0.0,0.0] + subPathStart = pen + lastControl = pen + lastCommand = nil + + lexPath(d) do |token, isCommand| + unless command + if isCommand + if lastCommand or token.upcase == 'M' + command = token + else + raise 'Invalid path, must begin with moveto.' + end + else + #command was omited + #use last command's implicit next command + if lastCommand + if lastCommand =~ /[A-Z]/ + command = PATHDEFS[lastCommand].implicit_next + else + command = PATHDEFS[lastCommand.upcase].implicit_next.downcase + end + else + raise 'Invalid path, no initial command.' + end + end + outputCommand = command.upcase + else + raise 'Invalid number of parameters' if isCommand + param = token.send PATHDEFS[outputCommand].casts[params.length] + if command =~ /[a-z]/ + case PATHDEFS[outputCommand].coord_types[params.length] + when :x: param += pen[0] + when :y: param += pen[1] + end + end + params.push param + end + + if params.length == PATHDEFS[outputCommand].param_count + + #Flesh out shortcut notation + case outputCommand + when 'H','V' + case outputCommand + when 'H': params.push pen[1] + when 'V': params.unshift pen[0] + end + outputCommand = 'L' + when 'S','T' + params.unshift(pen[1]+(pen[1]-lastControl[1])) + params.unshift(pen[0]+(pen[0]-lastControl[0])) + case outputCommand + when 'S': outputCommand = 'C' + when 'T': outputCommand = 'Q' + end + end + + #current values become "last" values + case outputCommand + when 'M' + subPathStart = params[0,2] + pen = subPathStart + when 'Z' + pen = subPathStart + else + pen = params[-2,2] + end + + case outputCommand + when 'Q','C' + lastControl = params[-4,2] + else + lastControl = pen + end + + lastCommand = command + retval.push [outputCommand,params] + command = nil + params = [] + end + end + + raise 'Unexpected end of path' if command + + return retval +end + +def formatPath(a) + # Format SVG path data from an array + a.map { |cmd,params| "#{cmd} #{params.join(' ')}" }.join +end + +def translatePath(p, x, y) + p.each do |cmd,params| + coord_types = PATHDEFS[cmd].coord_types + for i in 0...(params.length) + case coord_types[i] + when :x: params[i] += x + when :y: params[i] += y + end + end + end +end + +def scalePath(p, x, y) + p.each do |cmd,params| + coord_types = PATHDEFS[cmd].coord_types + for i in 0...(params.length) + case coord_types[i] + when :x: params[i] *= x + when :y: params[i] *= y + end + end + end +end + +def rotatePath(p, a, cx = 0, cy = 0) + return p if a == 0 + p.each do |cmd,params| + coord_types = PATHDEFS[cmd].coord_types + for i in 0...(params.length) + if coord_types[i] == :x + x = params[i] - cx + y = params[i + 1] - cy + r = Math.sqrt((x**2) + (y**2)) + unless r.zero? + theta = Math.atan2(y, x) + a + params[i] = (r * Math.cos(theta)) + cx + params[i + 1] = (r * Math.sin(theta)) + cy + end + end + end + end +end