initial commit
This commit is contained in:
86
src/func.typ
Normal file
86
src/func.typ
Normal file
@ -0,0 +1,86 @@
|
||||
#import "@preview/cetz:0.2.2": coordinate
|
||||
|
||||
#let val(value, alignment) = {
|
||||
return (
|
||||
type: "value",
|
||||
value: value,
|
||||
alignment: alignment
|
||||
)
|
||||
}
|
||||
|
||||
#let disc(eod: false, ..args) = {
|
||||
let pargs = args.pos()
|
||||
let left = pargs.at(0, default: (none, none))
|
||||
let right = pargs.at(1, default: (none, none))
|
||||
let center = pargs.at(2, default: (none, none))
|
||||
|
||||
let kwargs = args.named()
|
||||
if "left" in kwargs { left = kwargs.left }
|
||||
if "right" in kwargs { right = kwargs.right }
|
||||
if "center" in kwargs { center = kwargs.center }
|
||||
|
||||
let (value-l, alignment-l) = left
|
||||
let (value-r, alignment-r) = right
|
||||
let (value-c, alignment-c) = center
|
||||
return (
|
||||
type: "discontinuity",
|
||||
left: (
|
||||
value: value-l,
|
||||
alignment: alignment-l,
|
||||
),
|
||||
right: (
|
||||
value: value-r,
|
||||
alignment: alignment-r,
|
||||
),
|
||||
center: (
|
||||
value: value-c,
|
||||
alignment: alignment-c,
|
||||
),
|
||||
eod: eod
|
||||
)
|
||||
}
|
||||
|
||||
#let inter(value, start, end, i: auto) = {
|
||||
let i = i
|
||||
if i == auto {
|
||||
i = calc.floor( (start + end) / 2 )
|
||||
}
|
||||
return (
|
||||
type: "intermediate",
|
||||
value: value,
|
||||
start: start,
|
||||
end: end,
|
||||
i: i,
|
||||
)
|
||||
}
|
||||
|
||||
#let get-anchor(
|
||||
ctx,
|
||||
line-i,
|
||||
i,
|
||||
alignment,
|
||||
side,
|
||||
dx: .1,
|
||||
dy: .1
|
||||
) = {
|
||||
let (_, anchor) = coordinate.resolve(ctx, "anchor-" + str(i))
|
||||
let (_, header-t) = coordinate.resolve(ctx, "header-" + str(line-i) + ".north")
|
||||
let (_, header-b) = coordinate.resolve(ctx, "header-" + str(line-i) + ".south")
|
||||
|
||||
let x = anchor.at(0)
|
||||
if side == "left" {
|
||||
x -= dx
|
||||
} else if side == "right" {
|
||||
x += dx
|
||||
}
|
||||
|
||||
let yt = header-t.at(1)
|
||||
let yb = header-b.at(1)
|
||||
let y = (yt + yb) / 2
|
||||
if alignment == "top" {
|
||||
y = yt - dy
|
||||
} else if alignment == "bottom" {
|
||||
y = yb + dy
|
||||
}
|
||||
return (x, y)
|
||||
}
|
5
src/lib.typ
Normal file
5
src/lib.typ
Normal file
@ -0,0 +1,5 @@
|
||||
#let version = version((0,0,1))
|
||||
|
||||
#import "table.typ": sign-table, sign-line, func-line, convex-line
|
||||
#import "func.typ": *
|
||||
#import "utils.typ": *
|
325
src/renderer.typ
Normal file
325
src/renderer.typ
Normal file
@ -0,0 +1,325 @@
|
||||
#import "@preview/cetz:0.2.2": draw, vector, coordinate
|
||||
#import "@preview/modpattern:0.1.0": modpattern
|
||||
#import "func.typ"
|
||||
|
||||
#let stripes = modpattern((4pt, 4pt))[
|
||||
#line(
|
||||
start: (-50%, -50%),
|
||||
end: (50%, 50%),
|
||||
stroke: (
|
||||
paint: black,
|
||||
thickness: .5pt,
|
||||
cap: "square"
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
#let _draw-arrow(i) = {
|
||||
if i == 0 {
|
||||
return
|
||||
}
|
||||
draw.line(
|
||||
"value-" + str(i - 1),
|
||||
"value-" + str(i),
|
||||
mark: (
|
||||
end: ">",
|
||||
fill: black,
|
||||
scale: .5
|
||||
),
|
||||
stroke: black + .5pt
|
||||
)
|
||||
}
|
||||
|
||||
#let render-anchors(styles, ctx, line) = {
|
||||
for (i, value) in line.anchors.enumerate() {
|
||||
draw.content(
|
||||
name: "anchor-" + str(i),
|
||||
(v => vector.add(v, (styles.espcl * i + styles.deltacl, 0, 0)), "header-0.east"),
|
||||
anchor: "center",
|
||||
value
|
||||
)
|
||||
}
|
||||
let width = styles.espcl * (line.anchors.len() - 1) + 2 * styles.deltacl
|
||||
draw.rect(
|
||||
name: "anchors",
|
||||
"header-0.north-east",
|
||||
(
|
||||
v => vector.add(v, (width, 0, 0)),
|
||||
"header-0.south-east"
|
||||
),
|
||||
stroke: black + .5pt
|
||||
)
|
||||
}
|
||||
#let render-signs(styles, ctx, line, line-i) = {
|
||||
let header = "header-" + str(line-i)
|
||||
let (_, ht) = coordinate.resolve(ctx, header + ".north")
|
||||
let (_, hb) = coordinate.resolve(ctx, header + ".south")
|
||||
let yt = ht.at(1)
|
||||
let yb = hb.at(1)
|
||||
|
||||
for (i, value) in line.values.enumerate() {
|
||||
// Value
|
||||
if calc.rem(i, 2) == 1 {
|
||||
let (_, a0) = coordinate.resolve(ctx, "anchor-" + str((i - 1) / 2))
|
||||
let (_, a1) = coordinate.resolve(ctx, "anchor-" + str((i + 1) / 2))
|
||||
let x0 = a0.at(0)
|
||||
let x1 = a1.at(0)
|
||||
|
||||
if value == "h" {
|
||||
if line.values.at(i - 1) == "d" {
|
||||
x0 += 0.05
|
||||
}
|
||||
if line.values.at(i + 1, default: none) == "d" {
|
||||
x1 -= 0.05
|
||||
}
|
||||
draw.rect(
|
||||
(x0, yt),
|
||||
(x1, yb),
|
||||
fill: stripes,
|
||||
stroke: none
|
||||
)
|
||||
} else {
|
||||
draw.content(
|
||||
((x0 + x1) / 2, (yt + yb) / 2),
|
||||
anchor: "center",
|
||||
value
|
||||
)
|
||||
}
|
||||
|
||||
// Anchor
|
||||
} else {
|
||||
let (_, a) = coordinate.resolve(ctx, "anchor-" + str(i / 2))
|
||||
let x = a.at(0)
|
||||
if value == "d" {
|
||||
draw.line(
|
||||
(x - .05, yt),
|
||||
(x - .05, yb),
|
||||
stroke: black + .5pt
|
||||
)
|
||||
draw.line(
|
||||
(x + .05, yt),
|
||||
(x + .05, yb),
|
||||
stroke: black + .5pt
|
||||
)
|
||||
} else if value == "t" or value == "z" {
|
||||
draw.line(
|
||||
(x, yt),
|
||||
(x, yb),
|
||||
stroke: (
|
||||
paint: black,
|
||||
thickness: .5pt,
|
||||
dash: "dotted"
|
||||
)
|
||||
)
|
||||
|
||||
if value == "z" {
|
||||
draw.content(
|
||||
(x, (yt + yb) / 2),
|
||||
anchor: "center",
|
||||
$0$
|
||||
)
|
||||
}
|
||||
} else {
|
||||
draw.content(
|
||||
(x, (yt + yb) / 2),
|
||||
anchor: "center",
|
||||
value
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
draw.line(
|
||||
header + ".south-east",
|
||||
(vertical: (), horizontal: "anchors.east"),
|
||||
stroke: black + .5pt
|
||||
)
|
||||
}
|
||||
|
||||
#let render-func(styles, ctx, line, line-i) = {
|
||||
let header = "header-" + str(line-i)
|
||||
let ood = false
|
||||
let value-i = -1
|
||||
|
||||
for (i, value) in line.values.enumerate() {
|
||||
if value == none {
|
||||
continue
|
||||
}
|
||||
|
||||
if value.type == "value" {
|
||||
let pos = func.get-anchor(ctx, line-i, i, value.alignment, "center")
|
||||
value-i += 1
|
||||
draw.content(
|
||||
name: "value-" + str(value-i),
|
||||
pos,
|
||||
anchor: if value.alignment == "top" {"north"} else {"south"},
|
||||
value.value,
|
||||
frame: "rect",
|
||||
padding: 3pt,
|
||||
stroke: none
|
||||
)
|
||||
if not ood and i != 0 {
|
||||
_draw-arrow(value-i)
|
||||
}
|
||||
} else if value.type == "intermediate" {
|
||||
let start = "value-" + str(value.start)
|
||||
let end = "value-" + str(value.end)
|
||||
let top = func.get-anchor(ctx, line-i, value.i, "top", "center", dy: 0)
|
||||
let bottom = func.get-anchor(ctx, line-i, value.i, "bottom", "center", dy: 0)
|
||||
let inter-name = "inter-" + str(i)
|
||||
draw.intersections(inter-name, {
|
||||
draw.hide(draw.line(start, end))
|
||||
draw.hide(draw.line(top, bottom))
|
||||
})
|
||||
draw.content(
|
||||
inter-name + ".0",
|
||||
anchor: "center",
|
||||
value.value,
|
||||
frame: "rect",
|
||||
padding: 3pt,
|
||||
stroke: none,
|
||||
fill: white
|
||||
)
|
||||
|
||||
} else if value.type == "discontinuity" {
|
||||
let p0 = func.get-anchor(ctx, line-i, i, "top", "left", dx: 0.05, dy: 0)
|
||||
let p1 = func.get-anchor(ctx, line-i, i, "bottom", "left", dx: 0.05, dy: 0)
|
||||
let p2 = func.get-anchor(ctx, line-i, i, "top", "right", dx: 0.05, dy: 0)
|
||||
let p3 = func.get-anchor(ctx, line-i, i, "bottom", "right", dx: 0.05, dy: 0)
|
||||
|
||||
if not ood or not value.eod {
|
||||
draw.line(p0, p1, stroke: black + .5pt)
|
||||
draw.line(p2, p3, stroke: black + .5pt)
|
||||
}/* else {
|
||||
draw.rect(
|
||||
p0, p3, fill: stripes, stroke: none
|
||||
)
|
||||
}*/
|
||||
|
||||
if value.eod {
|
||||
// Only offset stripes if bound by double lines, otherwise
|
||||
// go until center of anchor
|
||||
let q0 = if not ood {p2} else {
|
||||
func.get-anchor(ctx, line-i, i, "top", "center", dy: 0)
|
||||
}
|
||||
let next = line.values.at(i + 1, default: none)
|
||||
let q1 = func.get-anchor(
|
||||
ctx,
|
||||
line-i,
|
||||
i + 1,
|
||||
"bottom",
|
||||
if next == none or not next.eod {"left"} else {"center"},
|
||||
dx: 0.05,
|
||||
dy: 0
|
||||
)
|
||||
|
||||
draw.rect(
|
||||
q0,
|
||||
q1,
|
||||
fill: stripes,
|
||||
stroke: none
|
||||
)
|
||||
}
|
||||
let anchors = (
|
||||
top: ("north-east", "north", "north-west"),
|
||||
center: ("east", "center", "west"),
|
||||
bottom: ("south-east", "south", "south-west")
|
||||
)
|
||||
if value.left.value != none {
|
||||
let p = func.get-anchor(ctx, line-i, i, value.left.alignment, "left")
|
||||
value-i += 1
|
||||
draw.content(
|
||||
name: "value-" + str(value-i),
|
||||
p,
|
||||
anchor: anchors.at(value.left.alignment).at(0),
|
||||
value.left.value,
|
||||
frame: "rect",
|
||||
fill: white,
|
||||
stroke: none,
|
||||
padding: 3pt
|
||||
)
|
||||
if not ood {
|
||||
_draw-arrow(value-i)
|
||||
}
|
||||
}
|
||||
if value.center.value != none {
|
||||
let p = func.get-anchor(ctx, line-i, i, value.center.alignment, "center")
|
||||
value-i += 1
|
||||
draw.content(
|
||||
name: "value-" + str(value-i),
|
||||
p,
|
||||
anchor: anchors.at(value.center.alignment).at(1),
|
||||
value.center.value,
|
||||
frame: "rect",
|
||||
fill: white,
|
||||
stroke: none,
|
||||
padding: 3pt
|
||||
)
|
||||
if not ood {
|
||||
_draw-arrow(value-i)
|
||||
}
|
||||
}
|
||||
if value.right.value != none {
|
||||
let p = func.get-anchor(ctx, line-i, i, value.right.alignment, "right")
|
||||
value-i += 1
|
||||
draw.content(
|
||||
name: "value-" + str(value-i),
|
||||
p,
|
||||
anchor: anchors.at(value.right.alignment).at(2),
|
||||
value.right.value,
|
||||
frame: "rect",
|
||||
fill: white,
|
||||
stroke: none,
|
||||
padding: 3pt
|
||||
)
|
||||
}
|
||||
if value.eod {
|
||||
ood = true
|
||||
} else {
|
||||
ood = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw.line(
|
||||
header + ".south-east",
|
||||
(vertical: (), horizontal: "anchors.east"),
|
||||
stroke: black + .5pt
|
||||
)
|
||||
}
|
||||
|
||||
#let render-convex(styles, ctx, line, line-i) = {
|
||||
let header = "header-" + str(line-i)
|
||||
|
||||
let (_, ht) = coordinate.resolve(ctx, header + ".north")
|
||||
let (_, hb) = coordinate.resolve(ctx, header + ".south")
|
||||
let yt = ht.at(1)
|
||||
let yb = hb.at(1)
|
||||
|
||||
for (i, value) in line.values.enumerate() {
|
||||
if value == none {
|
||||
continue
|
||||
}
|
||||
|
||||
// Value
|
||||
if calc.rem(i, 2) == 1 {
|
||||
let alignment = if value { "top" } else {"bottom"}
|
||||
let start = func.get-anchor(ctx, line-i, (i - 1) / 2, alignment, "right")
|
||||
let end = func.get-anchor(ctx, line-i, (i + 1) / 2, alignment, "left")
|
||||
let center = ((start.at(0) + end.at(0)) / 2, (yt + yb) / 2)
|
||||
draw.arc-through(start, center, end, stroke: black + .5pt)
|
||||
|
||||
// Anchor
|
||||
} else {
|
||||
let p = func.get-anchor(ctx, line-i, i / 2, "center", "center")
|
||||
let v = if value == "AV" [A.V] else if value == "PI" [P.I]
|
||||
|
||||
draw.content(p, anchor: "center", v)
|
||||
}
|
||||
}
|
||||
|
||||
draw.line(
|
||||
header + ".south-east",
|
||||
(vertical: (), horizontal: "anchors.east"),
|
||||
stroke: black + .5pt
|
||||
)
|
||||
}
|
135
src/table.typ
Normal file
135
src/table.typ
Normal file
@ -0,0 +1,135 @@
|
||||
#import "@preview/cetz:0.2.2": canvas, draw
|
||||
#import "renderer.typ": *
|
||||
|
||||
#let anchor-line(
|
||||
name,
|
||||
anchors
|
||||
) = {
|
||||
return ((
|
||||
type: "anchors",
|
||||
name: name,
|
||||
inset: 1em,
|
||||
anchors: anchors,
|
||||
),)
|
||||
}
|
||||
|
||||
#let sign-line(
|
||||
name,
|
||||
values
|
||||
) = {
|
||||
return ((
|
||||
type: "sign",
|
||||
name: name,
|
||||
inset: 1em,
|
||||
values: values,
|
||||
),)
|
||||
}
|
||||
|
||||
#let func-line(
|
||||
name,
|
||||
values,
|
||||
mult: 1
|
||||
) = {
|
||||
return ((
|
||||
type: "func",
|
||||
name: name,
|
||||
inset: (
|
||||
top: 1em * mult,
|
||||
bottom: 1em * mult,
|
||||
left: 1em,
|
||||
right: 1em
|
||||
),
|
||||
values: values,
|
||||
),)
|
||||
}
|
||||
|
||||
#let convex-line(
|
||||
name,
|
||||
values,
|
||||
mult: 1
|
||||
) = {
|
||||
return ((
|
||||
type: "convex",
|
||||
name: name,
|
||||
inset: (
|
||||
top: 1em * mult,
|
||||
bottom: 1em * mult,
|
||||
left: 1em,
|
||||
right: 1em
|
||||
),
|
||||
values: values,
|
||||
),)
|
||||
}
|
||||
|
||||
#let sign-table(
|
||||
anchors,
|
||||
lines,
|
||||
var: $x$,
|
||||
espcl: 1,
|
||||
deltacl: 1,
|
||||
) = context {
|
||||
let lines = anchor-line(var, anchors) + lines
|
||||
let name-sizes = lines.map(l => measure(box(l.name, inset: l.inset)))
|
||||
let headers-width = calc.max(..name-sizes.map(s => s.width))
|
||||
let headers-cx = headers-width / 2
|
||||
let styles = (
|
||||
espcl: espcl,
|
||||
deltacl: deltacl
|
||||
)
|
||||
|
||||
let render-anchors = render-anchors.with(styles)
|
||||
let render-signs = render-signs.with(styles)
|
||||
let render-func = render-func.with(styles)
|
||||
let render-convex = render-convex.with(styles)
|
||||
|
||||
canvas({
|
||||
/*draw.group(
|
||||
name: "headers",
|
||||
{
|
||||
}
|
||||
)*/
|
||||
for (i, line) in lines.enumerate() {
|
||||
let pos = if i == 0 {
|
||||
(headers-cx, 0)
|
||||
} else {
|
||||
"header-" + str(i - 1) + ".south"
|
||||
}
|
||||
let anchor-name = "header-" + str(i)
|
||||
draw.content(
|
||||
name: anchor-name,
|
||||
anchor: "north",
|
||||
padding: line.inset,
|
||||
pos,
|
||||
line.name
|
||||
)
|
||||
draw.rect(
|
||||
name: anchor-name,
|
||||
(v => (0, v.at(1), 0), anchor-name + ".north-west"),
|
||||
(v => (headers-width, v.at(1), 0), anchor-name + ".south-east"),
|
||||
stroke: black + .5pt
|
||||
)
|
||||
}
|
||||
for (i, line) in lines.enumerate() {
|
||||
draw.get-ctx(ctx => {
|
||||
if line.type == "anchors" {
|
||||
render-anchors(ctx, line)
|
||||
} else if line.type == "sign" {
|
||||
render-signs(ctx, line, i)
|
||||
} else if line.type == "func" {
|
||||
render-func(ctx, line, i)
|
||||
} else if line.type == "convex" {
|
||||
render-convex(ctx, line, i)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
draw.line(
|
||||
"anchors.south-east",
|
||||
(
|
||||
horizontal: (),
|
||||
vertical: "header-" + str(lines.len() - 1) + ".south-east"
|
||||
),
|
||||
stroke: black + .5pt
|
||||
)
|
||||
})
|
||||
}
|
5
src/utils.typ
Normal file
5
src/utils.typ
Normal file
@ -0,0 +1,5 @@
|
||||
#let asymp(value) = box(
|
||||
inset: .5em,
|
||||
stroke: black + .5pt,
|
||||
value
|
||||
)
|
Reference in New Issue
Block a user