Browse Source

First commit

master
hadware 2 months ago
commit
2315709ad6
  1. 2
      .gitignore
  2. 63
      logo.svg
  3. 200
      main.py
  4. 61
      openscad/couvercle.scad
  5. 33
      openscad/inferieure.scad
  6. 76
      openscad/parametres.scad
  7. 80
      openscad/superieure.scad
  8. 9
      pyproject.toml
  9. 297
      tractrudeuse.ipynb
  10. 2080
      uv.lock

2
.gitignore vendored

@ -0,0 +1,2 @@
exports
.idea/

63
logo.svg

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="45.95459mm"
height="36.433193mm"
viewBox="0 0 45.95459 36.433193"
version="1.1"
id="svg1"
inkscape:version="1.4.3 (1:1.4.3+202512261035+0d15f75042)"
sodipodi:docname="design_jeton.svg"
inkscape:export-filename="design_jeton.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="8.6595801"
inkscape:cx="1119.8003"
inkscape:cy="519.30924"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg1">
<inkscape:page
x="0"
y="0"
width="45.95459"
height="36.433193"
id="page1"
margin="0"
bleed="0" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Calque 2"
inkscape:export-filename="jeton_recto.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
transform="translate(-274.69442,-120.30951)">
<path
id="path38"
style="stroke-width:3.69297"
d="m 300.55478,120.33477 c -5.57921,-0.31612 -11.14216,2.35687 -14.46115,6.83797 -1.11672,1.50843 -1.75597,2.7358 -3.76341,3.19107 -2.82988,1.06566 -5.43857,3.33031 -6.89565,5.95056 -1.67493,3.47793 -0.30083,7.60014 2.06177,10.40528 4.21037,5.21133 10.89135,7.32352 17.2148,8.59449 5.22016,1.02118 10.57154,1.89072 15.8925,1.15427 3.49182,-0.28583 7.46063,-1.0955 9.60127,-4.14091 1.15534,-2.02032 -0.16066,-4.29603 -1.46933,-5.87398 2.28697,-4.77415 2.14814,-10.521 -0.214,-15.24306 0.69421,-3.74393 -1.07119,-8.21646 -4.88259,-9.52507 -2.55137,-1.05033 -5.36768,-0.82013 -8.04228,-0.68694 -1.64475,-0.43422 -3.34587,-0.61047 -5.04193,-0.66368 z m 8.15764,1.50844 c 3.16648,-0.0812 6.89815,0.95142 8.21906,4.15012 0.66101,1.3342 1.28928,4.64137 0.21925,4.96842 -1.48118,-1.59846 -2.53694,-4.18999 -5.08139,-4.20363 -3.92982,-0.20123 -7.28797,2.38262 -11.11666,2.72893 -2.0277,0.4686 -4.26446,-1.53564 -3.10241,-3.55861 1.2142,-2.34784 4.09239,-2.89233 6.41582,-3.55924 1.45811,-0.32946 2.95058,-0.5189 4.44633,-0.52599 z m -15.04197,7.45432 c 1.52643,-0.46956 1.05251,0.95134 1.14761,1.84898 0.39619,1.65489 0.2805,4.6425 -2.04808,4.34715 -0.88133,-0.29442 -1.31224,-2.669 -2.50638,-1.13973 0.1522,-1.11794 0.62297,-1.50888 -0.91732,-1.74496 -0.82909,-1.6585 1.7865,-3.2261 4.32391,-3.31134 z m -10.51512,2.80293 c -0.30825,1.75814 -0.69354,3.7938 0.11613,5.54709 2.16944,4.85531 6.6218,8.19862 11.094,10.81123 5.13142,2.80647 11.0116,5.014 16.94436,4.21731 2.47539,-0.35282 4.88595,-1.5921 6.14919,-3.8316 0.8946,-1.14394 1.86472,2.61143 0.67805,3.02903 -3.08646,2.71062 -7.48452,2.69645 -11.36197,2.75399 -5.57726,0.13705 -11.07258,-1.03092 -16.44209,-2.41948 -5.40032,-1.46737 -11.04766,-4.5288 -13.22654,-9.98156 -1.02207,-2.51108 -0.30819,-5.50838 1.73199,-7.29877 1.20502,-1.23433 2.62109,-2.37835 4.31688,-2.82724 z m 5.95932,2.19057 c 2.42288,0.0403 2.47822,3.4653 0.87751,4.65021 -1.93079,1.96294 -4.465,-0.95914 -3.14559,-2.95212 0.4108,-0.88564 1.25676,-1.64878 2.26808,-1.69809 z"
sodipodi:nodetypes="cccccccccccccccccccccscccccsccccccccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

