CMS 3D CMS Logo

Public Member Functions | Public Attributes | Static Public Attributes

svgfig::Path Class Reference

List of all members.

Public Member Functions

def __init__
def __repr__
def parse
def parse_boolean
def parse_command
def parse_number
def parse_whitespace
def SVG

Public Attributes

 attr
 d

Static Public Attributes

dictionary defaults = {}

Detailed Description

Path represents an SVG path, an arbitrary set of curves and
straight segments. Unlike SVG("path", d="..."), Path stores
coordinates as a list of numbers, rather than a string, so that it is
transformable in a Fig.

Path(d, attribute=value)

d                       required        path data
attribute=value pairs   keyword list    SVG attributes

See https://www.w3.org/TR/SVG/paths.html for specification of paths
from text.

Internally, Path data is a list of tuples with these definitions:

    * ("Z/z",): close the current path
    * ("H/h", x) or ("V/v", y): a horizontal or vertical line
      segment to x or y
    * ("M/m/L/l/T/t", x, y, global): moveto, lineto, or smooth
      quadratic curveto point (x, y). If global=True, (x, y) should
      not be transformed.
    * ("S/sQ/q", cx, cy, cglobal, x, y, global): polybezier or
      smooth quadratic curveto point (x, y) using (cx, cy) as a
      control point. If cglobal or global=True, (cx, cy) or (x, y)
      should not be transformed.
    * ("C/c", c1x, c1y, c1global, c2x, c2y, c2global, x, y, global):
      cubic curveto point (x, y) using (c1x, c1y) and (c2x, c2y) as
      control points. If c1global, c2global, or global=True, (c1x, c1y),
      (c2x, c2y), or (x, y) should not be transformed.
    * ("A/a", rx, ry, rglobal, x-axis-rotation, angle, large-arc-flag,
      sweep-flag, x, y, global): arcto point (x, y) using the
      aforementioned parameters.
    * (",/.", rx, ry, rglobal, angle, x, y, global): an ellipse at
      point (x, y) with radii (rx, ry). If angle is 0, the whole
      ellipse is drawn; otherwise, a partial ellipse is drawn.

Definition at line 1020 of file svgfig.py.


Constructor & Destructor Documentation

def svgfig::Path::__init__ (   self,
  d = [],
  attr 
)

Definition at line 1062 of file svgfig.py.

01063                                   :
01064     if isinstance(d, basestring): self.d = self.parse(d)
01065     else: self.d = list(d)
01066 
01067     self.attr = dict(self.defaults)
01068     self.attr.update(attr)


Member Function Documentation

def svgfig::Path::__repr__ (   self)

Definition at line 1059 of file svgfig.py.

01060                     :
01061     return "<Path (%d nodes) %s>" % (len(self.d), self.attr)

def svgfig::Path::parse (   self,
  pathdata 
)
Parses text-commands, converting them into a list of tuples.
Called by the constructor.

Definition at line 1117 of file svgfig.py.

