presentation prototype with llm
parent
b74be29ee6
commit
d498205e42
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
@ -0,0 +1,416 @@
|
||||
#import "@preview/touying:0.6.1": *
|
||||
#import "./presentation/theme.typ": *
|
||||
#import "@preview/cetz:0.3.2"
|
||||
#import "@preview/fletcher:0.5.4" as fletcher: node, edge
|
||||
#import "@preview/numbly:0.1.0": numbly
|
||||
#import "@preview/theorion:0.3.2": *
|
||||
#show: show-theorion
|
||||
|
||||
// cetz and fletcher bindings for touying
|
||||
#let cetz-canvas = touying-reducer.with(reduce: cetz.canvas, cover: cetz.draw.hide.with(bounds: true))
|
||||
|
||||
#let fletcher-diagram = touying-reducer.with(reduce: fletcher.diagram, cover: fletcher.hide)
|
||||
|
||||
#show: dm-unipi-theme.with(aspect-ratio: "16-9", config-common(frozen-counters: (theorem-counter,)), config-info(
|
||||
title: [Il Polinomio di Kauffman: Un Invariante di Isotopia Regolare],
|
||||
subtitle: [Tesi di Laurea Triennale],
|
||||
author: [Antonio De Lucreziis],
|
||||
date: datetime.today(),
|
||||
institution: [Dipartimento di Matematica \ Università di Pisa],
|
||||
logo: image("assets/dm-unipi-logo-bianco.png"),
|
||||
))
|
||||
|
||||
#set text(font: "Source Sans Pro", weight: 500, size: 20pt)
|
||||
#set strong(delta: 100)
|
||||
#set par(leading: 0.75em)
|
||||
|
||||
#title-slide()
|
||||
|
||||
= Introduzione e Motivazione
|
||||
|
||||
== Motivazione
|
||||
|
||||
#slide[
|
||||
Come distinguere matematicamente due nodi?
|
||||
|
||||
#pause
|
||||
|
||||
- I nodi possono apparire diversi ma essere topologicamente equivalenti
|
||||
- Abbiamo bisogno di *invarianti*: proprietà che non cambiano sotto deformazioni ammesse
|
||||
- Gli invarianti polinomiali sono strumenti potenti e computabili
|
||||
|
||||
#pause
|
||||
|
||||
#align(center)[
|
||||
#text(size: 1.2em, weight: "bold")[
|
||||
Obiettivo: costruire un invariante polinomiale robusto
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
== Nodi e Diagrammi
|
||||
|
||||
#slide[
|
||||
*Definizione:* Un *nodo tame* è un sottoinsieme $K subset bb(R)^3$ per cui esiste un embedding $f : bb(S)^1 arrow.hook bb(R)^3$ localmente piatto con $K = f(bb(S)^1)$.
|
||||
|
||||
#pause
|
||||
|
||||
*Problema:* Lavorare direttamente con embedding in $bb(R)^3$ è complesso
|
||||
|
||||
#pause
|
||||
|
||||
*Soluzione:* Proiezioni su un piano
|
||||
- Proiettiamo il nodo su un piano
|
||||
- Aggiungiamo informazione *sopra/sotto* ad ogni incrocio
|
||||
- Otteniamo un *diagramma* del nodo
|
||||
|
||||
#pause
|
||||
|
||||
#align(center)[
|
||||
I diagrammi sono l'interfaccia computazionale per studiare i nodi
|
||||
]
|
||||
]
|
||||
|
||||
== Equivalenze tra Nodi
|
||||
|
||||
#slide[
|
||||
*Isotopia Ambiente:* Deformare un nodo senza tagliarlo o incollarlo
|
||||
|
||||
Due nodi $K_0, K_1 subset bb(R)^3$ sono *equivalenti* se esiste un'*isotopia ambiente* $H : bb(R)^3 times [0,1] arrow bb(R)^3$
|
||||
|
||||
#pause
|
||||
|
||||
*Teorema di Reidemeister:* Due diagrammi rappresentano nodi equivalenti se e solo se sono collegati da mosse I, II, III
|
||||
|
||||
#pause
|
||||
|
||||
*Isotopia Regolare:* Equivalenza generata solo da mosse II e III (ignoriamo la mossa I)
|
||||
|
||||
#pause
|
||||
|
||||
#align(center)[
|
||||
L'isotopia regolare "vede" i riccioli (curls)
|
||||
]
|
||||
]
|
||||
|
||||
= Teoria e Costruzione
|
||||
|
||||
== Il Writhe: Un Primo Invariante
|
||||
|
||||
#slide[
|
||||
*Definizione del segno di un incrocio:*
|
||||
$epsilon(+) = +1 quad quad epsilon(-) = -1$
|
||||
|
||||
#pause
|
||||
|
||||
*Il writhe:* $w(K) := sum_("incroci " c) epsilon(c)$
|
||||
|
||||
#pause
|
||||
|
||||
*Proprietà:*
|
||||
- Invariante per mosse II e III ✓
|
||||
- NON invariante per mossa I ✗
|
||||
|
||||
#pause
|
||||
|
||||
*Formula di correzione:* Se $L_K$ è invariante per isotopia regolare con
|
||||
$L(#text("sopra-ricciolo")) = a L(#text("filo"))$, $L(#text("sotto-ricciolo")) = a^(-1) L(#text("filo"))$
|
||||
|
||||
Allora: $F_K := a^(-w(K)) L_K$ è invariante per isotopia ambiente!
|
||||
]
|
||||
|
||||
== Il Polinomio di Kauffman: Definizione
|
||||
|
||||
#slide[
|
||||
Il protagonista: $L_K (a,z) in bb(Z)[a, a^(-1), z, z^(-1)]$
|
||||
|
||||
*Assiomi:*
|
||||
1. Se $K$, $K'$ sono equivalenti a meno di isotopia regolare, allora $L_K = L_{K'}$
|
||||
|
||||
#pause
|
||||
|
||||
2. Relazioni skein:
|
||||
- $L(#text("sopra")) + L(#text("sotto")) = z(L(#text("h-splice")) + L(#text("v-splice")))$
|
||||
- $L(#text("nodo banale")) = 1$
|
||||
- $L(#text("sopra-ricciolo")) = a L(#text("filo"))$, $L(#text("sotto-ricciolo")) = a^(-1) L(#text("filo"))$
|
||||
|
||||
#pause
|
||||
|
||||
*Domanda:* Gli assiomi definiscono univocamente $L_K$?
|
||||
]
|
||||
|
||||
== Esempio: Link di Hopf
|
||||
|
||||
#slide[
|
||||
Applichiamo la relazione skein:
|
||||
|
||||
$L[#text("Hopf")] + L[#text("versione scambiata")] = z(L[#text("due cerchi")] + L[#text("due fili")])$
|
||||
|
||||
#pause
|
||||
|
||||
Sapendo che:
|
||||
- $L[#text("due cerchi")] = delta = (a + a^(-1))/z - 1$
|
||||
- $L[#text("due fili")] = a + a^(-1)$
|
||||
|
||||
#pause
|
||||
|
||||
Otteniamo:
|
||||
$L[#text("Hopf")] = -(a + a^(-1))z^(-1) + 1 + (a + a^(-1))z$
|
||||
|
||||
#pause
|
||||
|
||||
Questi calcoli suggeriscono che $L_K$ esiste, ma serve una dimostrazione rigorosa!
|
||||
]
|
||||
|
||||
== La Sfida della Buona Definizione
|
||||
|
||||
#slide[
|
||||
*Problema centrale:* Gli assiomi definiscono $L_K$ in modo unico?
|
||||
|
||||
#pause
|
||||
|
||||
*Sfide:*
|
||||
1. Gli assiomi sono *impliciti* (relazioni, non formule)
|
||||
2. Come garantire che esista una soluzione?
|
||||
3. Come garantire l'unicità?
|
||||
4. Come verificare l'indipendenza dalle scelte computazionali?
|
||||
|
||||
#pause
|
||||
|
||||
*Scelte che potrebbero influenzare il risultato:*
|
||||
- Scelta del punto base $p$ su ogni componente
|
||||
- Direzione del punto base (orario vs antiorario)
|
||||
- Ordine delle operazioni nelle sequenze
|
||||
|
||||
#pause
|
||||
|
||||
#align(center)[
|
||||
#box(fill: red.lighten(80%), inset: 1em, radius: 5pt)[
|
||||
*Senza questa dimostrazione, $L_K$ non sarebbe ben definito!*
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
== Ingredienti per la Costruzione
|
||||
|
||||
#slide[
|
||||
*Concetti chiave necessari:*
|
||||
|
||||
1. *Nodo banale standard*: $hat(K)(cal(U), p)$
|
||||
- Percorrere l'ombra planare da un punto base $p$
|
||||
- Primo passaggio su ogni incrocio = sopra-incrocio
|
||||
|
||||
#pause
|
||||
|
||||
2. *Operazioni sui diagrammi*:
|
||||
- $S_i K$: scambia l'incrocio $i$
|
||||
- $E_i K$, $e_i K$: splice orizzontale e verticale
|
||||
|
||||
#pause
|
||||
|
||||
3. *Sequenze di scambi*: $lambda = (lambda_n, dots, lambda_0)$
|
||||
- Trasformano $K$ in $hat(K)(lambda)$
|
||||
|
||||
#pause
|
||||
|
||||
*Proprietà fondamentale:* $L[hat(K)] = a^(w(hat(K)))$
|
||||
]
|
||||
|
||||
== La Formula Ricorsiva
|
||||
|
||||
#slide[
|
||||
*Idea*: Esprimere $L_K$ in termini di diagrammi "più semplici"
|
||||
|
||||
Applicando le relazioni skein incrementalmente:
|
||||
$
|
||||
L[K] + L[S_0 K] &= z(L[E_0 K] + L[e_0 K]) \
|
||||
L[S_0 K] + L[S_1 S_0 K] &= z(L[E_1 S_0 K] + L[e_1 S_0 K]) \
|
||||
&dots.v \
|
||||
L[S_(n-1) dots S_0 K] + L[hat(K)] &= z(L[E_n S_(n-1) dots S_0 K] + L[e_n S_(n-1) dots S_0 K])
|
||||
$
|
||||
|
||||
#pause
|
||||
|
||||
Sommando e sottraendo membro a membro, i termini intermedi si cancellano!
|
||||
|
||||
#pause
|
||||
|
||||
*Formula finale:*
|
||||
$L_K = (-1)^(n+1) L_(hat(K)) + z sum_(i=0)^n (-1)^i (L[A_i^lambda K] + L[B_i^lambda K])$
|
||||
]
|
||||
|
||||
== Definizione Induttiva Completa
|
||||
|
||||
#slide[
|
||||
*Caso 1:* $K = hat(K)$ (nodo banale standard)
|
||||
$L_K = a^(w(K))$
|
||||
|
||||
#pause
|
||||
|
||||
*Caso 2:* $K = K_1 union K_2$ con $K_1$ sovrastante $K_2$
|
||||
$L_K = delta L_(K_1) L_(K_2)$ dove $delta = (a + a^(-1))/z - 1$
|
||||
|
||||
#pause
|
||||
|
||||
*Caso 3:* Uso della formula ricorsiva:
|
||||
$L_K = 1/2 [sum_(q = p, overline(p)) ((-1)^(abs(lambda(q))+1) L_(hat(K)(q)) + z sum_K (lambda(q)))]$
|
||||
|
||||
#pause
|
||||
|
||||
*Proprietà cruciale:* Ogni termine a destra ha meno incroci o è "più vicino" al caso base
|
||||
]
|
||||
|
||||
== Strategia della Dimostrazione
|
||||
|
||||
#slide[
|
||||
*Ipotesi induttiva* (per diagrammi con $< N$ incroci):
|
||||
|
||||
1. $L_K$ è ben definito (indipendente dalle scelte)
|
||||
2. $L_K$ verifica tutte le relazioni skein
|
||||
3. $L_K$ è invariante per mosse II e III che non aumentano gli incroci
|
||||
|
||||
#pause
|
||||
|
||||
*Lemmi tecnici fondamentali:*
|
||||
|
||||
- *Lemma delle Rotazioni*: L'ordine ciclico degli scambi non influenza il risultato
|
||||
- *Invarianza del punto base*: La definizione non dipende dal punto base scelto
|
||||
- *Identità per nodi banali*: $L[hat(K)(p)] + L[hat(K)(q)] = z(L[E_i hat(K)] + L[e_i hat(K)])$
|
||||
|
||||
#pause
|
||||
|
||||
*Metodo:* Spostando il punto base di un incrocio per volta, si dimostra l'invarianza completa
|
||||
]
|
||||
|
||||
== Verifica degli Assiomi e Invarianza
|
||||
|
||||
#slide[
|
||||
*Teorema*: La definizione induttiva soddisfa tutti gli assiomi di Kauffman
|
||||
|
||||
#pause
|
||||
|
||||
*Dimostrazione per le relazioni skein*:
|
||||
1. Scegli il punto base in modo che l'incrocio sia il primo nella sequenza
|
||||
2. La relazione skein emerge naturalmente dalla formula ricorsiva
|
||||
3. Gli altri termini si cancellano per simmetria
|
||||
|
||||
#pause
|
||||
|
||||
*Invarianza per isotopia regolare*:
|
||||
- *Mossa II*: scegli punti base che evitano gli incroci coinvolti
|
||||
- *Mossa III*: usa equivalenze locali e induzione
|
||||
|
||||
#pause
|
||||
|
||||
#align(center)[
|
||||
La costruzione induttiva "conosce automaticamente" tutte le proprietà necessarie!
|
||||
]
|
||||
]
|
||||
|
||||
= Risultati e Applicazioni
|
||||
|
||||
== Il Risultato Principale
|
||||
|
||||
#slide[
|
||||
*Teorema*: Esiste ed è unico un invariante $L_K (a,z)$ che soddisfa gli assiomi di Kauffman.
|
||||
|
||||
#pause
|
||||
|
||||
*Dimostrazione completa*:
|
||||
1. *Esistenza*: La costruzione induttiva fornisce una definizione esplicita
|
||||
2. *Buona definizione*: Indipendenza dalle scelte arbitrarie
|
||||
3. *Verifica assiomi*: La definizione soddisfa tutte le relazioni richieste
|
||||
4. *Unicità*: Gli assiomi determinano univocamente i valori
|
||||
|
||||
#pause
|
||||
|
||||
*Conseguenza*: Il polinomio di Kauffman è un invariante completo e computabile per l'isotopia regolare
|
||||
|
||||
#pause
|
||||
|
||||
#align(center)[
|
||||
#box(fill: green.lighten(80%), inset: 1em, radius: 5pt)[
|
||||
*La teoria è ora su basi solide!*
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
== Dal Regolare all'Ambiente: Il Polinomio $F_K$
|
||||
|
||||
#slide[
|
||||
Usando la formula di correzione:
|
||||
$F_K = a^(-w(K)) L_K$
|
||||
|
||||
#pause
|
||||
|
||||
*Teorema*: $F_K (a,z)$ è un invariante di isotopia ambiente.
|
||||
|
||||
#pause
|
||||
|
||||
*Significato*:
|
||||
- Partendo da un invariante regolare ($L_K$)
|
||||
- Aggiungiamo la correzione del writhe
|
||||
- Otteniamo un vero invariante di nodi!
|
||||
|
||||
#pause
|
||||
|
||||
*Proprietà*:
|
||||
- $F_(m(K))(a,z) = F_K(1/a, z)$ (comportamento rispetto al mirror)
|
||||
- $F[K_1 hash K_2] = F[K_1] F[K_2]$ (moltiplicatività per somma connessa)
|
||||
- $F_K$ distingue nodi che $L_K$ non può distinguere
|
||||
]
|
||||
|
||||
== Implementazione e Verifica
|
||||
|
||||
#slide[
|
||||
*Progetto Computazionale:*
|
||||
|
||||
- *Implementazione in Python*: Algoritmo basato sulle relazioni skein
|
||||
- *Calcolo automatico* di $L_K$ e $F_K$ per diagrammi di nodi
|
||||
- *Interfaccia user-friendly* per l'inserimento di diagrammi
|
||||
|
||||
#pause
|
||||
|
||||
*Verifica sperimentale:*
|
||||
- Confronto con il database *KnotInfo*
|
||||
- Verifica su centinaia di nodi noti
|
||||
- Test di coerenza con valori pubblicati
|
||||
|
||||
#pause
|
||||
|
||||
*Scoperta interessante:* Trovato un *errore* nel valore pubblicato per il nodo $10_{125}$!
|
||||
|
||||
#pause
|
||||
|
||||
#align(center)[
|
||||
La teoria e l'implementazione si confermano a vicenda
|
||||
]
|
||||
]
|
||||
|
||||
== Conclusioni
|
||||
|
||||
#slide[
|
||||
*Percorso compiuto:*
|
||||
1. Dal concetto geometrico di nodo alla formalizzazione tramite diagrammi
|
||||
2. Definizione dell'isotopia regolare e degli invarianti
|
||||
3. Costruzione rigorosa del polinomio di Kauffman
|
||||
4. Dimostrazione della buona definizione
|
||||
5. Estensione agli invarianti di isotopia ambiente
|
||||
|
||||
#pause
|
||||
|
||||
*Risultati principali:*
|
||||
- *Dimostrazione* della buona definizione di $L_K$
|
||||
- *Costruzione* di $F_K$ come invariante ambiente
|
||||
- *Verifica computazionale* e scoperta di errori nella letteratura
|
||||
|
||||
#pause
|
||||
|
||||
#align(center)[
|
||||
#text(size: 1.2em, weight: "bold")[
|
||||
Grazie per l'attenzione!
|
||||
]
|
||||
]
|
||||
|
||||
*Domande?*
|
||||
]
|
||||
@ -0,0 +1,528 @@
|
||||
#import "@preview/touying:0.6.1": *
|
||||
|
||||
// #let slide(config: (:), repeat: auto, setting: body => body, composer: auto, ..bodies) = touying-slide-wrapper(self => {
|
||||
// let header(self) = {
|
||||
// place(left + top, dx: 3em, grid(
|
||||
// columns: (auto, 1fr),
|
||||
// column-gutter: 1em,
|
||||
// align: horizon,
|
||||
// image("../assets/logo-unipi-top.png"),
|
||||
// text(fill: self.colors.primary, utils.call-or-display(self, self.store.header)),
|
||||
// ))
|
||||
// // place(left + top, line(start: (40%, 0%), end: (40% - 3%, 3em + 0.5em + 0.5em), stroke: .75em + white))
|
||||
// }
|
||||
// let footer(self) = {
|
||||
// set text(size: 0.8em)
|
||||
// place(left, dx: 3em, utils.call-or-display(self, utils.call-or-display(self, self.store.footer)))
|
||||
// }
|
||||
// let self = utils.merge-dicts(self, config-page(header: header, footer: footer))
|
||||
// touying-slide(self: self, config: config, repeat: repeat, setting: setting, composer: composer, ..bodies)
|
||||
// })
|
||||
|
||||
// #let title-slide(config: (:), ..args) = touying-slide-wrapper(
|
||||
// self => {
|
||||
// self = utils.merge-dicts(
|
||||
// self,
|
||||
// config,
|
||||
// config-common(freeze-slide-counter: true),
|
||||
// config-page(background: utils.call-or-display(self, self.store.background), margin: (x: 0em, top: 30%, bottom: 0%)),
|
||||
// )
|
||||
// let info = self.info + args.named()
|
||||
// let body = {
|
||||
// set align(center)
|
||||
// stack(spacing: 3em, if info.title != none {
|
||||
// text(size: 48pt, weight: "bold", fill: self.colors.primary, info.title)
|
||||
// }, if info.author != none {
|
||||
// text(fill: self.colors.primary-light, size: 28pt, weight: "regular", info.author)
|
||||
// }, if info.date != none {
|
||||
// text(fill: self.colors.primary-light, size: 20pt, weight: "regular", utils.display-info-date(self))
|
||||
// })
|
||||
// }
|
||||
// touying-slide(self: self, body)
|
||||
// },
|
||||
// )
|
||||
|
||||
// #let outline-slide(config: (:), leading: 50pt) = touying-slide-wrapper(self => {
|
||||
// set text(size: 30pt, fill: self.colors.primary)
|
||||
// set par(leading: leading)
|
||||
|
||||
// let body = {
|
||||
// grid(columns: (1fr, 1fr), rows: (1fr), align(center + horizon, {
|
||||
// set par(leading: 20pt)
|
||||
// context {
|
||||
// if text.lang == "zh" {
|
||||
// text(size: 80pt, weight: "bold", [#text(size: 36pt)[CONTENTS]\ 目录])
|
||||
// } else {
|
||||
// text(size: 48pt, weight: "bold", [CONTENTS])
|
||||
// }
|
||||
// }
|
||||
// }), align(left + horizon, {
|
||||
// set par(leading: leading)
|
||||
// set text(weight: "bold")
|
||||
// components.custom-progressive-outline(level: none, depth: 1, numbered: (true,))
|
||||
// }))
|
||||
// }
|
||||
// self = utils.merge-dicts(
|
||||
// self,
|
||||
// config-common(freeze-slide-counter: true),
|
||||
// config-page(background: utils.call-or-display(self, self.store.background), margin: 0em),
|
||||
// )
|
||||
// touying-slide(self: self, config: config, body)
|
||||
// })
|
||||
|
||||
// #let new-section-slide(config: (:), level: 1, body) = touying-slide-wrapper(
|
||||
// self => {
|
||||
// let slide-body = {
|
||||
// stack(
|
||||
// dir: ttb,
|
||||
// spacing: 12%,
|
||||
// align(center, text(fill: self.colors.primary, size: 166pt, utils.display-current-heading-number(level: level))),
|
||||
// align(center, text(
|
||||
// fill: self.colors.primary,
|
||||
// size: 60pt,
|
||||
// weight: "bold",
|
||||
// utils.display-current-heading(level: level, numbered: false),
|
||||
// )),
|
||||
// )
|
||||
// body
|
||||
// }
|
||||
// self = utils.merge-dicts(self, config-page(
|
||||
// margin: (left: 0%, right: 0%, top: 20%, bottom: 0%),
|
||||
// background: utils.call-or-display(self, self.store.background),
|
||||
// ))
|
||||
// touying-slide(self: self, config: config, slide-body)
|
||||
// },
|
||||
// )
|
||||
|
||||
// /// Focus on some content.
|
||||
// ///
|
||||
// /// Example: `#focus-slide[Wake up!]`
|
||||
// ///
|
||||
// /// - config (dictionary): The configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more configurations, you can use `utils.merge-dicts` to merge them.
|
||||
// #let focus-slide(config: (:), body) = touying-slide-wrapper(
|
||||
// self => {
|
||||
// self = utils.merge-dicts(self, config-common(freeze-slide-counter: true), config-page(fill: self.colors.primary, margin: 2em))
|
||||
// set text(fill: self.colors.neutral-lightest, size: 2em, weight: "bold")
|
||||
// touying-slide(self: self, config: config, align(horizon + center, body))
|
||||
// },
|
||||
// )
|
||||
|
||||
// #let dm-unipi-theme(
|
||||
// aspect-ratio: "16-9",
|
||||
// header: self => utils.display-current-heading(depth: self.slide-level),
|
||||
// footer: context utils.slide-counter.display(),
|
||||
// ..args,
|
||||
// body,
|
||||
// ) = {
|
||||
// set text(font: "Open Sans")
|
||||
// set par(leading: 1em)
|
||||
|
||||
// set text(size: 20pt)
|
||||
// set heading(numbering: "1.1")
|
||||
// show heading.where(level: 1): set heading(numbering: "1")
|
||||
|
||||
// show: touying-slides.with(
|
||||
// config-page(paper: "presentation-" + aspect-ratio, margin: (x: 3em, top: 5em, bottom: 3em)),
|
||||
// config-common(slide-fn: slide, new-section-slide-fn: new-section-slide),
|
||||
// config-methods(init: (self: none, body) => {
|
||||
// show heading: set text(fill: self.colors.primary-light)
|
||||
|
||||
// body
|
||||
// }, alert: utils.alert-with-primary-color),
|
||||
// config-colors(
|
||||
// primary: rgb("#003c71"),
|
||||
// primary-light: rgb("#00213d"),
|
||||
// primary-lightest: rgb("#F2F4F8"),
|
||||
// neutral-lightest: rgb("#FFFFFF"),
|
||||
// ),
|
||||
// // save the variables for later use
|
||||
// config-store(
|
||||
// align: align,
|
||||
// header: header,
|
||||
// footer: footer,
|
||||
// background: self => {
|
||||
// let page-width = if self.page.paper == "presentation-16-9" { 841.89pt } else { 793.7pt }
|
||||
// let r = if self.at("show-notes-on-second-screen", default: none) == none { 1.0 } else { 0.5 }
|
||||
// let bias1 = - page-width * (1 - r)
|
||||
// let bias2 = - page-width * 2 * (1 - r)
|
||||
// place(center + horizon, dx: bias1, polygon(
|
||||
// fill: self.colors.primary-lightest,
|
||||
// (35% * page-width, -17%),
|
||||
// (70% * page-width, 10%),
|
||||
// (35% * page-width, 30%),
|
||||
// (0% * page-width, 10%),
|
||||
// ))
|
||||
// place(center + horizon, dy: 7%, dx: bias1, ellipse(fill: white, width: r * 45%, height: 120pt))
|
||||
// place(center + horizon, dy: 5%, dx: bias1, ellipse(fill: self.colors.primary-lightest, width: r * 40%, height: 80pt))
|
||||
// place(center + horizon, dy: 12%, dx: bias1, rect(fill: self.colors.primary-lightest, width: r * 40%, height: 60pt))
|
||||
// place(center + horizon, dy: 20%, dx: bias1, ellipse(fill: white, width: r * 40%, height: 70pt))
|
||||
// },
|
||||
// ),
|
||||
// ..args,
|
||||
// )
|
||||
|
||||
// body
|
||||
// }
|
||||
//
|
||||
|
||||
#let _tblock(self: none, title: none, it) = {
|
||||
grid(columns: 1, row-gutter: 0pt, block(
|
||||
fill: self.colors.primary-dark,
|
||||
width: 100%,
|
||||
radius: (top: 6pt),
|
||||
inset: (top: 0.4em, bottom: 0.3em, left: 0.5em, right: 0.5em),
|
||||
text(fill: self.colors.neutral-lightest, weight: "bold", title),
|
||||
), rect(
|
||||
fill: gradient.linear(self.colors.primary-dark, self.colors.primary.lighten(90%), angle: 90deg),
|
||||
width: 100%,
|
||||
height: 4pt,
|
||||
), block(
|
||||
fill: self.colors.primary.lighten(90%),
|
||||
width: 100%,
|
||||
radius: (bottom: 6pt),
|
||||
inset: (top: 0.4em, bottom: 0.5em, left: 0.5em, right: 0.5em),
|
||||
it,
|
||||
))
|
||||
}
|
||||
|
||||
/// Theorem block for the presentation.
|
||||
///
|
||||
/// - title (string): The title of the theorem. Default is `none`.
|
||||
///
|
||||
/// - it (content): The content of the theorem.
|
||||
#let tblock(title: none, it) = touying-fn-wrapper(_tblock.with(title: title, it))
|
||||
|
||||
/// Default slide function for the presentation.
|
||||
///
|
||||
/// - title (string): The title of the slide. Default is `auto`.
|
||||
///
|
||||
/// - config (dictionary): The configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them.
|
||||
///
|
||||
/// - repeat (auto): The number of subslides. Default is `auto`, which means touying will automatically calculate the number of subslides.
|
||||
///
|
||||
/// The `repeat` argument is necessary when you use `#slide(repeat: 3, self => [ .. ])` style code to create a slide. The callback-style `uncover` and `only` cannot be detected by touying automatically.
|
||||
///
|
||||
/// - setting (dictionary): The setting of the slide. You can use it to add some set/show rules for the slide.
|
||||
///
|
||||
/// - composer (function): The composer of the slide. You can use it to set the layout of the slide.
|
||||
///
|
||||
/// For example, `#slide(composer: (1fr, 2fr, 1fr))[A][B][C]` to split the slide into three parts. The first and the last parts will take 1/4 of the slide, and the second part will take 1/2 of the slide.
|
||||
///
|
||||
/// If you pass a non-function value like `(1fr, 2fr, 1fr)`, it will be assumed to be the first argument of the `components.side-by-side` function.
|
||||
///
|
||||
/// The `components.side-by-side` function is a simple wrapper of the `grid` function. It means you can use the `grid.cell(colspan: 2, ..)` to make the cell take 2 columns.
|
||||
///
|
||||
/// For example, `#slide(composer: 2)[A][B][#grid.cell(colspan: 2)[Footer]]` will make the `Footer` cell take 2 columns.
|
||||
///
|
||||
/// If you want to customize the composer, you can pass a function to the `composer` argument. The function should receive the contents of the slide and return the content of the slide, like `#slide(composer: grid.with(columns: 2))[A][B]`.
|
||||
///
|
||||
/// - bodies (content): The contents of the slide. You can call the `slide` function with syntax like `#slide[A][B][C]` to create a slide.
|
||||
#let slide(
|
||||
title: auto,
|
||||
header: auto,
|
||||
footer: auto,
|
||||
align: auto,
|
||||
config: (:),
|
||||
repeat: auto,
|
||||
setting: body => body,
|
||||
composer: auto,
|
||||
..bodies,
|
||||
) = touying-slide-wrapper(self => {
|
||||
if align != auto {
|
||||
self.store.align = align
|
||||
}
|
||||
if title != auto {
|
||||
self.store.title = title
|
||||
}
|
||||
if header != auto {
|
||||
self.store.header = header
|
||||
}
|
||||
if footer != auto {
|
||||
self.store.footer = footer
|
||||
}
|
||||
let new-setting = body => {
|
||||
show: std.align.with(self.store.align)
|
||||
show: setting
|
||||
body
|
||||
}
|
||||
touying-slide(self: self, config: config, repeat: repeat, setting: new-setting, composer: composer, ..bodies)
|
||||
})
|
||||
|
||||
/// Title slide for the presentation. You should update the information in the `config-info` function. You can also pass the information directly to the `title-slide` function.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```typst
|
||||
/// #show: stargazer-theme.with(
|
||||
/// config-info(
|
||||
/// title: [Title],
|
||||
/// logo: emoji.city,
|
||||
/// ),
|
||||
/// )
|
||||
///
|
||||
/// #title-slide(subtitle: [Subtitle])
|
||||
/// ```
|
||||
///
|
||||
/// - config (dictionary): The configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them.
|
||||
#let title-slide(config: (:), ..args) = touying-slide-wrapper(self => {
|
||||
self = utils.merge-dicts(self, config)
|
||||
self.store.title = none
|
||||
let info = self.info + args.named()
|
||||
info.authors = {
|
||||
let authors = if "authors" in info {
|
||||
info.authors
|
||||
} else {
|
||||
info.author
|
||||
}
|
||||
if type(authors) == array {
|
||||
authors
|
||||
} else {
|
||||
(authors,)
|
||||
}
|
||||
}
|
||||
let body = {
|
||||
show: std.align.with(center + horizon)
|
||||
block(fill: self.colors.primary, inset: 1.5em, radius: 0.5em, breakable: false, {
|
||||
text(size: 1.2em, fill: self.colors.neutral-lightest, weight: "bold", info.title)
|
||||
if info.subtitle != none {
|
||||
parbreak()
|
||||
text(size: 1.0em, fill: self.colors.neutral-lightest, weight: "bold", info.subtitle)
|
||||
}
|
||||
})
|
||||
// authors
|
||||
grid(
|
||||
columns: (1fr,) * calc.min(info.authors.len(), 3),
|
||||
column-gutter: 1em,
|
||||
row-gutter: 1em,
|
||||
..info.authors.map(author => text(fill: black, author)),
|
||||
)
|
||||
v(0.5em)
|
||||
// institution
|
||||
if info.institution != none {
|
||||
parbreak()
|
||||
text(size: 0.7em, info.institution)
|
||||
}
|
||||
// date
|
||||
if info.date != none {
|
||||
parbreak()
|
||||
text(size: 1.0em, utils.display-info-date(self))
|
||||
}
|
||||
}
|
||||
touying-slide(self: self, body)
|
||||
})
|
||||
|
||||
/// Outline slide for the presentation.
|
||||
///
|
||||
/// - config (dictionary): is the configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them.
|
||||
///
|
||||
/// - title (string): is the title of the outline. Default is `utils.i18n-outline-title`.
|
||||
///
|
||||
/// - level (int, none): is the level of the outline. Default is `none`.
|
||||
///
|
||||
/// - numbered (boolean): is whether the outline is numbered. Default is `true`.
|
||||
#let outline-slide(config: (:), title: utils.i18n-outline-title, numbered: true, level: none, ..args) = touying-slide-wrapper(
|
||||
self => {
|
||||
self.store.title = title
|
||||
touying-slide(
|
||||
self: self,
|
||||
config: config,
|
||||
std.align(
|
||||
self.store.align,
|
||||
components.adaptive-columns(text(fill: self.colors.primary, weight: "bold", components.custom-progressive-outline(
|
||||
level: level,
|
||||
alpha: self.store.alpha,
|
||||
indent: (0em, 1em),
|
||||
vspace: (.4em,),
|
||||
numbered: (numbered,),
|
||||
depth: 1,
|
||||
..args.named(),
|
||||
))) + args.pos().sum(default: none),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
/// New section slide for the presentation. You can update it by updating the `new-section-slide-fn` argument for `config-common` function.
|
||||
///
|
||||
/// Example: `config-common(new-section-slide-fn: new-section-slide.with(numbered: false))`
|
||||
///
|
||||
/// - config (dictionary): is the configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them.
|
||||
///
|
||||
/// - title (content, function): is the title of the section. The default is `utils.i18n-outline-title`.
|
||||
///
|
||||
/// - level (int): is the level of the heading. The default is `1`.
|
||||
///
|
||||
/// - numbered (boolean): is whether the heading is numbered. The default is `true`.
|
||||
///
|
||||
/// - body (none): is the body of the section. It will be passed by touying automatically.
|
||||
#let new-section-slide(config: (:), title: utils.i18n-outline-title, level: 1, numbered: true, ..args, body) = outline-slide(config: config, title: title, level: level, numbered: numbered, ..args, body)
|
||||
|
||||
/// Focus on some content.
|
||||
///
|
||||
/// Example: `#focus-slide[Wake up!]`
|
||||
///
|
||||
/// - config (dictionary): is the configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them.
|
||||
///
|
||||
/// - align (alignment): is the alignment of the content. The default is `horizon + center`.
|
||||
#let focus-slide(config: (:), align: horizon + center, body) = touying-slide-wrapper(self => {
|
||||
self = utils.merge-dicts(
|
||||
self,
|
||||
config-common(freeze-slide-counter: true),
|
||||
config-page(fill: self.colors.primary, margin: 2em, header: none, footer: none),
|
||||
)
|
||||
set text(fill: self.colors.neutral-lightest, weight: "bold", size: 1.5em)
|
||||
touying-slide(self: self, config: config, std.align(align, body))
|
||||
})
|
||||
|
||||
/// End slide for the presentation.
|
||||
///
|
||||
/// - config (dictionary): is the configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them.
|
||||
///
|
||||
/// - title (string): is the title of the slide. The default is `none`.
|
||||
///
|
||||
/// - body (array): is the content of the slide.
|
||||
#let ending-slide(config: (:), title: none, body) = touying-slide-wrapper(self => {
|
||||
let content = {
|
||||
set std.align(center + horizon)
|
||||
if title != none {
|
||||
block(
|
||||
fill: self.colors.tertiary,
|
||||
inset: (top: 0.7em, bottom: 0.7em, left: 3em, right: 3em),
|
||||
radius: 0.5em,
|
||||
text(size: 1.5em, fill: self.colors.neutral-lightest, title),
|
||||
)
|
||||
}
|
||||
body
|
||||
}
|
||||
touying-slide(self: self, config: config, content)
|
||||
})
|
||||
|
||||
#let dm-unipi-theme(
|
||||
aspect-ratio: "16-9",
|
||||
align: horizon,
|
||||
alpha: 20%,
|
||||
title: self => utils.display-current-heading(depth: self.slide-level),
|
||||
header-right: self => self.info.logo,
|
||||
progress-bar: true,
|
||||
footer-columns: (25%, 1fr, 5em),
|
||||
footer-a: self => self.info.author,
|
||||
footer-c: self => if self.info.short-title == auto {
|
||||
self.info.title
|
||||
} else {
|
||||
self.info.short-title
|
||||
},
|
||||
footer-d: context utils.slide-counter.display() + " / " + utils.last-slide-number,
|
||||
..args,
|
||||
body,
|
||||
) = {
|
||||
let header(self) = {
|
||||
set std.align(top)
|
||||
grid(
|
||||
rows: (auto, auto),
|
||||
utils.call-or-display(self, self.store.navigation),
|
||||
utils.call-or-display(self, self.store.header),
|
||||
)
|
||||
}
|
||||
let footer(self) = {
|
||||
set text(size: .5em)
|
||||
set std.align(center + bottom)
|
||||
grid(
|
||||
rows: (auto, auto),
|
||||
utils.call-or-display(self, self.store.footer),
|
||||
if self.store.progress-bar {
|
||||
utils.call-or-display(self, components.progress-bar(height: 2pt, self.colors.primary, self.colors.neutral-lightest))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
show: touying-slides.with(
|
||||
config-page(
|
||||
paper: "presentation-" + aspect-ratio,
|
||||
header: header,
|
||||
footer: footer,
|
||||
header-ascent: 0em,
|
||||
footer-descent: 0em,
|
||||
margin: (top: 3.5em, bottom: 2.5em, x: 2.5em),
|
||||
),
|
||||
config-common(slide-fn: slide, new-section-slide-fn: new-section-slide),
|
||||
config-methods(init: (self: none, body) => {
|
||||
set text(size: 18pt, font: "Open Sans")
|
||||
set par(leading: 0.75em)
|
||||
set list(marker: {
|
||||
place(top + left, dy: 0.2em, circle(fill: self.colors.primary, radius: 3pt))
|
||||
h(0.25em)
|
||||
})
|
||||
show figure.caption: set text(size: 0.6em)
|
||||
show footnote.entry: set text(size: 0.6em)
|
||||
show heading: set text(fill: self.colors.primary)
|
||||
show link: it => if type(it.dest) == str {
|
||||
set text(fill: self.colors.primary)
|
||||
it
|
||||
} else {
|
||||
it
|
||||
}
|
||||
show figure.where(kind: table): set figure.caption(position: top)
|
||||
|
||||
body
|
||||
}, alert: utils.alert-with-primary-color, tblock: _tblock),
|
||||
config-colors(
|
||||
primary: rgb("#003c71"),
|
||||
primary-dark: rgb("#005baa"),
|
||||
secondary: rgb("#ffffff"),
|
||||
neutral-lightest: rgb("#ffffff"),
|
||||
neutral-darkest: rgb("#001c35"),
|
||||
),
|
||||
// save the variables for later use
|
||||
config-store(
|
||||
align: align,
|
||||
alpha: alpha,
|
||||
title: title,
|
||||
header-right: header-right,
|
||||
progress-bar: progress-bar,
|
||||
footer-columns: footer-columns,
|
||||
footer-a: footer-a,
|
||||
footer-c: footer-c,
|
||||
footer-d: footer-d,
|
||||
navigation: self => components.simple-navigation(
|
||||
self: self,
|
||||
primary: white,
|
||||
secondary: gray,
|
||||
background: self.colors.neutral-darkest,
|
||||
logo: utils.call-or-display(self, self.store.header-right),
|
||||
),
|
||||
header: self => if self.store.title != none {
|
||||
block(
|
||||
width: 100%,
|
||||
height: 2em,
|
||||
fill: self.colors.primary,
|
||||
place(
|
||||
left + horizon,
|
||||
text(fill: self.colors.neutral-lightest, weight: 600, size: 1.2em, utils.call-or-display(self, self.store.title)),
|
||||
dx: 1.5em,
|
||||
),
|
||||
)
|
||||
},
|
||||
footer: self => {
|
||||
let cell(fill: none, it) = rect(
|
||||
width: 100%,
|
||||
height: 100%,
|
||||
inset: 1mm,
|
||||
outset: 0mm,
|
||||
fill: fill,
|
||||
stroke: none,
|
||||
std.align(horizon, text(fill: self.colors.neutral-lightest, it)),
|
||||
)
|
||||
grid(
|
||||
columns: self.store.footer-columns,
|
||||
rows: (1.5em, auto),
|
||||
cell(fill: self.colors.neutral-darkest, utils.call-or-display(self, self.store.footer-a)),
|
||||
cell(fill: self.colors.primary, utils.call-or-display(self, self.store.footer-c)),
|
||||
cell(fill: self.colors.primary, utils.call-or-display(self, self.store.footer-d)),
|
||||
)
|
||||
},
|
||||
),
|
||||
..args,
|
||||
)
|
||||
|
||||
body
|
||||
}
|
||||
Loading…
Reference in New Issue