|
917 | 917 |
|
918 | 918 | let (from-x, from-y, ..) = from |
919 | 919 | let (to-x, to-y, ..) = to |
920 | | - |
| 920 | + |
921 | 921 | // Resolve shift parameter |
922 | 922 | let shift = style.at("shift", default: 0) |
923 | 923 | if type(shift) == dictionary { |
|
2022 | 2022 | }) |
2023 | 2023 | return ctx |
2024 | 2024 | } |
| 2025 | + |
| 2026 | +/// Create a new path from a SVG-like list of commands. |
| 2027 | +/// |
| 2028 | +/// The following commands are supported (uppercase command names use absolute coordinates, lowercase use relative coordinates) |
| 2029 | +/// - `("l", pt)` line to `pt` |
| 2030 | +/// - `("h", num)` Horizontal line |
| 2031 | +/// - `("v", num)` Vertical line |
| 2032 | +/// - `("m", pt)` Move to `pt` |
| 2033 | +/// - `("c", ctrl-1, ctrl-2, pt)` Cubic bezier curve to `pt` with control points `ctrl-1` and `ctrl-2` |
| 2034 | +/// - `("q", ctrl, pt)` Quadratiuc bezier curve |
| 2035 | +/// - `("z")` Close the current path |
| 2036 | +#let svg-path(name: none, anchor: none, ..commands-style) = { |
| 2037 | + let style = commands-style.named() |
| 2038 | + let commands = commands-style.pos().map(cmd => { |
| 2039 | + if type(cmd) == str { |
| 2040 | + (cmd,) |
| 2041 | + } else { |
| 2042 | + cmd |
| 2043 | + } |
| 2044 | + }) |
| 2045 | + return (ctx => { |
| 2046 | + let paths = () |
| 2047 | + |
| 2048 | + let origin = (0, 0, 0) |
| 2049 | + let current = () |
| 2050 | + |
| 2051 | + for ((cmd, ..args)) in commands { |
| 2052 | + assert(cmd in ("m", "M", "l", "L", "c", "C", "h", "H", "v", "V", "z", "Z", "q", "Q"), |
| 2053 | + message: "Unknown svg-path command: " + repr(cmd)) |
| 2054 | + |
| 2055 | + let is-relative = cmd in ("m", "l", "c", "h", "v") |
| 2056 | + let wrap-coordinate = if is-relative { |
| 2057 | + x => (rel: x) |
| 2058 | + } else { |
| 2059 | + x => x |
| 2060 | + } |
| 2061 | + |
| 2062 | + if cmd in ("h", "H") { |
| 2063 | + assert.eq(args.len(), 1) |
| 2064 | + let (x, ..rest) = args |
| 2065 | + args = ((x, 0.0, 0.0),) |
| 2066 | + cmd = if cmd == "h" { "l" } else { "L" } |
| 2067 | + } else if cmd in ("v", "V") { |
| 2068 | + assert.eq(args.len(), 1) |
| 2069 | + let (y, ..rest) = args |
| 2070 | + args = ((0.0, y, 0.0),) |
| 2071 | + cmd = if cmd == "v" { "l" } else { "L" } |
| 2072 | + } |
| 2073 | + |
| 2074 | + (ctx, ..args) = coordinate.resolve(ctx, ..args.map(wrap-coordinate)) |
| 2075 | + |
| 2076 | + if cmd in ("z", "Z") { |
| 2077 | + assert.eq(args.len(), 0) |
| 2078 | + if current != () { |
| 2079 | + paths.push(path-util.make-subpath(origin, current, closed: cmd == "z")) |
| 2080 | + } |
| 2081 | + |
| 2082 | + current = () |
| 2083 | + } |
| 2084 | + |
| 2085 | + if cmd in ("m", "M", "l", "L") { |
| 2086 | + if cmd in ("m", "M") { |
| 2087 | + assert.eq(args.len(), 1) |
| 2088 | + origin = args.at(0, default: (0, 0, 0)) |
| 2089 | + args.pop() |
| 2090 | + } |
| 2091 | + |
| 2092 | + current += args.map(pt => ("l", pt)) |
| 2093 | + } else if cmd in ("c", "C") { |
| 2094 | + assert.eq(args.len(), 3) |
| 2095 | + let (c1, c2, pt) = args |
| 2096 | + current.push(("c", c1, c2, pt)) |
| 2097 | + } else if cmd in ("q", "Q") { |
| 2098 | + assert.eq(args.len(), 2) |
| 2099 | + let (c1, pt) = args |
| 2100 | + let (_, pt, c1, c2) = bezier_.quadratic-to-cubic(ctx.prev.pt, pt, c1) |
| 2101 | + current.push(("c", c1, c2, pt)) |
| 2102 | + } |
| 2103 | + } |
| 2104 | + |
| 2105 | + if current != () { |
| 2106 | + paths.push(path-util.make-subpath(origin, current, closed: false)) |
| 2107 | + } |
| 2108 | + |
| 2109 | + let style = styles.resolve(ctx.style, merge: style) |
| 2110 | + let drawables = drawable.path(paths, stroke: style.stroke, fill: style.fill, fill-rule: style.fill-rule) |
| 2111 | + |
| 2112 | + let (transform, anchors) = anchor_.setup( |
| 2113 | + (_) => none, |
| 2114 | + (), |
| 2115 | + default: none, |
| 2116 | + name: name, |
| 2117 | + offset-anchor: anchor, |
| 2118 | + transform: ctx.transform, |
| 2119 | + //border-anchors: true, |
| 2120 | + path-anchors: true, |
| 2121 | + path: drawables, |
| 2122 | + ) |
| 2123 | + |
| 2124 | + if mark_.check-mark(style.mark) { |
| 2125 | + drawables = mark_.place-marks-along-path(ctx, style.mark, transform, drawables) |
| 2126 | + } else { |
| 2127 | + drawables = drawable.apply-transform(transform, drawables) |
| 2128 | + } |
| 2129 | + |
| 2130 | + return ( |
| 2131 | + ctx: ctx, |
| 2132 | + name: name, |
| 2133 | + anchors: anchors, |
| 2134 | + drawables: drawables, |
| 2135 | + ) |
| 2136 | + },) |
| 2137 | +} |
0 commit comments