01118                            :
01119     """Parses text-commands, converting them into a list of tuples.
01120     Called by the constructor."""
01121     output = []
01122     index = 0
01123     while True:
01124       command, index, pathdata = self.parse_command(index, pathdata)
01125       index, pathdata = self.parse_whitespace(index, pathdata)
01126 
01127       if command == None and index == len(pathdata): break  # this is the normal way out of the loop
01128       if command in ("Z", "z"):
01129         output.append((command,))
01130 
01131       ######################
01132       elif command in ("H", "h", "V", "v"):
01133         errstring = "Path command \"%s\" requires a number at index %d" % (command, index)
01134         num1, index, pathdata = self.parse_number(index, pathdata)
01135         if num1 == None: raise ValueError, errstring
01136 
01137         while num1 != None:
01138           output.append((command, num1))
01139           num1, index, pathdata = self.parse_number(index, pathdata)
01140 
01141       ######################
01142       elif command in ("M", "m", "L", "l", "T", "t"):
01143         errstring = "Path command \"%s\" requires an x,y pair at index %d" % (command, index)
01144         num1, index, pathdata = self.parse_number(index, pathdata)
01145         num2, index, pathdata = self.parse_number(index, pathdata)
01146 
01147         if num1 == None: raise ValueError, errstring
01148 
01149         while num1 != None:
01150           if num2 == None: raise ValueError, errstring
01151           output.append((command, num1, num2, False))
01152 
01153           num1, index, pathdata = self.parse_number(index, pathdata)
01154           num2, index, pathdata = self.parse_number(index, pathdata)
01155 
01156       ######################
01157       elif command in ("S", "s", "Q", "q"):
01158         errstring = "Path command \"%s\" requires a cx,cy,x,y quadruplet at index %d" % (command, index)
01159         num1, index, pathdata = self.parse_number(index, pathdata)
01160         num2, index, pathdata = self.parse_number(index, pathdata)
01161         num3, index, pathdata = self.parse_number(index, pathdata)
01162         num4, index, pathdata = self.parse_number(index, pathdata)
01163 
01164         if num1 == None: raise ValueError, errstring
01165 
01166         while num1 != None:
01167           if num2 == None or num3 == None or num4 == None: raise ValueError, errstring
01168           output.append((command, num1, num2, False, num3, num4, False))
01169 
01170           num1, index, pathdata = self.parse_number(index, pathdata)
01171           num2, index, pathdata = self.parse_number(index, pathdata)
01172           num3, index, pathdata = self.parse_number(index, pathdata)
01173           num4, index, pathdata = self.parse_number(index, pathdata)
01174           
01175       ######################
01176       elif command in ("C", "c"):
01177         errstring = "Path command \"%s\" requires a c1x,c1y,c2x,c2y,x,y sextuplet at index %d" % (command, index)
01178         num1, index, pathdata = self.parse_number(index, pathdata)
01179         num2, index, pathdata = self.parse_number(index, pathdata)
01180         num3, index, pathdata = self.parse_number(index, pathdata)
01181         num4, index, pathdata = self.parse_number(index, pathdata)
01182         num5, index, pathdata = self.parse_number(index, pathdata)
01183         num6, index, pathdata = self.parse_number(index, pathdata)
01184         
01185         if num1 == None: raise ValueError, errstring
01186 
01187         while num1 != None:
01188           if num2 == None or num3 == None or num4 == None or num5 == None or num6 == None: raise ValueError, errstring
01189 
01190           output.append((command, num1, num2, False, num3, num4, False, num5, num6, False))
01191 
01192           num1, index, pathdata = self.parse_number(index, pathdata)
01193           num2, index, pathdata = self.parse_number(index, pathdata)
01194           num3, index, pathdata = self.parse_number(index, pathdata)
01195           num4, index, pathdata = self.parse_number(index, pathdata)
01196           num5, index, pathdata = self.parse_number(index, pathdata)
01197           num6, index, pathdata = self.parse_number(index, pathdata)
01198 
01199       ######################
01200       elif command in ("A", "a"):
01201         errstring = "Path command \"%s\" requires a rx,ry,angle,large-arc-flag,sweep-flag,x,y septuplet at index %d" % (command, index)
01202         num1, index, pathdata = self.parse_number(index, pathdata)
01203         num2, index, pathdata = self.parse_number(index, pathdata)
01204         num3, index, pathdata = self.parse_number(index, pathdata)
01205         num4, index, pathdata = self.parse_boolean(index, pathdata)
01206         num5, index, pathdata = self.parse_boolean(index, pathdata)
01207         num6, index, pathdata = self.parse_number(index, pathdata)
01208         num7, index, pathdata = self.parse_number(index, pathdata)
01209 
01210         if num1 == None: raise ValueError, errstring
01211 
01212         while num1 != None:
01213           if num2 == None or num3 == None or num4 == None or num5 == None or num6 == None or num7 == None: raise ValueError, errstring
01214 
01215           output.append((command, num1, num2, False, num3, num4, num5, num6, num7, False))
01216 
01217           num1, index, pathdata = self.parse_number(index, pathdata)
01218           num2, index, pathdata = self.parse_number(index, pathdata)
01219           num3, index, pathdata = self.parse_number(index, pathdata)
01220           num4, index, pathdata = self.parse_boolean(index, pathdata)
01221           num5, index, pathdata = self.parse_boolean(index, pathdata)
01222           num6, index, pathdata = self.parse_number(index, pathdata)
01223           num7, index, pathdata = self.parse_number(index, pathdata)
01224 
01225     return output