200
main.py

@ -0,0 +1,200 @@
"""
punch-guide build123d
4 pièces : superieure, inferieure, couvercle, (bac)
Exporte chaque pièce en STEP et STL dans ./exports/
"""
from build123d import *
# ─────────────────────────────────────────────────────────────────────────────
# PARAMÈTRES — modifier ici uniquement
# ─────────────────────────────────────────────────────────────────────────────
# Fente (guide tract) — face droite, centrée en Y
fente_largeur = 152.0 # mm — largeur de la fente
fente_profondeur = 60.0 # mm — profondeur (longueur de la rainure)
fente_section = 2.0 # mm — hauteur de la section (épaisseur papier + jeu)
fente_z = 15.0 # mm — hauteur depuis le fond intérieur du chapeau
# Cavité perforatrice
cav_largeur = 100.0 # mm — X
cav_profondeur = 85.0 # mm — Y
cav_hauteur = 55.0 # mm — profondeur de creusement depuis le dessus
cav_offset_y = 30.0 # mm — décalage en Y depuis la paroi arrière
# Trou d'évacuation (confettis)
evac_taille = 48.0 # mm — côté du carré (45 + jeu)
evac_offset_x = 40.0 # mm — depuis paroi gauche intérieure
evac_offset_y = 50.0 # mm — depuis paroi arrière intérieure
# Parois
epaisseur_paroi = 5.0 # mm
epaisseur_fond = 10.0 # mm
# Emboîtement
emboitement_profondeur = 10.0 # mm
emboitement_jeu = 0.3 # mm
# Couvercle
couvercle_epaisseur = 3.0 # mm
couvercle_ouv_largeur = 70.0 # mm — ouverture X
couvercle_ouv_profondeur = 50.0 # mm — ouverture Y
# Bac
bac_hauteur = 50.0 # mm
# ─────────────────────────────────────────────────────────────────────────────
# DIMENSIONS DÉRIVÉES
# ─────────────────────────────────────────────────────────────────────────────
chapeau_int_x = max(cav_largeur, fente_profondeur)
chapeau_int_y = max(cav_profondeur + cav_offset_y, fente_largeur)
chapeau_ext_x = chapeau_int_x + 2 * epaisseur_paroi
chapeau_ext_y = chapeau_int_y + 2 * epaisseur_paroi
chapeau_ext_z = epaisseur_fond + cav_hauteur
# Position de la cavité dans le chapeau
cav_x = (chapeau_ext_x - cav_largeur) / 2 # centrée en X
cav_y = epaisseur_paroi + cav_offset_y # paroi arrière + offset
# Fente
fente_y = (chapeau_ext_y - fente_largeur) / 2 # centrée en Y sur la face
fente_z_abs = epaisseur_fond + fente_z # hauteur absolue depuis le bas
# Bac
bac_ext_x = chapeau_ext_x - 2 * emboitement_jeu
bac_ext_y = chapeau_ext_y - 2 * emboitement_jeu
# Couvercle
couv_ext_x = chapeau_ext_x + 2 * (epaisseur_paroi + emboitement_jeu)
couv_ext_y = chapeau_ext_y + 2 * (epaisseur_paroi + emboitement_jeu)
couv_ext_z = couvercle_epaisseur + emboitement_profondeur
offset_couv = epaisseur_paroi + emboitement_jeu
cav_centre_x = offset_couv + epaisseur_paroi + chapeau_int_x / 2
cav_centre_y = offset_couv + epaisseur_paroi + cav_offset_y + cav_profondeur / 2
# ─────────────────────────────────────────────────────────────────────────────
# HELPER — boîte à l'origine, coin bas-gauche en (0,0,0)
# build123d centre les Box sur l'origine par défaut,
# on les translate pour les aligner comme en OpenSCAD.
# ─────────────────────────────────────────────────────────────────────────────
def box(x, y, z):
"""Boîte pleine, coin bas-gauche-avant en (0,0,0)."""
return Box(x, y, z).translate((x/2, y/2, z/2))
# ─────────────────────────────────────────────────────────────────────────────
# PARTIE SUPÉRIEURE (chapeau)
# ─────────────────────────────────────────────────────────────────────────────
def make_superieure():
# 1. Corps principal
corps = box(chapeau_ext_x, chapeau_ext_y, chapeau_ext_z)
# 2. Lèvre d'emboîtement (cadre périphérique en bas)
levre_bloc = box(chapeau_ext_x, chapeau_ext_y, emboitement_profondeur)
levre_vide = box(
chapeau_ext_x - 2 * epaisseur_paroi,
chapeau_ext_y - 2 * epaisseur_paroi,
emboitement_profondeur + 1
).translate((epaisseur_paroi, epaisseur_paroi, 0))
levre = levre_bloc - levre_vide
levre = levre.translate((0, 0, -emboitement_profondeur))
base = corps + levre
# 3. Cavité perforatrice (ouverte vers le haut)
cavite = box(cav_largeur, cav_profondeur, cav_hauteur + 1)
cavite = cavite.translate((cav_x, cav_y, epaisseur_fond))
# 4. Fente latérale (débouche sur la face droite, X max)
fente = box(fente_profondeur + 1, fente_largeur, fente_section)
fente = fente.translate((
chapeau_ext_x - fente_profondeur,
fente_y,
fente_z_abs
))
# 5. Trou d'évacuation (perce le fond depuis en dessous)
evac = box(evac_taille, evac_taille, epaisseur_fond + 2)
evac = evac.translate((
epaisseur_paroi + evac_offset_x,
epaisseur_paroi + evac_offset_y,
-1
))
return base - cavite - fente - evac
# ─────────────────────────────────────────────────────────────────────────────
# PARTIE INFÉRIEURE (bac)
# ─────────────────────────────────────────────────────────────────────────────
def make_inferieure():
# 1. Bloc de base
bac = box(bac_ext_x, bac_ext_y, bac_hauteur)
# 2. Vidage intérieur (ouvert par le haut)
vide = box(
bac_ext_x - 2 * epaisseur_paroi,
bac_ext_y - 2 * epaisseur_paroi,
bac_hauteur
).translate((epaisseur_paroi, epaisseur_paroi, epaisseur_fond))
return bac - vide
# ─────────────────────────────────────────────────────────────────────────────
# COUVERCLE
# ─────────────────────────────────────────────────────────────────────────────
def make_couvercle():
# 1. Bloc de base
bloc = box(couv_ext_x, couv_ext_y, couv_ext_z)
# 2. Évidement intérieur (lèvre qui coiffe le chapeau)
vide_levre = box(
couv_ext_x - 2 * epaisseur_paroi,
couv_ext_y - 2 * epaisseur_paroi,
emboitement_profondeur + 1
).translate((epaisseur_paroi, epaisseur_paroi, -1))
# 3. Ouverture rectangulaire centrée sur la cavité
ouverture = box(couvercle_ouv_largeur, couvercle_ouv_profondeur, couvercle_epaisseur + 2)
ouverture = ouverture.translate((
cav_centre_x - couvercle_ouv_largeur / 2,
cav_centre_y - couvercle_ouv_profondeur / 2,
emboitement_profondeur - 1
))
return bloc - vide_levre - ouverture
# ─────────────────────────────────────────────────────────────────────────────
# EXPORT
# ─────────────────────────────────────────────────────────────────────────────
import os
os.makedirs("exports", exist_ok=True)
pieces = {
"superieure": make_superieure,
"inferieure": make_inferieure,
"couvercle": make_couvercle,
}
for nom, fn in pieces.items():
print(f"Construction : {nom}...")
piece = fn()
export_step(piece, f"exports/{nom}.step")
export_stl(piece, f"exports/{nom}.stl")
print(f" → exports/{nom}.step / .stl")
print("Terminé.")

