added parallel task with real / planned work
updated timeliney to use latest CeTZ
This commit is contained in:
parent
dff52e79ea
commit
2a31df7d50
BIN
example.pdf
BIN
example.pdf
Binary file not shown.
@ -59,6 +59,7 @@
|
|||||||
[Report / Presentation],
|
[Report / Presentation],
|
||||||
(date.start, date.finish, true),
|
(date.start, date.finish, true),
|
||||||
(datetime(year: 2024, month: 06, day: 24), 1),
|
(datetime(year: 2024, month: 06, day: 24), 1),
|
||||||
|
(datetime(year: 2024, month: 06, day: 20), 1.2, false, true),
|
||||||
(datetime(year: 2024, month: 08, day: 26), 1),
|
(datetime(year: 2024, month: 08, day: 26), 1),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -141,10 +141,11 @@
|
|||||||
// stroke: (paint: p, thickness: 4pt)
|
// stroke: (paint: p, thickness: 4pt)
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
} else if real{
|
} else if real {
|
||||||
complemented_task.push((
|
complemented_task.push((
|
||||||
from: weeks,
|
from: weeks,
|
||||||
to: end,
|
to: end,
|
||||||
|
real: true,
|
||||||
style: (
|
style: (
|
||||||
stroke: (
|
stroke: (
|
||||||
//dash: "dotted",
|
//dash: "dotted",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#import "@preview/cetz:0.1.2": canvas, draw
|
#import "@preview/cetz:0.2.2": canvas, draw, coordinate, util
|
||||||
|
|
||||||
#let timeline(
|
#let timeline(
|
||||||
body,
|
body,
|
||||||
@ -51,22 +51,22 @@
|
|||||||
content(
|
content(
|
||||||
(rel: (0, 0)),
|
(rel: (0, 0)),
|
||||||
task.name,
|
task.name,
|
||||||
anchor: "top",
|
anchor: "north",
|
||||||
name: "task" + str(i),
|
name: "task" + str(i),
|
||||||
padding: spacing,
|
padding: if task.parallel {(x: spacing, y: 2 * spacing)} else {spacing},
|
||||||
)
|
)
|
||||||
|
|
||||||
anchor(
|
anchor(
|
||||||
"task" + str(i) + "-bottom",
|
"task" + str(i) + "-bottom",
|
||||||
(rel: (0, 0), to: "task" + str(i) + ".bottom", update: true),
|
(rel: (0, 0), to: "task" + str(i) + ".south", update: true),
|
||||||
)
|
)
|
||||||
anchor(
|
anchor(
|
||||||
"task" + str(i) + "-top",
|
"task" + str(i) + "-top",
|
||||||
(rel: (0, 0), to: "task" + str(i) + ".top-left", update: false),
|
(rel: (0, 0), to: "task" + str(i) + ".north-west", update: false),
|
||||||
)
|
)
|
||||||
anchor(
|
anchor(
|
||||||
"task" + str(i),
|
"task" + str(i),
|
||||||
(rel: (0, 0), to: "task" + str(i) + ".right", update: false),
|
(rel: (0, 0), to: "task" + str(i) + ".east", update: false),
|
||||||
)
|
)
|
||||||
|
|
||||||
flat_tasks.push(task)
|
flat_tasks.push(task)
|
||||||
@ -77,22 +77,22 @@
|
|||||||
content(
|
content(
|
||||||
(rel: (0, 0)),
|
(rel: (0, 0)),
|
||||||
t.name,
|
t.name,
|
||||||
anchor: "top",
|
anchor: "north",
|
||||||
name: "task" + str(i),
|
name: "task" + str(i),
|
||||||
padding: spacing,
|
padding: spacing,
|
||||||
)
|
)
|
||||||
|
|
||||||
anchor(
|
anchor(
|
||||||
"task" + str(i) + "-bottom",
|
"task" + str(i) + "-bottom",
|
||||||
(rel: (0, 0), to: "task" + str(i) + ".bottom", update: true),
|
(rel: (0, 0), to: "task" + str(i) + ".south", update: true),
|
||||||
)
|
)
|
||||||
anchor(
|
anchor(
|
||||||
"task" + str(i) + "-top",
|
"task" + str(i) + "-top",
|
||||||
(rel: (0, 0), to: "task" + str(i) + ".top-left", update: false),
|
(rel: (0, 0), to: "task" + str(i) + ".north-west", update: false),
|
||||||
)
|
)
|
||||||
anchor(
|
anchor(
|
||||||
"task" + str(i),
|
"task" + str(i),
|
||||||
(rel: (0, 0), to: "task" + str(i) + ".right", update: false),
|
(rel: (0, 0), to: "task" + str(i) + ".east", update: false),
|
||||||
)
|
)
|
||||||
|
|
||||||
flat_tasks.push(t)
|
flat_tasks.push(t)
|
||||||
@ -107,22 +107,22 @@
|
|||||||
content(
|
content(
|
||||||
(rel: (0, 0)),
|
(rel: (0, 0)),
|
||||||
milestone.body,
|
milestone.body,
|
||||||
anchor: "top",
|
anchor: "north",
|
||||||
name: "milestone" + str(i),
|
name: "milestone" + str(i),
|
||||||
padding: spacing,
|
padding: spacing,
|
||||||
)
|
)
|
||||||
|
|
||||||
anchor(
|
anchor(
|
||||||
"milestone" + str(i) + "-bottom",
|
"milestone" + str(i) + "-bottom",
|
||||||
(rel: (0, 0), to: "milestone" + str(i) + ".bottom", update: true),
|
(rel: (0, 0), to: "milestone" + str(i) + ".south", update: true),
|
||||||
)
|
)
|
||||||
anchor(
|
anchor(
|
||||||
"milestone" + str(i) + "-right",
|
"milestone" + str(i) + "-right",
|
||||||
(rel: (0, 0), to: "milestone" + str(i) + ".right", update: false),
|
(rel: (0, 0), to: "milestone" + str(i) + ".east", update: false),
|
||||||
)
|
)
|
||||||
anchor(
|
anchor(
|
||||||
"milestone" + str(i) + "-top",
|
"milestone" + str(i) + "-top",
|
||||||
(rel: (0, 0), to: "milestone" + str(i) + ".top", update: false),
|
(rel: (0, 0), to: "milestone" + str(i) + ".north", update: false),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,7 +136,7 @@
|
|||||||
on-layer(
|
on-layer(
|
||||||
1,
|
1,
|
||||||
{
|
{
|
||||||
let (start_x, _, _) = coordinate.resolve(ctx, "titles.top-left")
|
let (_, (start_x,_ , _)) = coordinate.resolve(ctx, "titles.north-west")
|
||||||
let end_x = 1 + start_x
|
let end_x = 1 + start_x
|
||||||
|
|
||||||
let i = 0
|
let i = 0
|
||||||
@ -152,11 +152,11 @@
|
|||||||
|
|
||||||
for task in group.tasks {
|
for task in group.tasks {
|
||||||
if group_start == none {
|
if group_start == none {
|
||||||
let (_, start_y, _) = coordinate.resolve(ctx, "titles.task" + str(i) + "-top")
|
let (_, (_, start_y, _)) = coordinate.resolve(ctx, "titles.task" + str(i) + "-top")
|
||||||
group_start = (start_x, start_y)
|
group_start = (start_x, start_y)
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, end_y, _) = coordinate.resolve(ctx, "titles.task" + str(i) + "-bottom")
|
let (_, (_, end_y, _)) = coordinate.resolve(ctx, "titles.task" + str(i) + "-bottom")
|
||||||
group_end = (end_x, end_y)
|
group_end = (end_x, end_y)
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
@ -166,7 +166,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tasks-vline {
|
if tasks-vline {
|
||||||
line("titles.top-right", "titles.bottom-right")
|
line("titles.north-east", "titles.south-east")
|
||||||
}
|
}
|
||||||
|
|
||||||
if box-milestones and milestone-layout == "aligned" {
|
if box-milestones and milestone-layout == "aligned" {
|
||||||
@ -175,10 +175,10 @@
|
|||||||
|
|
||||||
for (i, milestone) in milestones.enumerate() {
|
for (i, milestone) in milestones.enumerate() {
|
||||||
if start == none {
|
if start == none {
|
||||||
let (_, start_y, _) = coordinate.resolve(ctx, "titles.milestone" + str(i) + "-top")
|
let (_, (_, start_y, _)) = coordinate.resolve(ctx, "titles.milestone" + str(i) + "-top")
|
||||||
start = (start_x, start_y)
|
start = (start_x, start_y)
|
||||||
}
|
}
|
||||||
let (_, end_y, _) = coordinate.resolve(ctx, "titles.milestone" + str(i) + "-bottom")
|
let (_, (_, end_y, _)) = coordinate.resolve(ctx, "titles.milestone" + str(i) + "-bottom")
|
||||||
end = (end_x, end_y)
|
end = (end_x, end_y)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,9 +192,9 @@
|
|||||||
|
|
||||||
get-ctx(
|
get-ctx(
|
||||||
ctx => {
|
ctx => {
|
||||||
let (start_x, start_y, _) = coordinate.resolve(ctx, "titles.top-right")
|
let (_, (start_x, start_y, _)) = coordinate.resolve(ctx, "titles.north-east")
|
||||||
let end_x = 1 + coordinate.resolve(ctx, "titles.top-left").at(0)
|
let end_x = 1 + coordinate.resolve(ctx, "titles.north-west").at(1).at(0)
|
||||||
let end_y = coordinate.resolve(ctx, "titles.bottom").at(1)
|
let end_y = coordinate.resolve(ctx, "titles.south").at(1).at(1)
|
||||||
|
|
||||||
group(
|
group(
|
||||||
{
|
{
|
||||||
@ -208,7 +208,7 @@
|
|||||||
let start = (
|
let start = (
|
||||||
a: (start_x, start_y + 16 * (i + 1) * pt),
|
a: (start_x, start_y + 16 * (i + 1) * pt),
|
||||||
b: (end_x, start_y + 16 * (i + 1) * pt),
|
b: (end_x, start_y + 16 * (i + 1) * pt),
|
||||||
number: passed / n_cols,
|
number: passed / n_cols * 100%,
|
||||||
)
|
)
|
||||||
|
|
||||||
if group_start == none { group_start = start }
|
if group_start == none { group_start = start }
|
||||||
@ -216,12 +216,12 @@
|
|||||||
let end = (
|
let end = (
|
||||||
a: (start_x, start_y + 16 * i * pt),
|
a: (start_x, start_y + 16 * i * pt),
|
||||||
b: (end_x, start_y + 16 * i * pt),
|
b: (end_x, start_y + 16 * i * pt),
|
||||||
number: (passed + len) / n_cols,
|
number: (passed + len) / n_cols * 100%,
|
||||||
)
|
)
|
||||||
|
|
||||||
group_end = end
|
group_end = end
|
||||||
|
|
||||||
content(start, end, anchor: "top-left", align(center + horizon, name))
|
content(start, end, anchor: "north-west", align(center + horizon, name))
|
||||||
|
|
||||||
passed += len
|
passed += len
|
||||||
}
|
}
|
||||||
@ -240,21 +240,30 @@
|
|||||||
// Draw the lines
|
// Draw the lines
|
||||||
for (i, task) in flat_tasks.enumerate() {
|
for (i, task) in flat_tasks.enumerate() {
|
||||||
let start = "titles.task" + str(i)
|
let start = "titles.task" + str(i)
|
||||||
let (_, task_start_y, _) = coordinate.resolve(ctx, "titles.task" + str(i))
|
let (_, (_, task_start_y, _)) = coordinate.resolve(ctx, "titles.task" + str(i))
|
||||||
let (task_top_x, task_top_y, _) = coordinate.resolve(ctx, "titles.task" + str(i) + "-top")
|
let (_, (task_top_x, task_top_y, _)) = coordinate.resolve(ctx, "titles.task" + str(i) + "-top")
|
||||||
let (_, task_bottom_y, _) = coordinate.resolve(ctx, "titles.task" + str(i) + "-bottom")
|
let (_, (_, task_bottom_y, _)) = coordinate.resolve(ctx, "titles.task" + str(i) + "-bottom")
|
||||||
|
let h = task_top_y - task_bottom_y
|
||||||
|
|
||||||
for gantt_line in task.lines {
|
for gantt_line in task.lines {
|
||||||
|
let y = task_start_y
|
||||||
|
if task.parallel {
|
||||||
|
if gantt_line.at("real", default: false) {
|
||||||
|
y -= h / 6
|
||||||
|
} else {
|
||||||
|
y += h / 6
|
||||||
|
}
|
||||||
|
}
|
||||||
let start = (
|
let start = (
|
||||||
a: (start_x, task_start_y),
|
a: (start_x, y),
|
||||||
b: (end_x, task_start_y),
|
b: (end_x, y),
|
||||||
number: (gantt_line.from + offset) / n_cols,
|
number: (gantt_line.from + offset) / n_cols * 100%,
|
||||||
)
|
)
|
||||||
|
|
||||||
let end = (
|
let end = (
|
||||||
a: (start_x, task_start_y),
|
a: (start_x, y),
|
||||||
b: (end_x, task_start_y),
|
b: (end_x, y),
|
||||||
number: (gantt_line.to + offset) / n_cols,
|
number: (gantt_line.to + offset) / n_cols * 100%,
|
||||||
)
|
)
|
||||||
|
|
||||||
let style = line-style
|
let style = line-style
|
||||||
@ -283,20 +292,20 @@
|
|||||||
|
|
||||||
if show-grid == true or show-grid == "y" {
|
if show-grid == true or show-grid == "y" {
|
||||||
for (i, task) in flat_tasks.enumerate() {
|
for (i, task) in flat_tasks.enumerate() {
|
||||||
let (_, task_bottom_y, _) = coordinate.resolve(ctx, "titles.task" + str(i) + "-bottom")
|
let (_, (_, task_bottom_y, _)) = coordinate.resolve(ctx, "titles.task" + str(i) + "-bottom")
|
||||||
line((start_x, task_bottom_y), (end_x, task_bottom_y), ..grid-style)
|
line((start_x, task_bottom_y), (end_x, task_bottom_y), ..grid-style)
|
||||||
}
|
}
|
||||||
|
|
||||||
if milestone-layout == "aligned" {
|
if milestone-layout == "aligned" {
|
||||||
for (i, milestone) in milestones.enumerate() {
|
for (i, milestone) in milestones.enumerate() {
|
||||||
let (_, bottom_y, _) = coordinate.resolve(ctx, "titles.milestone" + str(i) + "-bottom")
|
let (_, (_, bottom_y, _)) = coordinate.resolve(ctx, "titles.milestone" + str(i) + "-bottom")
|
||||||
line((start_x, bottom_y), (end_x, bottom_y), ..grid-style)
|
line((start_x, bottom_y), (end_x, bottom_y), ..grid-style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Border all around the timeline
|
// Border all around the timeline
|
||||||
rect("titles.top-left", (end_x, end_y), stroke: black + 1pt)
|
rect("titles.north-west", (end_x, end_y), stroke: black + 1pt)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -310,7 +319,7 @@
|
|||||||
style: milestone-line-style,
|
style: milestone-line-style,
|
||||||
overhang: milestone-overhang,
|
overhang: milestone-overhang,
|
||||||
spacing: spacing,
|
spacing: spacing,
|
||||||
anchor: "top",
|
anchor: "north",
|
||||||
type: "milestone",
|
type: "milestone",
|
||||||
) = {
|
) = {
|
||||||
if milestone-layout == "in-place" {
|
if milestone-layout == "in-place" {
|
||||||
@ -321,14 +330,14 @@
|
|||||||
let pos = (x: x, y: end_y - (spacing + overhang).pt() * pt)
|
let pos = (x: x, y: end_y - (spacing + overhang).pt() * pt)
|
||||||
let box_x = x
|
let box_x = x
|
||||||
|
|
||||||
let (w, h) = measure(body, ctx)
|
let (w, h) = util.measure(ctx, body)
|
||||||
if x + w / 2 > end_x {
|
if x + w / 2 > end_x {
|
||||||
box_x = end_x - w / 2
|
box_x = end_x - w / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
let (prev_end_x, prev_start_y, _) = coordinate.resolve-anchor(ctx, "milestone" + str(i - 1) + ".top-right")
|
let (prev_end_x, prev_start_y, _) = coordinate.resolve-anchor(ctx, "milestone" + str(i - 1) + ".north-east")
|
||||||
let prev_end_y = coordinate.resolve-anchor(ctx, "milestone" + str(i - 1) + ".bottom").at(1)
|
let prev_end_y = coordinate.resolve-anchor(ctx, "milestone" + str(i - 1) + ".south").at(1)
|
||||||
|
|
||||||
if box_x - w / 2 < prev_end_x and pos.y <= prev_start_y and pos.y + h >= prev_end_y {
|
if box_x - w / 2 < prev_end_x and pos.y <= prev_start_y and pos.y + h >= prev_end_y {
|
||||||
pos = (x: x, y: prev_end_y - spacing.pt() * pt * 2)
|
pos = (x: x, y: prev_end_y - spacing.pt() * pt * 2)
|
||||||
@ -346,7 +355,7 @@
|
|||||||
)
|
)
|
||||||
} else if milestone-layout == "aligned" {
|
} else if milestone-layout == "aligned" {
|
||||||
let x = (end_x - start_x) * (at / n_cols) + start_x
|
let x = (end_x - start_x) * (at / n_cols) + start_x
|
||||||
let end_y = coordinate.resolve(ctx, "titles.milestone" + str(i) + "-right").at(1)
|
let end_y = coordinate.resolve(ctx, "titles.milestone" + str(i) + "-right").at(1).at(1)
|
||||||
line((x, start_y), (x, end_y), (start_x, end_y), ..style)
|
line((x, start_y), (x, end_y), (start_x, end_y), ..style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -354,7 +363,7 @@
|
|||||||
on-layer(-0.5, {
|
on-layer(-0.5, {
|
||||||
if milestone-layout == "aligned" {
|
if milestone-layout == "aligned" {
|
||||||
set-ctx(ctx => {
|
set-ctx(ctx => {
|
||||||
ctx.prev.pt = coordinate.resolve(ctx, "titles.bottom")
|
ctx.prev.pt = coordinate.resolve(ctx, "titles.south").at(1)
|
||||||
return ctx
|
return ctx
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -417,9 +426,13 @@
|
|||||||
|
|
||||||
#let task(name, style: none, ..lines) = {
|
#let task(name, style: none, ..lines) = {
|
||||||
let processed_lines = ()
|
let processed_lines = ()
|
||||||
|
let parallel = false
|
||||||
|
|
||||||
for line in lines.pos() {
|
for line in lines.pos() {
|
||||||
if type(line) == dictionary {
|
if type(line) == dictionary {
|
||||||
|
if line.at("real", default: false) {
|
||||||
|
parallel = true
|
||||||
|
}
|
||||||
processed_lines.push(line)
|
processed_lines.push(line)
|
||||||
} else {
|
} else {
|
||||||
let (from, to) = line
|
let (from, to) = line
|
||||||
@ -431,7 +444,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
((type: "task", name: name, lines: processed_lines),)
|
((type: "task", name: name, parallel: parallel, lines: processed_lines),)
|
||||||
}
|
}
|
||||||
|
|
||||||
#let taskgroup(title: none, tasks) = {
|
#let taskgroup(title: none, tasks) = {
|
||||||
|
Loading…
Reference in New Issue
Block a user