def svgfig::Path::parse_boolean (   self,
  index,
  pathdata 
)
Part of Path's text-command parsing algorithm; used internally.

Definition at line 1104 of file svgfig.py.

01105                                           :
01106     """Part of Path's text-command parsing algorithm; used internally."""
01107     index, pathdata = self.parse_whitespace(index, pathdata)
01108 
01109     if index >= len(pathdata): return None, index, pathdata
01110     first_digit = pathdata[index]
01111 
01112     if first_digit in ("0", "1"):
01113       index += 1
01114       return int(first_digit), index, pathdata
01115     else:
01116       return None, index, pathdata

def svgfig::Path::parse_command (   self,
  index,
  pathdata 
)
Part of Path's text-command parsing algorithm; used internally.

Definition at line 1074 of file svgfig.py.

01075                                           :
01076     """Part of Path's text-command parsing algorithm; used internally."""
01077     index, pathdata = self.parse_whitespace(index, pathdata)
01078 
01079     if index >= len(pathdata): return None, index, pathdata
01080     command = pathdata[index]
01081     if "A" <= command <= "Z" or "a" <= command <= "z":
01082       index += 1
01083       return command, index, pathdata
01084     else: 
01085       return None, index, pathdata

def svgfig::Path::parse_number (   self,
  index,
  pathdata 
)
Part of Path's text-command parsing algorithm; used internally.

Definition at line 1086 of file svgfig.py.

01087                                          :
01088     """Part of Path's text-command parsing algorithm; used internally."""
01089     index, pathdata = self.parse_whitespace(index, pathdata)
01090 
01091     if index >= len(pathdata): return None, index, pathdata
01092     first_digit = pathdata[index]
01093 
01094     if "0" <= first_digit <= "9" or first_digit in ("-", "+", "."):
01095       start = index
01096       while index < len(pathdata) and ("0" <= pathdata[index] <= "9" or pathdata[index] in ("-", "+", ".", "e", "E")):
01097         index += 1
01098       end = index
01099 
01100       index = end
01101       return float(pathdata[start:end]), index, pathdata
01102     else: 
01103       return None, index, pathdata

def svgfig::Path::parse_whitespace (   self,
  index,
  pathdata 
)
Part of Path's text-command parsing algorithm; used internally.

Definition at line 1069 of file svgfig.py.

01070                                              :
01071     """Part of Path's text-command parsing algorithm; used internally."""
01072     while index < len(pathdata) and pathdata[index] in (" ", "\t", "\r", "\n", ","): index += 1
01073     return index, pathdata

def svgfig::Path::SVG (   self,
  trans = None 
)
Apply the transformation "trans" and return an SVG object.

Definition at line 1226 of file svgfig.py.