61
openscad/couvercle.scad

@ -0,0 +1,61 @@
// =============================================
// COUVERCLE.scad
// Coiffe la partie supérieure par l'extérieur,
// même principe d'emboîtement que chapeau/bac.
// Ouverture rectangulaire centrée sur la cavité.
// =============================================
include <parametres.scad>
couvercle();
module couvercle() {
// Dimensions extérieures du couvercle.
// La lèvre intérieure doit glisser autour du chapeau :
// on ajoute le jeu + l'épaisseur de paroi de chaque côté.
couv_ext_x = chapeau_ext_x + 2 * (epaisseur_paroi + emboitement_jeu);
couv_ext_y = chapeau_ext_y + 2 * (epaisseur_paroi + emboitement_jeu);
couv_ext_z = couvercle_epaisseur + emboitement_profondeur;
// Centre de la cavité en X/Y dans le repère du couvercle.
// La cavité est centrée en X dans le chapeau, et positionnée en Y par cav_offset_y.
// Dans le repère du couvercle, on décale de epaisseur_paroi + emboitement_jeu
// pour compenser le débord de la lèvre.
offset_couv = epaisseur_paroi + emboitement_jeu; // décalage repère chapeau couvercle
cav_centre_x = offset_couv + epaisseur_paroi + chapeau_int_x / 2;
cav_centre_y = offset_couv + epaisseur_paroi + cav_offset_y + cav_profondeur / 2;
difference() {
// 1. BLOC DE BASE
cube([couv_ext_x, couv_ext_y, couv_ext_z]);
// 2. ÉVIDEMENT INTÉRIEUR lèvre d'emboîtement
// On creuse le dessous du couvercle pour qu'il vienne coiffer
// le chapeau. La lèvre a l'épaisseur epaisseur_paroi.
translate([epaisseur_paroi, epaisseur_paroi, -1])
cube([
couv_ext_x - 2 * epaisseur_paroi,
couv_ext_y - 2 * epaisseur_paroi,
emboitement_profondeur + 1 // s'arrête au plateau
]);
// 3. OUVERTURE RECTANGULAIRE
// Trou traversant le plateau, centré sur la cavité perforatrice.
translate([
cav_centre_x - couvercle_ouv_largeur / 2,
cav_centre_y - couvercle_ouv_profondeur / 2,
emboitement_profondeur - 1 // perce depuis le plateau vers le haut
])
cube([
couvercle_ouv_largeur,
couvercle_ouv_profondeur,
couvercle_epaisseur + 2
]);
}
}

