initial commit

This commit is contained in:
2024-06-23 20:59:51 +02:00
commit 81ca0b8e32
10 changed files with 736 additions and 0 deletions

86
src/func.typ Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,5 @@
#let asymp(value) = box(
inset: .5em,
stroke: black + .5pt,
value
)