added parallel task with real / planned work
updated timeliney to use latest CeTZ
This commit is contained in:
		
							
								
								
									
										
											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), | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -145,6 +145,7 @@ | |||||||
|           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) = { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user