33
openscad/inferieure.scad

@ -0,0 +1,33 @@
// =============================================
// INFERIEURE.scad Le bac de récupération
// Boîte ouverte sur le dessus.
// Dimensions dérivées du chapeau.
// =============================================
include <parametres.scad>
inferieure();
module inferieure() {
// Dimensions extérieures du bac.
// Le bac doit s'insérer dans la lèvre du chapeau :
// on retire le jeu d'emboîtement sur chaque face.
bac_ext_x = chapeau_ext_x - 2 * emboitement_jeu;
bac_ext_y = chapeau_ext_y - 2 * emboitement_jeu;
difference() {
// 1. BLOC DE BASE
cube([bac_ext_x, bac_ext_y, bac_hauteur]);
// 2. VIDAGE INTÉRIEUR (ouvert par le haut)
translate([epaisseur_paroi, epaisseur_paroi, epaisseur_fond])
cube([
bac_ext_x - 2 * epaisseur_paroi,
bac_ext_y - 2 * epaisseur_paroi,
bac_hauteur // traversant vers le haut
]);
}
}

76
openscad/parametres.scad

@ -0,0 +1,76 @@
// =============================================
// PARAMETRES GLOBAUX punch-guide
// Modifier uniquement ce fichier.
// =============================================
// FENTE (guide tract)
// La fente est centrée sur la face droite du chapeau.
fente_largeur = 152; // mm largeur de la fente (tract 105mm + jeu latéral)
fente_profondeur = 60; // mm profondeur d'enfoncement du tract jusqu'à la butée
fente_section = 2.0; // mm hauteur de la section (épaisseur papier + jeu vertical)
fente_z = 15; // mm hauteur de la fente depuis le fond intérieur du chapeau
// CAVITÉ PERFORATRICE
// Pavé ouvert vers le haut, dans lequel la perforatrice s'encastre.
cav_largeur = 100; // mm dimension X de la cavité <<<< À MESURER
cav_profondeur = 85; // mm dimension Y de la cavité <<<< À MESURER
cav_hauteur = 55; // mm profondeur du creusement depuis le dessus <<<< À MESURER
// Décalage de la cavité sur l'axe Y (perpendiculaire à la face de la fente).
// Permet d'aligner la lame avec l'axe de la fente.
// 0 = cavité calée contre la paroi arrière (opposée à la fente).
cav_offset_y = 30; // mm <<<< À AJUSTER
// TROU D'ÉVACUATION
// Trou carré dans le fond du chapeau, centré sous la lame.
// Les confettis tombent dans le bac.
evac_taille = 48; // mm côté du trou (carré 45mm + jeu)
evac_offset_x = 40; // mm position X depuis la paroi gauche intérieure <<<< À AJUSTER
evac_offset_y = 50; // mm position Y depuis la paroi arrière intérieure <<<< À AJUSTER
// PAROIS
epaisseur_paroi = 5; // mm
epaisseur_fond = 10; // mm
// EMBOÎTEMENT chapeau / bac
emboitement_profondeur = 10; // mm hauteur du chevauchement
emboitement_jeu = 0.3; // mm jeu radial entre lèvre et bac (ajuster selon imprimante)
// COUVERCLE
// Le couvercle coiffe le chapeau par l'extérieur (même système que chapeau/bac).
// Il a une ouverture rectangulaire centrée sur la cavité de la perforatrice.
couvercle_epaisseur = 3; // mm épaisseur du plateau du couvercle
couvercle_ouv_largeur = 70; // mm largeur de l'ouverture / X
couvercle_ouv_profondeur = 50; // mm profondeur de l'ouverture / Y
// BAC DE RÉCUPÉRATION
bac_hauteur = 50; // mm hauteur utile du bac
// DIMENSIONS DÉRIVÉES (ne pas modifier)
//
// Le chapeau englobe tout :
// X : le plus grand entre la cavité et la fente, + 2 × paroi
// Y : le plus grand entre (cavité + son offset) et la profondeur de fente, + 2 × paroi
// Z : fond + hauteur de cavité (la cavité ouvre le dessus, donc elle fixe la hauteur)
chapeau_int_x = max(cav_largeur, fente_profondeur);
chapeau_int_y = max(cav_profondeur + cav_offset_y, fente_largeur);
chapeau_ext_x = chapeau_int_x + 2 * epaisseur_paroi;
chapeau_ext_y = chapeau_int_y + 2 * epaisseur_paroi;
chapeau_ext_z = epaisseur_fond + cav_hauteur;

