"""06 — Les deux sols du quartier (2035 et aujourd'hui), depuis le plan vectoriel Illustrator. Entrées : TRANSFO_TOTAL.pdf (Illustrator, 155 calques), la planche de fond du concours (lavis), la texture de papier aquarelle, transfo.json (recalage), scene.json (axes des routes). Sorties : lignes2035.png, lignesauj.png, sol2035.jpg, solauj.jpg. Principe : on éteint dans le PDF les calques qui deviendront des objets 3D (arbres, ombres, personnages) ; pour « aujourd'hui », on éteint aussi tous les calques « 8_ NEW » et « 10_NEW ». Le lavis (jardins vert pâle, routes grises, forêt ou champ) est multiplié sous les traits, avec un grain de papier passe-haut (les grandes taches de la texture sont retirées). Usage : python 06_sols.py [dpi=155] """ import sys, json import numpy as np import pymupdf from PIL import Image, ImageDraw, ImageFilter PDF, PAPIER = sys.argv[1], sys.argv[2] DPI = int(sys.argv[3]) if len(sys.argv) > 3 else 155 T = json.load(open("transfo.json")); s = T["s"] # px/m à 100 dpi SC = json.load(open("scene.json")) def rendre(eteindre, sortie): doc = pymupdf.open(PDF) for u in doc.layer_ui_configs(): if eteindre(u["text"]): doc.set_layer_ui_config(u["number"], action=2) # 2 = éteint doc[0].get_pixmap(dpi=DPI).save(sortie) objets_3d = lambda t: any(k in t for k in ["FORET", "VEGETATION", "ombres", "OMBRES", "PERSOS", "Calque 37"]) or t == "0" rendre(objets_3d, "lignes2035.png") rendre(lambda t: objets_3d(t) or t.startswith("8_ NEW") or t.startswith("10_NEW"), "lignesauj.png") L35 = Image.open("lignes2035.png").convert("RGB"); Wt, Ht = L35.size; k = Wt / 1654 tex = Image.open(PAPIER).convert("L").resize((Wt // 2, Ht // 2)) t = np.asarray(tex).astype(np.float32); lo = np.asarray(tex.filter(ImageFilter.GaussianBlur(14))).astype(np.float32) g = np.clip((t - lo) / ((t - lo).std() + 1e-6), -2.5, 2.5) g = np.asarray(Image.fromarray(((g + 2.5) / 5 * 255).astype(np.uint8)).resize((Wt, Ht), Image.BILINEAR)).astype(np.float32) / 255 - 0.5 pap = 1.0 + 0.035 * g routes = {n: [(x * s + 827, z * s + 585) for x, z in pts] for n, pts in SC["routes"].items()} largeur = {"principale": 9.5, "verticale": 7.5, "anneau": 6.5, "liaison_anneau": 6.5, "droite": 7.0} foret = [(1335, 0), (1654, 0), (1654, 780), (1565, 752), (1160, 596), (1330, 40)] # relevé sur la planche def lavis(couleur_est): w = Image.new("RGB", (Wt, Ht), (239, 240, 219)); d = ImageDraw.Draw(w) d.polygon([(x * k, y * k) for x, y in foret], fill=couleur_est) for n, pts in routes.items(): wpx = largeur[n] * s * k d.line([(x * k, y * k) for x, y in pts], fill=(229, 229, 224), width=int(wpx), joint="curve") for x, y in pts: d.ellipse([x * k - wpx / 2, y * k - wpx / 2, x * k + wpx / 2, y * k + wpx / 2], fill=(229, 229, 224)) return np.asarray(w.filter(ImageFilter.GaussianBlur(2))).astype(np.float32) * pap[..., None] def composer(lignes, lav, sortie): l = np.asarray(lignes.convert("RGB").resize((Wt, Ht))).astype(np.float32) / 255 Image.fromarray(np.clip(lav * l, 0, 255).astype(np.uint8)).save(sortie, quality=80, optimize=True, progressive=True) composer(L35, lavis((226, 236, 212)), "sol2035.jpg") # forêt nourricière composer(Image.open("lignesauj.png"), lavis((241, 236, 214)), "solauj.jpg") # aujourd'hui : un champ print("sols écrits", Wt, "x", Ht)