15 Commits

Author SHA1 Message Date
26d11301a2 added range names and bit ranks 2025-11-21 18:08:59 +01:00
aeb413abb4 added bit separators 2025-11-21 16:50:46 +01:00
6095b5784e reimplemented struct frame rendering 2025-11-21 15:06:51 +01:00
6f13df815f documented start property of structures 2025-11-10 21:05:28 +01:00
1dd66fd587 Merge pull request 'Support for non-consecutive ranges' (#13) from rajayonin/rivet-typst:non-consecutive-ranges into dev
Reviewed-on: #13
Reviewed-by: Louis Heredero <louis@herdac.ch>
2025-10-21 06:02:04 +00:00
c7c777f5fa ensure first and last bits are included when all-bit-i is set 2025-10-20 19:06:53 +02:00
7ef7f653b7 support for non-consecutive ranges
Paints the start/end line of a range if there was no previous range.
2025-10-20 18:01:28 +02:00
dd6d38a282 fixed README example usage 2025-05-03 00:54:38 +02:00
6140cff3c5 updated CHANGELOG.md 2025-05-03 00:39:03 +02:00
1be1dccb6b bumped version in README 2025-05-03 00:35:35 +02:00
422681ba6e Merge pull request 'Left to right bits + Loading manual section' (#6) from feat/left-to-right-bits-1 into dev
Reviewed-on: #6
2025-04-15 17:45:51 +00:00
23af042a36 completed CHANGELOG 2025-04-15 19:37:29 +02:00
c7d12bf6c6 bumped to 0.3.0 + completed manual 2025-04-15 19:32:05 +02:00
6394c8e5c5 updated manual 2025-04-15 18:30:38 +02:00
65d11fc920 added ltr-bits config option 2025-04-15 18:19:51 +02:00
19 changed files with 266 additions and 552 deletions

View File

@@ -1,5 +1,13 @@
# Changelog
## [v0.3.0] - 2025-05-03
- updated CeTZ to 0.3.4
- updated to Typst 0.13.1
- updated Tidy to 0.4.2
- updated Codly to 1.3.0 and codly-languages to 0.1.8
- added `ltr-bits` config option
- added a "Loading" section to the manual
## [v0.2.0] - 2025-02-23
- updated CeTZ to 0.3.2
- updated to Typst 0.13.0

View File

@@ -35,7 +35,7 @@ For more information, see the [manual](manual.pdf)
To use this package, simply import `schema` from [rivet](https://typst.app/universe/package/rivet) and call `schema.load` to parse a schema description. Then use `schema.render` to render it, et voilà !
```typ
#import "@preview/rivet:0.2.0": schema
#let doc = schema.load("path/to/schema.yaml")
#import "@preview/rivet:0.3.0": schema
#let doc = schema.load(yaml("path/to/schema.yaml"))
#schema.render(doc)
```

View File

@@ -25,6 +25,7 @@
/// - height (float): TODO -> remove
/// - full-page (bool): If true, the page will be resized to fit the diagram and take the background color
/// - all-bit-i (bool): If true, all bit indices will be rendered, otherwise, only the ends of each range will be displayed
/// - ltr-bits (bool): If true, bits are placed with the LSB on the left instead of the right
/// -> dictionary
#let config(
default-font-family: "Ubuntu Mono",
@@ -51,7 +52,8 @@
width: 1200,
height: 800,
full-page: false,
all-bit-i: true
all-bit-i: true,
ltr-bits: false
) = {}
/// Dark theme config

View File

@@ -1,5 +1,6 @@
/// Loads a schema from a file or a raw block.
/// This function returns a dictionary of structures
/// This function returns a dictionary of structures\
/// See the #link(<loading>)[Loading] chapter for examples of schema loading for each supported format
///
/// Supported formats: #schema.valid-extensions.map(e => raw("." + e)).join(", ")
/// - path-or-schema (str, raw, dictionary):
@@ -12,7 +13,6 @@
#let load(path-or-schema) = {}
/// Renders the given schema
/// This functions
/// - schema (dictionary): A schema dictionary, as returned by @@load()
/// - config (auto, dictionary): The configuration parameters, as returned by #doc-ref("config.config")
/// - width (ratio, length): The width of the generated figure

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 KiB

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#import "@preview/tidy:0.4.1"
#import "@preview/codly:1.2.0": codly-init, codly
#import "@preview/codly-languages:0.1.7": codly-languages
#import "@preview/tidy:0.4.2"
#import "@preview/codly:1.3.0": codly-init, codly
#import "@preview/codly-languages:0.1.8": codly-languages
#import "@preview/showybox:2.0.4": showybox
#import "src/lib.typ"
#import "src/schema.typ"
@@ -84,22 +84,26 @@ This is a port of the #link("https://git.kb28.ch/HEL/rivet")[homonymous Python s
= Usage
Simply import `schema` from #link("src/lib.typ") and call `schema.load` to parse a schema description. Then use `schema.render` to render it, et voilà !
#pad(left: 1em)[```typ
#import "@preview/rivet:0.1.0": schema
#let doc = schema.load("path/to/schema.yaml")
#let import-stmt = "#import \"@preview/rivet:" + str(lib.version) + "\""
Simply import the `schema` module and call `schema.load` to parse a schema description. Then use `schema.render` to render it, et voilà !
#raw(block: true, lang: "typ", ```typ
$import: schema
#let doc = schema.load(yaml("path/to/schema.yaml"))
#schema.render(doc)
```]
```.text.replace("$import", import-stmt))
Please read the #link(<loading>)[Loading] chapter for more detailed explanations on how to load schema descriptions.
= Format
This section describes the structure of a schema definition. The examples given use the JSON syntax. For examples in different formats, see #link("https://git.kb28.ch/HEL/rivet-typst/src/branch/main/gallery/test.yaml")[test.yaml], #link("https://git.kb28.ch/HEL/rivet-typst/src/branch/main/gallery/test.json")[test.json] and #link("https://git.kb28.ch/HEL/rivet-typst/src/branch/main/gallery/test.xml")[test.xml]. You can also directly define a schema using Typst dictionaries and arrays.
Since the XML format is quite different from the other, you might find it helpful to look at the examples on GitHub to get familiar with it.
Since the XML format is quite different from the other, you might find it helpful to look at the examples in the #link("https://git.kb28.ch/HEL/rivet-typst/src/branch/main/gallery/")[Gitea repo] to get familiar with it.
== Main layout
A schema contains a dictionary of structures. The must be at least one defined structure named "main".
A schema contains a dictionary of structures. There must be at least one defined structure named "main".
It can also optionnaly contain a "colors" dictionary. More details about this in #link(<format-colors>)[Colors]
@@ -126,7 +130,7 @@ It can also optionnaly contain a "colors" dictionary. More details about this in
A structure has a given number of bits and one or multiple ranges. Each range of bits can have a name, a description and / or values with special meaning (see #link(<format-range>)[Range]). A range's structure can also depend on another range's value (see #link(<format-dependencies>)[Dependencies]).
The range name (or key) defines the left- and rightmost bits (e.g. `7-4` goes from bit 7 down to bit 4). Bits are displayed in big-endian, i.e. the leftmost bit has the highest value.
The range name (or key) defines the left- and rightmost bits (e.g. `7-4` goes from bit 7 down to bit 4). The order in which you write the range is not important, meaning `7-4` is equivalent to `4-7`. Bits are displayed in big-endian, i.e. the leftmost bit has the highest value, except if you enable the `ltr-bits` #doc-ref("config.config") option.
```json
"main": {
@@ -148,6 +152,34 @@ The range name (or key) defines the left- and rightmost bits (e.g. `7-4` goes fr
}
```
=== Start <format-start>
By default, structures start at bit 0, but you may want to number bits from 1, or another arbitrary value. To do this, you can set the `start` property of a structure to the desired start value. For example,
```json
"main": {
"bits": 8,
"start": 4,
"ranges": {
"11-7": { ... },
"6-4": { ... }
}
}
```
#let start-schema = (structures: (main: (bits: 8, start: 4, ranges: ("11-7": (name: ""), "6-4": (name: "")))))
would render as
#align(
center,
schema.render(
schema.load(start-schema),
width: 50%
)
)
== Range <format-range>
A range represents a group of consecutive bits. It can have a name (displayed in the bit cells), a description (displayed under the structure) and / or values.
@@ -177,7 +209,7 @@ For values depending on other ranges, see #link(<format-dependencies>)[Dependenc
The structure of one range may depend on the value of another. To represent this situation, first indicate on the child range the range on which it depends.
Then, in its values, indicate which structure to use. A description can also be added (displayed above the horizontal dependency arrow)
Then, in its values, indicate which structure to use. A description can also be added (displayed below the horizontal dependency arrow)
```json
"7-4": {
@@ -270,6 +302,81 @@ Valid color formats are:
#pagebreak(weak: true)
= Loading <loading>
Due to current limitations of the Typst compiler, the package can only access its own files, unless directly included in your project. For this reason, rivet cannot load a schema from a path, and you will need to read the files yourself to pass their contents to the package.
Here are a number of ways you can load your schemas:
== JSON Format
````typ
// From file (ONLY IF PACKAGE INSTALLED IN PROJECT)
#let s = schema.load("schema.json")
// From file
#let s = schema.load(json("schema.json"))
// Raw block
#let s = schema.load(```json
{
"structures": {
"main": {
...
}
}
}
```)
````
== YAML Format
````typ
// From file (ONLY IF PACKAGE INSTALLED IN PROJECT)
#let s = schema.load("schema.yaml")
// From file
#let s = schema.load(yaml("schema.yaml"))
// Raw block
#let s = schema.load(```yaml
structures:
main:
...
```)
````
== Typst Format
```typ
#let s = schema.load((
structures: (
main: (
...
)
)
))
```
#pagebreak(weak: true)
== XML Format
````typ
// From file (ONLY IF PACKAGE INSTALLED IN PROJECT)
#let x = schema.xml-loader.load("schema.xml")
#let s = schema.load(x)
// From file
#let x = schema.xml-loader.parse(yaml("schema.yaml").first())
#let s = schema.load(x)
// Raw block
#let s = schema.load(```xml
<schema>
<structure id="main" bits="32">
...
</structure>
</schema>
```)
````
#pagebreak(weak: true)
= Config presets
Aside from the default config, some example presets are also provided:

1
src/cetz.typ Normal file
View File

@@ -0,0 +1 @@
#import "@preview/cetz:0.3.4": *

View File

@@ -23,7 +23,8 @@
width: 1200,
height: 800,
full-page: false,
all-bit-i: true
all-bit-i: true,
ltr-bits: false,
) = {
return (
default-font-family: default-font-family,
@@ -50,7 +51,8 @@
width: width,
height: height,
full-page: full-page,
all-bit-i: all-bit-i
all-bit-i: all-bit-i,
ltr-bits: ltr-bits,
)
}

View File

@@ -1,4 +1,4 @@
#let version = version(0,2,0)
#let version = version(0,3,0)
#import "config.typ"
#import "schema.typ"

View File

@@ -15,6 +15,9 @@
}
let start = int(start-end.last())
let end = int(start-end.first())
if end < start {
(start, end) = (end, start)
}
return (start, end)
}

116
src/render/structure.typ Normal file
View File

@@ -0,0 +1,116 @@
#import "../cetz.typ": draw
#let BACKGROUND-LAYER = -1
#let draw-track(config, schema, struct, pos) = draw.group(name: "track", {
let colors = schema.at("colors", default: (:))
let total-width = struct.bits * config.bit-width
let bit-colors = (:)
if struct.name in colors {
for (s, col) in colors.at(struct.name) {
let (start, end) = rng.parse-span(s)
for i in range(start, end + 1) {
bit-colors.insert(str(i), col)
}
}
}
let boundaries = ()
for r in struct.ranges.values() {
boundaries.push(if config.ltr-bits {r.end} else {r.start})
boundaries.push(if config.ltr-bits {r.start - 1} else {r.end + 1})
}
draw.group(name: "bits", {
for i in range(struct.bits) {
let rank = struct.start + i
let j = if config.ltr-bits {i} else {struct.bits - i - 1}
let col = bit-colors.at(str(rank), default: config.background)
// Background color
draw.on-layer(BACKGROUND-LAYER, {
draw.rect(
(rel: (j * config.bit-width, 0), to: pos),
(rel: (config.bit-width, config.bit-height)),
fill: col,
stroke: none,
name: str(rank)
)
})
if j != struct.bits - 1 {
let top = str(rank) + ".north-east"
let bottom = str(rank) + ".south-east"
// Group boundary
if rank in boundaries {
draw.line(
top,
bottom,
stroke: config.border-color
)
// Bit separator
} else {
draw.line(
top,
((), 20%, bottom),
stroke: config.border-color
)
draw.line(
bottom,
((), 20%, top),
stroke: config.border-color
)
}
}
}
})
// Frame
draw.rect(
"bits.north-west", "bits.south-east",
name: "frame",
stroke: config.border-color + 2pt
)
// Names
for r in struct.ranges.values() {
let i0 = r.start
let i1 = r.end
if not config.ltr-bits {
(i0, i1) = (i1, i0)
}
let start = "bits." + str(i0) + ".north-west"
let end = "bits." + str(i1) + ".south-east"
draw.content(
(start, 50%, end),
r.name
)
}
let indices = if config.all-bit-i {
range(struct.start, struct.start + struct.bits)
} else {
struct.ranges.values().map(r => (r.start, r.end)).flatten().dedup()
}
for i in indices {
draw.content(
"bits." + str(i) + ".north",
anchor: "south",
str(i),
padding: 4pt // TODO: Maybe configurable
)
}
})
#let render(
config,
schema,
struct,
pos: (0, 0),
) = draw.group(name: struct.name, {
draw-track(config, schema, struct, pos)
})

View File

@@ -1,535 +1,10 @@
#import "@preview/cetz:0.3.2": canvas, draw
#import "range.typ" as rng
#import "structure.typ"
#import "vec.typ"
#let draw-rect(color, x, y, width, height, thickness: 0) = {
let fill = none
let stroke = color + thickness * 1pt
if thickness == 0 {
fill = color
stroke = none
}
draw.rect((x, -y), (x + width, -y - height), fill: fill, stroke: stroke)
}
#let draw-text(
txt,
color,
x,
y,
anchor: "center",
font: none,
italic: false,
size: 1em,
fill: none
) = {
let text-params = (:)
if font != none {
text-params.insert("font", font)
}
if italic {
text-params.insert("style", "italic")
}
draw.content(
(x, -y),
text(txt, fill: color, size: size, ..text-params),
anchor: anchor,
stroke: none
)
}
#let draw-line(color, a, b) = {
let (x0, y0) = a
let (x1, y1) = b
draw.line((x0, -y0), (x1, -y1), stroke: color)
}
#let draw-lines(color, ..pts) = {
let pts = pts.pos().map(pt => (pt.at(0), -pt.at(1)))
draw.line(..pts, stroke: color)
}
#let draw-poly(color, ..pts, thickness: 0) = {
let pts = pts.pos().map(pt => (pt.at(0), -pt.at(1)))
let params = (
stroke: (paint: color, thickness: thickness),
fill: none
)
if thickness == 0 {
params = (
stroke: none,
fill: color
)
}
draw.line(..pts, ..params)
}
#let draw-underbracket(config, start, end, bits-y) = {
let bit-w = config.bit-width
let bit-h = config.bit-height
let x0 = start + bit-w / 2
let x1 = end - bit-w / 2
let y0 = bits-y + bit-h * 1.25
let y1 = bits-y + bit-h * 1.5
let col = config.link-color
draw-lines(col, (x0, y0), (x0, y1), (x1, y1), (x1, y0))
}
#let draw-link(
config,
start-x,
start-y,
end-x,
end-y
) = {
let bit-h = config.bit-height
let arrow-margin = config.arrow-margin
if end-x > start-x {
end-x -= arrow-margin
} else {
end-x += arrow-margin
}
draw-lines(
config.link-color,
(start-x, start-y + bit-h * 1.5),
(start-x, end-y + bit-h / 2),
(end-x, end-y + bit-h / 2),
)
}
#let draw-values(config, values, desc-x, desc-y) = {
let shapes = ()
let txt-col = config.text-color
let bit-w = config.bit-height // Why ? I don't remember
let gap = config.values-gap
for (val, desc) in values.pairs().sorted(key: p => p.first()) {
desc-y += gap
let txt = val + " = " + desc
shapes += draw-text(
txt, txt-col, desc-x + bit-w / 2, desc-y,
anchor: "north-west",
font: config.italic-font-family,
italic: true,
size: config.italic-font-size
)
desc-y += config.italic-font-size / 1.2pt
}
return (shapes, desc-x, desc-y)
}
#let draw-description(
config,
range_,
start-x,
start-y,
width,
desc-x,
desc-y
) = {
let shapes = ()
let bit-w = config.bit-width
let bit-h = config.bit-height
if config.left-labels {
desc-x = calc.min(desc-x, start-x + width / 2 - bit-w)
} else {
desc-x = calc.max(desc-x, start-x + width / 2 + bit-w)
}
shapes += draw-underbracket(config, start-x, start-x + width, start-y)
let mid-x = start-x + width / 2
shapes += draw-link(config, mid-x, start-y, desc-x, desc-y)
let txt-x = desc-x
if config.left-labels {
txt-x -= range_.description.len() * config.default-font-size / 2pt
}
shapes += draw-text(
range_.description,
config.text-color,
txt-x, desc-y + bit-h / 2,
anchor: "west"
)
desc-y += config.default-font-size / 0.75pt
if range_.values != none and range_.depends-on == none {
let shapes_
(shapes_, _, desc-y) = draw-values(config, range_.values, txt-x, desc-y)
shapes += shapes_
}
desc-y += config.description-margin
return (shapes, desc-x, desc-y)
}
#let draw-arrow(config, start-x, start-y, end-x, end-y, label: "") = {
let shapes = ()
let dash-len = config.dash-length
let dash-space = config.dash-space
let arrow-size = config.arrow-size
let link-col = config.link-color
let txt-col = config.text-color
let arrow-label-dist = config.arrow-label-distance
let start = vec.vec(start-x, start-y)
let end = vec.vec(end-x, end-y)
let start-end = vec.sub(end, start)
let d = vec.normalize(start-end)
let dashes = int(vec.mag(start-end) / (dash-len + dash-space))
for i in range(dashes) {
let a = vec.add(
start,
vec.mul(d, i * (dash-len + dash-space))
)
let b = vec.add(
a,
vec.mul(d, dash-len)
)
shapes += draw-line(link-col, (a.x, a.y), (b.x, b.y))
}
let n = vec.vec(d.y, -d.x)
let width = arrow-size / 1.5
let p1 = vec.sub(
end,
vec.sub(
vec.mul(d, arrow-size),
vec.mul(n, width)
)
)
let p2 = vec.sub(
end,
vec.add(
vec.mul(d, arrow-size),
vec.mul(n, width)
)
)
shapes += draw-poly(
link-col,
(end.x, end.y),
(p1.x, p1.y),
(p2.x, p2.y)
)
if label != "" {
shapes += draw-text(
label,
txt-col,
(start.x + end.x) / 2,
(start.y + end.y) / 2 + arrow-label-dist,
anchor: "north"
)
}
return shapes
}
#let draw-dependency(
draw-struct, config,
struct, schema, bits-x, bits-y, range_, desc-x, desc-y
) = {
let shapes = ()
let bit-w = config.bit-width
let bit-h = config.bit-height
let arrow-margin = config.arrow-margin
let start-i = struct.bits - range_.end - 1
let start-x = bits-x + start-i * bit-w
let width = rng.bits(range_) * bit-w
shapes += draw-underbracket(config, start-x, start-x + width, bits-y)
let depend-key = rng.key(..range_.depends-on)
let depend-range = struct.ranges.at(depend-key)
let prev-range-y = bits-y + bit-h * 1.5
let prev-depend-y = if depend-range.last-value-y == -1 {
bits-y + bit-h * 1.5
} else {
depend-range.last-value-y
}
let depend-start-i = struct.bits - depend-range.end - 1
let depend-start-x = bits-x + depend-start-i * bit-w
let depend-width = rng.bits(depend-range) * bit-w
let depend-mid = depend-start-x + depend-width / 2
shapes += draw-underbracket(config, depend-start-x, depend-start-x + depend-width, bits-y)
for (val, data) in range_.values.pairs().sorted(key: p => p.first()) {
shapes += draw-arrow(config, depend-mid, prev-depend-y, depend-mid, desc-y - arrow-margin)
let val-ranges = (:)
for i in range(rng.bits(depend-range)) {
val-ranges.insert(
str(depend-range.end - i),
(name: val.at(i))
)
}
let val-struct = (
bits: rng.bits(depend-range),
start: depend-range.start,
ranges: val-ranges
)
val-struct = structure.load("", val-struct)
let shapes_
(shapes_, ..) = draw-struct(config, val-struct, schema, ox: depend-start-x, oy: desc-y)
shapes += shapes_
let y = desc-y + bit-h * 1.5
let x1
let x2
// Arrow from left to right
if depend-range.end > range_.start {
x1 = depend-start-x + depend-width + arrow-margin
x2 = start-x - arrow-margin
// Arrow from right to left
} else {
x1 = depend-start-x - arrow-margin
x2 = start-x + width + arrow-margin
}
shapes += draw-arrow(config, x1, y, x2, y, label: data.description)
shapes += draw-arrow(config,
start-x + width - bit-w,
prev-range-y,
start-x + width - bit-w,
desc-y + bit-h - arrow-margin
)
prev-depend-y = desc-y + bit-h * 2 + arrow-margin
prev-range-y = prev-depend-y
depend-range.last-value-y = prev-depend-y
(shapes_, desc-y) = draw-struct(config, schema.structures.at(data.structure), schema, ox: start-x, oy: desc-y)
shapes += shapes_
}
struct.ranges.at(depend-key) = depend-range
return (shapes, desc-x, desc-y, struct)
}
#let draw-structure(config, struct, schema, ox: 0, oy: 0) = {
let shapes
let colors = schema.at("colors", default: (:))
let bg-col = config.background
let txt-col = config.text-color
let border-col = config.border-color
let bit-w = config.bit-width
let bit-h = config.bit-height
let (bits-x, bits-y) = (ox, oy + bit-h)
let bits-width = struct.bits * bit-w
let start-bit = struct.start
let bit-colors = (:)
for i in range(struct.bits) {
bit-colors.insert(str(i), bg-col)
}
if struct.name in colors {
for (s, col) in colors.at(struct.name) {
let (start, end) = rng.parse-span(s)
for i in range(start, end + 1) {
let real-i = struct.bits - i - 1 + start-bit
bit-colors.insert(str(real-i), col)
}
}
}
let range-boundaries = ()
for r in struct.ranges.values() {
let i = struct.bits - r.end - 1 + start-bit
range-boundaries.push(i)
}
// Draw colors
for i in range(struct.bits) {
let bit-x = ox + i * bit-w
shapes += draw-rect(bit-colors.at(str(i)), bit-x, bits-y, bit-w+1, bit-h)
}
// Draw rectangle around structure
shapes += draw-rect(border-col, bits-x, bits-y, bits-width, bit-h, thickness: 2)
let indices = range(struct.start, struct.start + struct.bits)
if not config.all-bit-i {
indices = ()
for r in struct.ranges.values() {
indices.push(r.start)
indices.push(r.end)
}
}
for i in range(struct.bits) {
let bit-x = ox + i * bit-w
let real-i = struct.bits - i - 1 + start-bit
if real-i in indices {
shapes += draw-text(
str(real-i),
txt-col,
bit-x + bit-w / 2,
oy + bit-h / 2
)
}
// Draw separator
if i != 0 and not i in range-boundaries {
shapes += draw-line(border-col, (bit-x, bits-y), (bit-x, bits-y + bit-h * 0.2))
shapes += draw-line(border-col, (bit-x, bits-y + bit-h * 0.8), (bit-x, bits-y + bit-h))
}
}
let ranges = structure.get-sorted-ranges(struct)
if config.left-labels {
ranges = ranges.rev()
}
let desc-x
if config.force-descs-on-side {
desc-x = config.margins.at(3) + structures.main.bits * bit-w
if config.left-labels {
desc-x = config.width - desc-x
}
} else {
desc-x = ox
if config.left-labels {
desc-x += struct.bits * bit-w
}
}
let desc-y = bits-y + bit-h * 2
// Names + simple descriptions
for range_ in ranges {
let start-i = struct.bits - range_.end + start-bit - 1
let start-x = bits-x + start-i * bit-w
let width = rng.bits(range_) * bit-w
let name-x = start-x + width / 2
let name-y = bits-y + bit-h / 2
shapes += draw-line(border-col, (start-x, bits-y), (start-x, bits-y + bit-h))
shapes += draw-text(range_.name, txt-col, name-x, name-y, fill: bg-col)
if range_.description != "" {
let shapes_
(shapes_, desc-x, desc-y) = draw-description(
config, range_, start-x, bits-y, width, desc-x, desc-y
)
shapes += shapes_
}
}
// Dependencies
for range_ in ranges {
if range_.values() != none and range_.depends-on != none {
let shapes_
(shapes_, desc-x, desc-y, struct) = draw-dependency(
draw-structure, config,
struct, schema, bits-x, bits-y, range_, desc-x, desc-y,
)
shapes += shapes_
}
}
return (shapes, desc-y)
}
#import "cetz.typ": canvas
#import "render/structure.typ" as struct
#let render(config, schema, width: 100%) = {
set text(
font: config.default-font-family,
size: config.default-font-size
)
let main = schema.structures.main
let ox = config.margins.at(3)
if config.left-labels {
ox = config.width - ox - main.bits * config.bit-width
}
let params = if config.full-page {
(
width: auto,
height: auto,
fill: config.background,
margin: 0cm
)
} else {
(:)
}
set page(..params)
let cnvs = canvas(length: 1pt, background: config.background, {
let (shapes, _) = draw-structure(
config, main, schema,
ox: ox,
oy: config.margins.at(0)
)
// Workaround for margins
draw.group(name: "g", padding: config.margins, shapes)
draw.line(
"g.north-west",
"g.north-east",
"g.south-east",
"g.south-west",
stroke: none,
fill: none
)
canvas(length: 1pt, {
struct.render(config, schema, schema.structures.main)
})
if config.full-page {
cnvs
} else {
layout(size => {
let m = measure(cnvs)
let w = m.width
let h = m.height
let base-w = if type(width) == ratio {
size.width * width
} else {
width
}
let r = if w == 0 {
0
} else {
base-w / w
}
let new-w = w * r
let new-h = h * r
r *= 100%
box(
width: new-w,
height: new-h,
scale(x: r, y: r, cnvs, reflow: true)
)
})
}
}
#let make(config) = {
@@ -537,4 +12,4 @@
config: config,
render: render.with(config)
)
}
}

View File

@@ -1,7 +1,7 @@
[package]
name = "rivet"
version = "0.2.0"
compiler = "0.13.0"
version = "0.3.0"
compiler = "0.13.1"
repository = "https://git.kb28.ch/HEL/rivet-typst"
entrypoint = "src/lib.typ"
authors = [