Merge pull request #44 from guzba/master

faster, no start
This commit is contained in:
treeform 2021-01-02 21:04:57 -08:00 committed by GitHub
commit 040a66018b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 135 additions and 127 deletions

View file

@ -1,4 +1,4 @@
version = "0.0.9"
version = "0.0.10"
author = "Andre von Houck and Ryan Oldenburg"
description = "Full-featured 2d graphics library for Nim."
license = "MIT"

View file

@ -7,9 +7,9 @@ type
PathCommandKind* = enum
## Type of path commands
Start, End
Move, Line, HLine, VLine, Cubic, SCurve, Quad, TQuad, Arc,
RMove, RLine, RHLine, RVLine, RCubic, RSCurve, RQuad, RTQuad, RArc
Close,
Move, Line, HLine, VLine, Cubic, SCubic, Quad, TQuad, Arc,
RMove, RLine, RHLine, RVLine, RCubic, RSCubic, RQuad, RTQuad, RArc
PathCommand* = object
## Binary version of an SVG command
@ -23,131 +23,140 @@ type
proc newPath*(): Path =
result = Path()
proc commandNumbers(kind: PathCommandKind): int =
## How many numbers does a command take:
proc parameterCount(kind: PathCommandKind): int =
case kind:
of Start, End: 0
of Close: 0
of Move, Line, RMove, RLine: 2
of HLine, VLine, RHLine, RVLine: 1
of Cubic, RCubic: 6
of SCurve, RSCurve, Quad, RQuad: 4
of SCubic, RSCubic, Quad, RQuad: 4
of TQuad, RTQuad: 2
of Arc, RArc: 7
proc parsePath*(path: string): Path =
## Converts a SVG style path into seq of commands.
result = newPath()
var command = Start
var number = ""
var numbers = newSeq[float32]()
template finishDigit() =
if number.len > 0:
numbers.add(parseFloat(number))
number = ""
if path.len == 0:
return
template finishCommand() =
finishDigit()
if command != Start:
let num = commandNumbers(command)
if num > 0:
if numbers.len mod num != 0:
raise newException(PixieError,
"Could not parse path: " & $command & " has wrong number of prams," &
" got " & $numbers.len & " but expected " & $num & ".")
for batch in 0 ..< numbers.len div num:
result.commands.add PathCommand(
kind: command,
numbers: numbers[batch*num ..< (batch+1)*num]
)
numbers.setLen(0)
var
p, numberStart: int
armed: bool
kind: PathCommandKind
numbers: seq[float32]
proc finishNumber() =
if numberStart > 0:
try:
numbers.add(parseFloat(path[numberStart ..< p]))
except ValueError:
raise newException(PixieError, "Invalid path, parsing paramter failed")
numberStart = 0
proc finishCommand(result: var Path) =
finishNumber()
if armed: # The first finishCommand() arms
let paramCount = parameterCount(kind)
if paramCount == 0:
if numbers.len != 0:
raise newException(PixieError, "Invalid path, unexpected paramters")
result.commands.add(PathCommand(kind: kind))
else:
assert numbers.len == 0
result.commands.add PathCommand(kind: command)
if numbers.len mod paramCount != 0:
raise newException(
PixieError,
"Invalid path, wrong number of parameters"
)
for batch in 0 ..< numbers.len div paramCount:
result.commands.add(PathCommand(
kind: kind,
numbers: numbers[batch * paramCount ..< (batch + 1) * paramCount]
))
numbers.setLen(0)
for c in path:
case c:
# Relative.
armed = true
while p < path.len:
case path[p]:
# Relative
of 'm':
finishCommand()
command = RMove
finishCommand(result)
kind = RMove
of 'l':
finishCommand()
command = RLine
finishCommand(result)
kind = RLine
of 'h':
finishCommand()
command = RHLine
finishCommand(result)
kind = RHLine
of 'v':
finishCommand()
command = RVLine
finishCommand(result)
kind = RVLine
of 'c':
finishCommand()
command = RCubic
finishCommand(result)
kind = RCubic
of 's':
finishCommand()
command = RSCurve
finishCommand(result)
kind = RSCubic
of 'q':
finishCommand()
command = RQuad
finishCommand(result)
kind = RQuad
of 't':
finishCommand()
command = RTQuad
finishCommand(result)
kind = RTQuad
of 'a':
finishCommand()
command = RArc
finishCommand(result)
kind = RArc
of 'z':
finishCommand()
command = End
finishCommand(result)
kind = Close
# Absolute
of 'M':
finishCommand()
command = Move
finishCommand(result)
kind = Move
of 'L':
finishCommand()
command = Line
finishCommand(result)
kind = Line
of 'H':
finishCommand()
command = HLine
finishCommand(result)
kind = HLine
of 'V':
finishCommand()
command = VLine
finishCommand(result)
kind = VLine
of 'C':
finishCommand()
command = Cubic
finishCommand(result)
kind = Cubic
of 'S':
finishCommand()
command = SCurve
finishCommand(result)
kind = SCubic
of 'Q':
finishCommand()
command = Quad
finishCommand(result)
kind = Quad
of 'T':
finishCommand()
command = TQuad
finishCommand(result)
kind = TQuad
of 'A':
finishCommand()
command = Arc
finishCommand(result)
kind = Arc
of 'Z':
finishCommand()
command = End
# Punctuation
finishCommand(result)
kind = Close
of '-', '+':
if number.len > 0 and number[^1] in {'e', 'E'}:
number &= c
if numberStart > 0 and path[p - 1] in {'e', 'E'}:
discard
else:
finishDigit()
number = $c
finishNumber()
numberStart = p
of ' ', ',', '\r', '\n', '\t':
finishDigit()
else: # TODO: still needed?
if command == Move and numbers.len == 2:
finishCommand()
command = Line
elif command == Line and numbers.len == 2:
finishCommand()
command = Line
number &= c
finishNumber()
else:
if numberStart == 0:
numberStart = p
finishCommand()
inc p
finishCommand(result)
proc `$`*(path: Path): string =
for i, command in path.commands:
@ -157,7 +166,7 @@ proc `$`*(path: Path): string =
of HLine: result.add "H"
of VLine: result.add "V"
of Cubic: result.add "C"
of SCurve: result.add "S"
of SCubic: result.add "S"
of Quad: result.add "Q"
of TQuad: result.add "T"
of Arc: result.add "A"
@ -166,15 +175,14 @@ proc `$`*(path: Path): string =
of RHLine: result.add "h"
of RVLine: result.add "v"
of RCubic: result.add "c"
of RSCurve: result.add "s"
of RSCubic: result.add "s"
of RQuad: result.add "q"
of RTQuad: result.add "t"
of RArc: result.add "a"
of End: result.add "Z"
of Start: result.add "?"
of Close: result.add "Z"
for j, number in command.numbers:
if floor(number) == number:
result.add $(number.int)
result.add $number.int
else:
result.add $number
if i != path.commands.len - 1 or j != command.numbers.len - 1:
@ -412,7 +420,7 @@ proc commandsToPolygons*(commands: seq[PathCommand]): seq[seq[Vec2]] =
a += step
at = polygon[^1]
of End:
of Close:
assert command.numbers.len == 0
if at != start:
if prevCommand == Quad or prevCommand == TQuad:
@ -481,7 +489,7 @@ proc commandsToPolygons*(commands: seq[PathCommand]): seq[seq[Vec2]] =
drawCurve(@[at, ctr, ctr2, to])
at = to
of RSCurve:
of RSCubic:
assert command.numbers.len == 4
ctr = at
ctr2.x = at.x + command.numbers[0]
@ -795,7 +803,7 @@ proc addPath*(path: Path, other: Path) =
proc closePath*(path: Path) =
## Causes the point of the pen to move back to the start of the current sub-path. It tries to draw a straight line from the current point to the start. If the shape has already been closed or has only one point, this function does nothing.
path.commands.add PathCommand(kind: End)
path.commands.add PathCommand(kind: Close)
proc moveTo*(path: Path, x, y: float32) =
## Moves the starting point of a new sub-path to the (x, y) coordinates.