01227                            :
01228     """Apply the transformation "trans" and return an SVG object."""
01229     if isinstance(trans, basestring): trans = totrans(trans)
01230 
01231     x, y, X, Y = None, None, None, None
01232     output = []
01233     for datum in self.d:
01234       if not isinstance(datum, (tuple, list)):
01235         raise TypeError, "pathdata elements must be tuples/lists"
01236 
01237       command = datum[0]
01238 
01239       ######################
01240       if command in ("Z", "z"):
01241         x, y, X, Y = None, None, None, None
01242         output.append("Z")
01243 
01244       ######################
01245       elif command in ("H", "h", "V", "v"):
01246         command, num1 = datum
01247 
01248         if command == "H" or (command == "h" and x == None): x = num1
01249         elif command == "h": x += num1
01250         elif command == "V" or (command == "v" and y == None): y = num1
01251         elif command == "v": y += num1
01252 
01253         if trans == None: X, Y = x, y
01254         else: X, Y = trans(x, y)
01255 
01256         output.append("L%g %g" % (X, Y))
01257         
01258       ######################
01259       elif command in ("M", "m", "L", "l", "T", "t"):
01260         command, num1, num2, isglobal12 = datum
01261 
01262         if trans == None or isglobal12:
01263           if command.isupper() or X == None or Y == None:
01264             X, Y = num1, num2
01265           else:
01266             X += num1
01267             Y += num2
01268           x, y = X, Y
01269 
01270         else:
01271           if command.isupper() or x == None or y == None:
01272             x, y = num1, num2
01273           else:
01274             x += num1
01275             y += num2
01276           X, Y = trans(x, y)
01277 
01278         COMMAND = command.capitalize()
01279         output.append("%s%g %g" % (COMMAND, X, Y))
01280 
01281       ######################
01282       elif command in ("S", "s", "Q", "q"):
01283         command, num1, num2, isglobal12, num3, num4, isglobal34 = datum
01284 
01285         if trans == None or isglobal12:
01286           if command.isupper() or X == None or Y == None:
01287             CX, CY = num1, num2
01288           else:
01289             CX = X + num1
01290             CY = Y + num2
01291 
01292         else:
01293           if command.isupper() or x == None or y == None:
01294             cx, cy = num1, num2
01295           else:
01296             cx = x + num1
01297             cy = y + num2
01298           CX, CY = trans(cx, cy)
01299 
01300         if trans == None or isglobal34:
01301           if command.isupper() or X == None or Y == None:
01302             X, Y = num3, num4
01303           else:
01304             X += num3
01305             Y += num4
01306           x, y = X, Y
01307 
01308         else:
01309           if command.isupper() or x == None or y == None:
01310             x, y = num3, num4
01311           else:
01312             x += num3
01313             y += num4
01314           X, Y = trans(x, y)
01315 
01316         COMMAND = command.capitalize()
01317         output.append("%s%g %g %g %g" % (COMMAND, CX, CY, X, Y))
01318 
01319       ######################
01320       elif command in ("C", "c"):
01321         command, num1, num2, isglobal12, num3, num4, isglobal34, num5, num6, isglobal56 = datum
01322 
01323         if trans == None or isglobal12:
01324           if command.isupper() or X == None or Y == None:
01325             C1X, C1Y = num1, num2
01326           else:
01327             C1X = X + num1
01328             C1Y = Y + num2
01329 
01330         else:
01331           if command.isupper() or x == None or y == None:
01332             c1x, c1y = num1, num2
01333           else:
01334             c1x = x + num1
01335             c1y = y + num2
01336           C1X, C1Y = trans(c1x, c1y)
01337 
01338         if trans == None or isglobal34:
01339           if command.isupper() or X == None or Y == None:
01340             C2X, C2Y = num3, num4
01341           else:
01342             C2X = X + num3
01343             C2Y = Y + num4
01344 
01345         else:
01346           if command.isupper() or x == None or y == None:
01347             c2x, c2y = num3, num4
01348           else:
01349             c2x = x + num3
01350             c2y = y + num4
01351           C2X, C2Y = trans(c2x, c2y)
01352 
01353         if trans == None or isglobal56:
01354           if command.isupper() or X == None or Y == None:
01355             X, Y = num5, num6
01356           else:
01357             X += num5
01358             Y += num6
01359           x, y = X, Y
01360 
01361         else:
01362           if command.isupper() or x == None or y == None:
01363             x, y = num5, num6
01364           else:
01365             x += num5
01366             y += num6
01367           X, Y = trans(x, y)
01368 
01369         COMMAND = command.capitalize()
01370         output.append("%s%g %g %g %g %g %g" % (COMMAND, C1X, C1Y, C2X, C2Y, X, Y))
01371 
01372       ######################
01373       elif command in ("A", "a"):
01374         command, num1, num2, isglobal12, angle, large_arc_flag, sweep_flag, num3, num4, isglobal34 = datum
01375 
01376         oldx, oldy = x, y
01377         OLDX, OLDY = X, Y
01378 
01379         if trans == None or isglobal34:
01380           if command.isupper() or X == None or Y == None:
01381             X, Y = num3, num4
01382           else:
01383             X += num3
01384             Y += num4
01385           x, y = X, Y
01386 
01387         else:
01388           if command.isupper() or x == None or y == None:
01389             x, y = num3, num4
01390           else:
01391             x += num3
01392             y += num4
01393           X, Y = trans(x, y)
01394         
01395         if x != None and y != None:
01396           centerx, centery = (x + oldx)/2., (y + oldy)/2.
01397         CENTERX, CENTERY = (X + OLDX)/2., (Y + OLDY)/2.
01398 
01399         if trans == None or isglobal12:
01400           RX = CENTERX + num1
01401           RY = CENTERY + num2
01402 
01403         else:
01404           rx = centerx + num1
01405           ry = centery + num2
01406           RX, RY = trans(rx, ry)
01407 
01408         COMMAND = command.capitalize()
01409         output.append("%s%g %g %g %d %d %g %g" % (COMMAND, RX - CENTERX, RY - CENTERY, angle, large_arc_flag, sweep_flag, X, Y))
01410 
01411       elif command in (",", "."):
01412         command, num1, num2, isglobal12, angle, num3, num4, isglobal34 = datum
01413         if trans == None or isglobal34:
01414           if command == "." or X == None or Y == None:
01415             X, Y = num3, num4
01416           else:
01417             X += num3
01418             Y += num4
01419             x, y = None, None
01420 
01421         else:
01422           if command == "." or x == None or y == None:
01423             x, y = num3, num4
01424           else:
01425             x += num3
01426             y += num4
01427           X, Y = trans(x, y)
01428 
01429         if trans == None or isglobal12:
01430           RX = X + num1
01431           RY = Y + num2
01432 
01433         else:
01434           rx = x + num1
01435           ry = y + num2
01436           RX, RY = trans(rx, ry)
01437 
01438         RX, RY = RX - X, RY - Y
01439 
01440         X1, Y1 = X + RX * math.cos(angle*math.pi/180.), Y + RX * math.sin(angle*math.pi/180.)
01441         X2, Y2 = X + RY * math.sin(angle*math.pi/180.), Y - RY * math.cos(angle*math.pi/180.)
01442         X3, Y3 = X - RX * math.cos(angle*math.pi/180.), Y - RX * math.sin(angle*math.pi/180.)
01443         X4, Y4 = X - RY * math.sin(angle*math.pi/180.), Y + RY * math.cos(angle*math.pi/180.)
01444 
01445         output.append("M%g %gA%g %g %g 0 0 %g %gA%g %g %g 0 0 %g %gA%g %g %g 0 0 %g %gA%g %g %g 0 0 %g %g" \
01446                       % (X1, Y1, RX, RY, angle, X2, Y2, RX, RY, angle, X3, Y3, RX, RY, angle, X4, Y4, RX, RY, angle, X1, Y1))
01447 
01448     return SVG("path", d="".join(output), **self.attr)


Member Data Documentation

Definition at line 1062 of file svgfig.py.

Definition at line 1062 of file svgfig.py.

dictionary svgfig::Path::defaults = {} [static]

Definition at line 1057 of file svgfig.py.