diff --git a/eslint.config.js b/eslint.config.js index 5e6b472..442ef96 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -19,5 +19,12 @@ export default defineConfig([ ecmaVersion: 2020, globals: globals.browser, }, + rules: { + '@typescript-eslint/no-unused-vars': ['error', { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }] + } }, ]) diff --git a/public/newsletters/2702206/draft-022026.md b/public/newsletters/2702206/draft-022026.md index a71ce09..f058ba2 100644 --- a/public/newsletters/2702206/draft-022026.md +++ b/public/newsletters/2702206/draft-022026.md @@ -10,17 +10,17 @@ Qui dit nouvelle année, dit nouveau thème pour la Coupe de France de Robotique Après concertation avec l'équipe, nous avons choisi les actions qui nous semblaient les plus réalisables, tout en nous ramenant suffisamment de points. Dans cette newsletter, nous décrirons uniquement les actions que nous avons choisi de réaliser, avec la table de jeu suivante, table officielle de la CDFR 2026 : - ![Table de jeu - CDFR 2026](attachments/e63d3f2d-d3bf-4ed2-b486-fb2854401849.png " =493x275") +![Table de jeu - CDFR 2026](attachments/e63d3f2d-d3bf-4ed2-b486-fb2854401849.png " =493x275") Le robot principal partira de la zone 1, appelée le nid, pour aller vers la zone 8, où la première action a lieu : le curseur de température. À l'aide d'un bras actionné par un servo-moteur, le robot principal viendra déplacer un curseur le long de la table, où figure une échelle de température, et l'amener le plus proche des températures basses. - ![Position finale du thermomètre (espérée)](attachments/4be89b77-448f-4954-913f-b98b904b4e12.png " =269x251") +![Position finale du thermomètre (espérée)](attachments/4be89b77-448f-4954-913f-b98b904b4e12.png " =269x251") Après avoir déplacé le curseur, il passera par les zones 5 qui le séparent du nid, emportant avec lui les caisses de noisettes qui s'y trouvent, pour les ramener dans le nid, et s'y placer à son tour. La mission du robot principal est à présent terminée. Au tour de nos PAMIs de survivre à l'hiver, pendant les 15 dernières secondes du match : il leur faut atteindre des garde-mangers (zones 6), les occuper jusqu'à la fin du match et faire bouger un actionneur, visible depuis le public, même après la fin du match. -Cependant, un PAMI sort du lot : le PAMI ninja. Il a le droit d'évoluer durant toute la durée du match, il est placé dans la zone 2 (en hauteur par rapport aux autres robots) et travaille en collaboration avec le ninja de l'équipe adverse. Le ninja a 2 missions : vider les frigos (zones 3), jusqu'alors remplis de caisses de noisettes, et remplir les frigos (zones 3) des caisses de noisettes vides situées en zone de chargement (zones 4). Les actions réalisées à cet endroit de la table donnent des points aux 2 équipes. +Cependant, un PAMI sort du lot : le PAMI ninja. Il a le droit d'évoluer durant toute la durée du match, il est placé dans la zone 2 (en hauteur par rapport aux autres robots) et travaille en collaboration avec le ninja de l'équipe adverse. Le ninja a 2 missions : vider les frigos (zones 3), jusqu'alors remplis de caisses de noisettes, et remplir les frigos des caisses de noisettes vides situées en zone de chargement (zones 4). Les actions réalisées à cet endroit de la table donnent des points aux 2 équipes. Nous pouvons à présent détailler les spécificités de nos robots et de nos systèmes pour cette année : place au côté technique ! @@ -28,7 +28,7 @@ Nous pouvons à présent détailler les spécificités de nos robots et de nos s Le robot principal et le PAMI Ninja gardent la même architecture que l'année dernière : une Raspberry Pi 4 et notre PCB plein de connecteurs pour y brancher tous les capteurs et actionneurs. Le robot principal accueille tout de même quelques modifications : les systèmes liés à la CDFR 2025 sont retirés pour y ajouter deux bras permettant de déplacer le curseur du thermomètre (un pour chaque côté) ainsi que des améliorations sur le système d'estimation de position. En plus de ces modifications fonctionnelles, le robot principal a reçu un relooking complet ! - ![Le robot principal, avec un carénage en plexiglas et non en papier (c'est quand même plus stylé), aux couleurs de notre logo.](attachments/e0439810-b100-4580-8906-6052359aa179.png " =214x268") +![Le robot principal, avec un carénage en plexiglas et non en papier (c'est quand même plus stylé), aux couleurs de notre logo.](attachments/e0439810-b100-4580-8906-6052359aa179.png " =214x268") Concernant le PAMI Ninja, il était nécessaire de faire une petite adaptation pour que le capteur d'obstacle ne détecte pas les caisses de noisettes comme un obstacle et puisse les pousser. @@ -36,7 +36,7 @@ Concernant le PAMI Ninja, il était nécessaire de faire une petite adaptation p Pour déplacer le curseur sur le thermomètre représenté le long de la table, nous avons choisi de déployer un bras qui viendra se placer à l'intérieur du curseur, tandis que le robot avancera jusqu'à la zone de température froide. Pour cela, un peu de dimensionnement est nécessaire : il faut étudier l'écart entre le robot principal et le bord de la table afin de déterminer la longueur du bras. - ![Etude de l'écartement à la table](attachments/2a92d6eb-f05b-4344-b7ba-354325d62938.jpg " =375x211") +![Etude de l'écartement à la table](attachments/2a92d6eb-f05b-4344-b7ba-354325d62938.jpg " =375x211") Avec ces dimensions, qui respectent les contraintes de périmètre déployé, on peut construire notre bras et le système d'accroche sur l'étage principal du robot, qui lui permettra de se déployer au moment voulu. @@ -46,7 +46,9 @@ L'année dernière, en plus du gros PAMI Superstar (devenu cette année le PAMI Depuis le début de cette année, de nouveaux PCBs ont été désignés et un premier exemplaire a été soudé. Le PCB principal contient un microprocesseur STM32, le "cerveau" du PAMI, tandis que deux autres PCBs permettent de placer les capteurs d'obstacle et les boutons pour interagir avec le PAMI. Les premiers tests ont permis de montrer que les PCBs n'ont pas de grosses erreurs ! - ![Le PCB principal de nos nouveaux petits PAMIs lors de la pose des composants.](attachments/3ca55032-5f23-4738-8d3e-e380cb03d3b9.png " =428x250")Désormais, il s'agit de tester les composants qui permettront de détecter un obstacle, faire avancer le PAMI, et créer les pièces mécaniques qui constitueront le PAMI. +![Le PCB principal de nos nouveaux petits PAMIs lors de la pose des composants.](attachments/3ca55032-5f23-4738-8d3e-e380cb03d3b9.png " =428x250") + +Désormais, il s'agit de tester les composants qui permettront de détecter un obstacle, faire avancer le PAMI, et créer les pièces mécaniques qui constitueront le PAMI. On vous parlera de tout ça lors de notre prochaine newsletter, qui mettra *(normalement)* moins de temps à sortir que celle-ci… diff --git a/public/restricted-assets/cdroms/logos/cdroms-500x500.png b/public/restricted-assets/cdroms/logos/cdroms-500x500.png new file mode 100644 index 0000000..fcd43ae Binary files /dev/null and b/public/restricted-assets/cdroms/logos/cdroms-500x500.png differ diff --git a/public/restricted-assets/cdroms/members/sk.png b/public/restricted-assets/cdroms/members/sk.png index 5f0b422..ded1ce0 100644 Binary files a/public/restricted-assets/cdroms/members/sk.png and b/public/restricted-assets/cdroms/members/sk.png differ diff --git a/src/pages/Home.scss b/src/pages/Home.scss new file mode 100644 index 0000000..22b3910 --- /dev/null +++ b/src/pages/Home.scss @@ -0,0 +1,66 @@ + +.parallax { + height: calc(100vh - 42px); + width: 100vw; + overflow-x: hidden; + overflow-y: hidden; +} + +.parallax-layer { + position: absolute; + height: calc(100vh - 42px); + width: 100vw; +} + +.background { + background: url('../../public/assets/home.jpg') no-repeat fixed center; + z-index: 0; +} + +.logo-container { + display: flex; +} + +.logo { + margin: auto; + background: url('../../public/restricted-assets/cdroms/logos/cdroms-500x500.png') no-repeat center; + background-size: max(20vw, 20vh, 250px); + width: max(20vw, 20vh, 250px); + height: max(20vw, 20vh, 250px); + z-index: 1; + animation: 3s cubic-bezier(.22, .61, .36, 1) 0s normal forwards 1 appears; +} + +.logo-leaves { + animation: 3s cubic-bezier(.22, .61, .36, 1) 0s normal forwards 1 disappears; +} + +@keyframes appears { + 0% { + opacity: 0; + transform: translateX(-400px) rotate(-180deg); + } + 33% { + opacity: 0; + transform: translateX(-400px) rotate(-180deg); + } + 100% { + opacity: 1; + transform: translateX(0) rotate(0deg); + } +} + +@keyframes disappears { + 0% { + opacity: 1; + transform: translateX(0) rotate(0deg); + } + 33% { + opacity: 1; + transform: translateX(0) rotate(0deg); + } + 100% { + opacity: 0; + transform: translateX(400px) rotate(180deg); + } +} \ No newline at end of file diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index a6d9406..9291961 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,16 +1,60 @@ import * as React from "react"; - +import {useMemo, useState} from "react"; +import "./Home.scss"; const Home: React.FC = () => { + const [_, setDisappearsTrigger] = useState(null); + + const hideLogo = useMemo(() => { + return (logo: HTMLElement) => { + logo.style.display = "none"; + } + }, []); + const enterLogo = useMemo(() => { + return (event: React.MouseEvent) => { + // Prevent creating trigger while the logo appears + const logo = event.target as HTMLElement; + for (const animation of logo.getAnimations()) { + if (animation.playState != "finished") + return; + } + // Create a timeout to trigger the animation of disappearance + console.log("Trigger created") + const timeout = setTimeout(() => { + setDisappearsTrigger((trigger) => { + if (trigger != null) + clearTimeout(trigger); + return null + }); + const logo = event.target as HTMLElement; + console.log("Trigger triggered") + console.log(logo) + logo.classList.add("logo-leaves"); + logo.onanimationend = () => hideLogo(logo); + }, 5000); + setDisappearsTrigger(timeout); + } + }, [hideLogo, setDisappearsTrigger]); + const leaveLogo = useMemo(() => { + return () => { + setDisappearsTrigger((trigger) => { + if (trigger != null) { + console.log("Trigger removed") + clearTimeout(trigger); + } + return null + }); + } + }, [setDisappearsTrigger]); + return ( <> - image +
+
+
+
+
+
@@ -68,7 +112,6 @@ const Home: React.FC = () => {
- ) };