80
openscad/superieure.scad

@ -0,0 +1,80 @@
// =============================================
// SUPERIEURE.scad Le chapeau
// =============================================
include <parametres.scad>
superieure();
module superieure() {
difference() {
// 1. BLOC DE BASE + LÈVRE D'EMBOÎTEMENT
union() {
// Corps principal du chapeau
cube([chapeau_ext_x, chapeau_ext_y, chapeau_ext_z]);
// Lèvre d'emboîtement cadre périphérique en bas du chapeau,
// qui vient coiffer le bac par glissement vers le bas.
translate([0, 0, -emboitement_profondeur])
difference() {
// Bloc extérieur de la lèvre (même empreinte que le chapeau)
cube([chapeau_ext_x, chapeau_ext_y, emboitement_profondeur]);
// On vide l'intérieur : ne reste que le cadre
translate([epaisseur_paroi, epaisseur_paroi, -1])
cube([
chapeau_ext_x - 2 * epaisseur_paroi,
chapeau_ext_y - 2 * epaisseur_paroi,
emboitement_profondeur + 2
]);
}
}
// 2. CAVITÉ PERFORATRICE
// Pavé creusé depuis le dessus, ouvert vers le haut.
// Centré en X, décalé en Y selon cav_offset_y.
//
// Centrage en X dans le chapeau :
cav_x = (chapeau_ext_x - cav_largeur) / 2;
// Position en Y : paroi arrière + offset
cav_y = epaisseur_paroi + cav_offset_y;
// La cavité part du dessus et descend de cav_hauteur
cav_z = epaisseur_fond; // démarre au-dessus du fond
translate([cav_x, cav_y, cav_z])
cube([cav_largeur, cav_profondeur, cav_hauteur + 1]); // +1 pour sortir du dessus
// 3. FENTE LATÉRALE guide tract (face DROITE, centrée)
// Rainure horizontale débouchant sur la face droite (X max).
// Centrée en Y sur la face.
// La butée est le fond de la rainure (paroi non percée).
//
// Centre de la fente en Y = centre du chapeau
fente_y = (chapeau_ext_y - fente_largeur) / 2;
// Hauteur absolue de la fente (depuis le bas du chapeau)
fente_z_abs = epaisseur_fond + fente_z;
translate([
chapeau_ext_x - fente_profondeur, // la fente entre par la droite
fente_y,
fente_z_abs
])
cube([fente_profondeur + 1, fente_largeur, fente_section]); // +1 pour déboucher
// 4. TROU D'ÉVACUATION DES CONFETTIS
// Trou carré dans le fond, positionné par evac_offset_x/y
// depuis le coin intérieur bas-gauche / arrière.
translate([
epaisseur_paroi + evac_offset_x,
epaisseur_paroi + evac_offset_y,
-1 // perce depuis en dessous
])
cube([evac_taille, evac_taille, epaisseur_fond + 2]);
}
}

9
pyproject.toml

@ -0,0 +1,9 @@
[project]
name = "tractrudeuse"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"build123d",
"ipykernel>=7.2.0",
"ocp-vscode",
]

297
tractrudeuse.ipynb

@ -0,0 +1,297 @@
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2026-05-08T19:21:25.951438147Z",
"start_time": "2026-05-08T19:21:24.452607608Z"
}
},
"source": [
"\"\"\"\n",
"punch-guide — build123d\n",
"4 pièces : superieure, inferieure, couvercle, (bac)\n",
"Exporte chaque pièce en STEP et STL dans ./exports/\n",
"\"\"\"\n",
"\n",
"from build123d import *\n",
"\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"# PARAMÈTRES — modifier ici uniquement\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"\n",
"# Fente (guide tract) — face droite, centrée en Y\n",
"fente_largeur = 152.0 # mm — largeur de la fente\n",
"fente_profondeur = 60.0 # mm — profondeur (longueur de la rainure)\n",
"fente_section = 2.0 # mm — hauteur de la section (épaisseur papier + jeu)\n",
"fente_z = 15.0 # mm — hauteur depuis le fond intérieur du chapeau\n",
"\n",
"# Cavité perforatrice\n",
"cav_largeur = 100.0 # mm — X\n",
"cav_profondeur = 85.0 # mm — Y\n",
"cav_hauteur = 55.0 # mm — profondeur de creusement depuis le dessus\n",
"cav_offset_y = 30.0 # mm — décalage en Y depuis la paroi arrière\n",
"\n",
"# Trou d'évacuation (confettis)\n",
"evac_taille = 48.0 # mm — côté du carré (45 + jeu)\n",
"evac_offset_x = 40.0 # mm — depuis paroi gauche intérieure\n",
"evac_offset_y = 50.0 # mm — depuis paroi arrière intérieure\n",
"\n",
"# Parois\n",
"epaisseur_paroi = 5.0 # mm\n",
"epaisseur_fond = 10.0 # mm\n",
"\n",
"# Emboîtement\n",
"emboitement_profondeur = 10.0 # mm\n",
"emboitement_jeu = 0.3 # mm\n",
"\n",
"# Couvercle\n",
"couvercle_epaisseur = 3.0 # mm\n",
"couvercle_ouv_largeur = 70.0 # mm — ouverture X\n",
"couvercle_ouv_profondeur = 50.0 # mm — ouverture Y\n",
"\n",
"# Bac\n",
"bac_hauteur = 50.0 # mm\n",
"\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"# DIMENSIONS DÉRIVÉES\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"\n",
"chapeau_int_x = max(cav_largeur, fente_profondeur)\n",
"chapeau_int_y = max(cav_profondeur + cav_offset_y, fente_largeur)\n",
"\n",
"chapeau_ext_x = chapeau_int_x + 2 * epaisseur_paroi\n",
"chapeau_ext_y = chapeau_int_y + 2 * epaisseur_paroi\n",
"chapeau_ext_z = epaisseur_fond + cav_hauteur\n",
"\n",
"# Position de la cavité dans le chapeau\n",
"cav_x = (chapeau_ext_x - cav_largeur) / 2 # centrée en X\n",
"cav_y = epaisseur_paroi + cav_offset_y # paroi arrière + offset\n",
"\n",
"# Fente\n",
"fente_y = (chapeau_ext_y - fente_largeur) / 2 # centrée en Y sur la face\n",
"fente_z_abs = epaisseur_fond + fente_z # hauteur absolue depuis le bas\n",
"\n",
"# Bac\n",
"bac_ext_x = chapeau_ext_x + 2 * (epaisseur_paroi + emboitement_jeu)\n",
"bac_ext_y = chapeau_ext_y + 2 * (epaisseur_paroi + emboitement_jeu)\n",
"\n",
"# Couvercle\n",
"couv_ext_x = chapeau_ext_x + 2 * (epaisseur_paroi + emboitement_jeu)\n",
"couv_ext_y = chapeau_ext_y + 2 * (epaisseur_paroi + emboitement_jeu)\n",
"couv_ext_z = couvercle_epaisseur + emboitement_profondeur\n",
"\n",
"offset_couv = epaisseur_paroi + emboitement_jeu\n",
"cav_centre_x = offset_couv + epaisseur_paroi + chapeau_int_x / 2\n",
"cav_centre_y = offset_couv + epaisseur_paroi + cav_offset_y + cav_profondeur / 2\n",
"\n",
"\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"# PARTIE SUPÉRIEURE (chapeau)\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"\n",
"def make_superieure():\n",
" with BuildPart() as p:\n",
" # 1. Corps principal\n",
" with Locations(Location((chapeau_ext_x / 2, chapeau_ext_y / 2, chapeau_ext_z / 2))):\n",
" Box(chapeau_ext_x, chapeau_ext_y, chapeau_ext_z)\n",
"\n",
" chamfer(edges().filter_by(Axis.Z), length=2)\n",
"\n",
" # 2. Cavité perforatrice (ouverte vers le haut)\n",
" with Locations(Location((\n",
" cav_x + cav_largeur / 2,\n",
" cav_y + cav_profondeur / 2,\n",
" epaisseur_fond + (cav_hauteur + 1) / 2,\n",
" ))):\n",
" Box(cav_largeur, cav_profondeur, cav_hauteur + 1, mode=Mode.SUBTRACT)\n",
"\n",
" # 3. Fente latérale (débouche sur la face droite, X max)\n",
" with Locations(Location((\n",
" chapeau_ext_x - fente_profondeur + (fente_profondeur + 1) / 2,\n",
" fente_y + fente_largeur / 2,\n",
" fente_z_abs + fente_section / 2,\n",
" ))):\n",
" Box(fente_profondeur + 1, fente_largeur, fente_section, mode=Mode.SUBTRACT)\n",
"\n",
" # 4. Trou d'évacuation (perce le fond depuis en dessous)\n",
" with Locations(Location((\n",
" epaisseur_paroi + evac_offset_x + evac_taille / 2,\n",
" epaisseur_paroi + evac_offset_y + evac_taille / 2,\n",
" -1 + (epaisseur_fond + 2) / 2,\n",
" ))):\n",
" Box(evac_taille, evac_taille, epaisseur_fond + 2, mode=Mode.SUBTRACT)\n",
"\n",
" # Chanfrein sur le pourtour supérieur du trou\n",
" evac_top = edges().filter_by_position(Axis.Z, epaisseur_fond - 0.01, epaisseur_fond + 0.01)\n",
" chamfer(evac_top, length=2)\n",
"\n",
" # chamfrein sur le pourtour de la fente\n",
" fente_opening = (\n",
" faces().sort_by(Axis.X)[-1]\n",
" .edges()\n",
" .filter_by_position(Axis.Z, fente_z_abs - 0.01, fente_z_abs + fente_section + 0.01)\n",
" )\n",
" chamfer(fente_opening, length=0.5)\n",
"\n",
" # Trous de boulons pour fixer la trouyoteuse\n",
" cav_bottom = faces().filter_by_position(\n",
" Axis.Z, epaisseur_fond - 0.01, epaisseur_fond + 0.01\n",
" )[0]\n",
"\n",
" with Locations(cav_bottom):\n",
" with GridLocations(50, 60, 2, 2):\n",
" Hole(radius=3)\n",
"\n",
" # Plan centré sur la face droite, au-dessus de la fente\n",
" z_logo = fente_z_abs + fente_section + 5\n",
" plan_logo = Plane(\n",
" origin=Vector(chapeau_ext_x, chapeau_ext_y / 2, z_logo),\n",
" x_dir=Vector(0, 1, 0), # Y global → X local\n",
" z_dir=Vector(1, 0, 0), # normale sortante (X+)\n",
" )\n",
"\n",
" logo_shapes = import_svg(\"logo.svg\")\n",
" min_x = min(s.bounding_box().min.X for s in logo_shapes)\n",
" max_x = max(s.bounding_box().max.X for s in logo_shapes)\n",
" cx = (min_x + max_x) / 2\n",
"\n",
" with BuildSketch(plan_logo) as sk:\n",
" add([s.translate((-cx, 0, 0)) for s in logo_shapes])\n",
" scale(by=0.7)\n",
"\n",
" extrude(amount=-1.5, mode=Mode.SUBTRACT) # 1.5 mm de profondeur\n",
"\n",
"\n",
" return p.part\n",
"\n",
"\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"# PARTIE INFÉRIEURE (bac)\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"\n",
"def make_inferieure():\n",
" with BuildPart() as p:\n",
" # Corps extérieur\n",
" with Locations(Location((bac_ext_x / 2, bac_ext_y / 2, bac_hauteur / 2))):\n",
" Box(bac_ext_x, bac_ext_y, bac_hauteur)\n",
"\n",
" top_edges = faces().sort_by(Axis.Z)[-1].edges()\n",
" lateral_edges = edges().filter_by(Axis.Z)\n",
" chamfer(top_edges + lateral_edges, length=2)\n",
"\n",
" # Lèvre intérieure d'emboitement\n",
" with Locations(Location((bac_ext_x / 2, bac_ext_y / 2, bac_hauteur))):\n",
" Box(\n",
" bac_ext_x - 2 * epaisseur_paroi,\n",
" bac_ext_y - 2 * epaisseur_paroi,\n",
" emboitement_profondeur,\n",
" mode=Mode.SUBTRACT,\n",
" )\n",
" # Vidage intérieur — ouvert par le haut\n",
" with Locations(Location((bac_ext_x / 2, bac_ext_y / 2, epaisseur_fond + bac_hauteur / 2))):\n",
" Box(\n",
" bac_ext_x - 3 * epaisseur_paroi,\n",
" bac_ext_y - 3 * epaisseur_paroi,\n",
" bac_hauteur,\n",
" mode=Mode.SUBTRACT,\n",
" )\n",
"\n",
" return p.part\n",
"\n",
"\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"# COUVERCLE\n",
"# ─────────────────────────────────────────────────────────────────────────────\n",
"\n",
"def make_couvercle():\n",
" with BuildPart() as p:\n",
" # 1. Bloc de base\n",
" with Locations(Location((couv_ext_x / 2, couv_ext_y / 2, couv_ext_z / 2))):\n",
" Box(couv_ext_x, couv_ext_y, couv_ext_z)\n",
"\n",
" # 2. Évidement intérieur (lèvre qui coiffe le chapeau)\n",
" with Locations(Location((couv_ext_x / 2, couv_ext_y / 2, (emboitement_profondeur - 1) / 2))):\n",
" Box(\n",
" couv_ext_x - 2 * epaisseur_paroi,\n",
" couv_ext_y - 2 * epaisseur_paroi,\n",
" emboitement_profondeur + 1,\n",
" mode=Mode.SUBTRACT,\n",
" )\n",
"\n",
" # 3. Ouverture rectangulaire centrée sur la cavité\n",
" ouv_z = emboitement_profondeur - 1 + (couvercle_epaisseur + 2) / 2\n",
" with Locations(Location((cav_centre_x, cav_centre_y, ouv_z))):\n",
" Box(\n",
" couvercle_ouv_largeur,\n",
" couvercle_ouv_profondeur,\n",
" couvercle_epaisseur + 2,\n",
" mode=Mode.SUBTRACT,\n",
" )\n",
"\n",
" # 4. Chanfrein — arêtes du dessus + arêtes latérales (verticales)\n",
" top_edges = faces().sort_by(Axis.Z)[-1].edges()\n",
" lateral_edges = edges().filter_by(Axis.Z)\n",
" chamfer(top_edges + lateral_edges, length=2)\n",
"\n",
" return p.part\n",
"\n",
"\n",
"def center_xy(part):\n",
" bb = part.bounding_box()\n",
" return part.translate((-bb.center().X, -bb.center().Y, 0))\n",
"\n",
"\n",
"show(\n",
" center_xy(make_superieure()),\n",
" center_xy(make_inferieure()).translate((0, 0, -(bac_hauteur + gap))),\n",
" center_xy(make_couvercle()).translate((0, 0, chapeau_ext_z + gap)),\n",
" names=[\"superieure\", \"inferieure\", \"couvercle\"],\n",
" colors=[\"#ff7800\", \"#000000\", \"#000000\"],\n",
")"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"+cc\n"
]
}
],
"execution_count": 75
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": "",
"id": "810fb3a101c2fccd"
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

2080
uv.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save