Skip to content

Commit f8cdf69

Browse files
Add colors
1 parent 1e60848 commit f8cdf69

8 files changed

Lines changed: 115 additions & 18 deletions

File tree

public/ai-manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
"exploration_modes": 8
7979
},
8080
"deeplink_format": {
81-
"pattern": "https://multilingualprogramming.github.io/fractales/#f={fractal}&x={centerX}&y={centerY}&ps={pixelSize}&r={rotation}&i={maxIter}&p={palette}&jr={juliaRe}&ji={juliaIm}&cm={coloringMode}&ov={overlays}&lp={lsystemProposalActive}&ls={lsystemSeed}&fpa={formulaProposalActive}",
81+
"pattern": "https://multilingualprogramming.github.io/fractales/#f={fractal}&x={centerX}&y={centerY}&ps={pixelSize}&r={rotation}&i={maxIter}&p={palette}&jr={juliaRe}&ji={juliaIm}&cm={coloringMode}&lc={lsystemLineColor}&ov={overlays}&lp={lsystemProposalActive}&ls={lsystemSeed}&fpa={formulaProposalActive}",
8282
"parameters": {
8383
"f": "Fractal identifier (required)",
8484
"x": "Center X in complex plane (required)",
@@ -92,6 +92,7 @@
9292
"cm": "Palette Physics coloring mode (optional)",
9393
"ph": "Palette phase offset (optional)",
9494
"pc": "Palette contour enhancement flag, 1 when enabled (optional)",
95+
"lc": "L-system line coloring mode: uniforme, progression, profondeur, orientation (optional)",
9596
"azi": "Mode Abysses automatic iteration flag, 1 when enabled (optional)",
9697
"q": "Mode Abysses quality level: standard, fine, extreme (optional)",
9798
"mat": "Studio 3D material hint (optional)",

public/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,17 @@ <h2>Source &amp; Performances</h2>
631631
<div id="custom-palette-stops" class="custom-palette-stops" role="list"
632632
aria-label="Stops de gradient"></div>
633633
</section>
634+
635+
<div id="lsystem-line-color-group" class="control-group hidden" role="group"
636+
aria-label="Couleur des traits L-système">
637+
<label class="control-label" for="lsystem-line-color-select">Traits :</label>
638+
<select id="lsystem-line-color-select" aria-label="Choisir la coloration des traits L-système">
639+
<option value="uniforme">Uniforme</option>
640+
<option value="progression" selected>Progression</option>
641+
<option value="profondeur">Profondeur</option>
642+
<option value="orientation">Orientation</option>
643+
</select>
644+
</div>
634645
</div>
635646
</section>
636647

public/js/renderer-lsystem.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,25 +111,27 @@ export function pointsPropositionLSysteme(config, options = {}) {
111111
let angle = 0.0;
112112
let angleSign = 1;
113113
const stack = [];
114-
const points = [{ x, y, move: true }];
114+
let profondeur = 0;
115+
const points = [{ x, y, move: true, profondeur, angle }];
115116
for (const ch of sequence) {
116117
if (ch === "F" || ch === "G") {
117118
x += Math.cos(angle);
118119
y += Math.sin(angle);
119-
points.push({ x, y });
120+
points.push({ x, y, profondeur, angle });
120121
} else if (ch === "f") {
121122
x += Math.cos(angle);
122123
y += Math.sin(angle);
123-
points.push({ x, y, move: true });
124+
points.push({ x, y, move: true, profondeur, angle });
124125
} else if (ch === "+") {
125126
angle += angleStep * angleSign;
126127
} else if (ch === "-") {
127128
angle -= angleStep * angleSign;
128129
} else if (ch === "[") {
129-
stack.push([x, y, angle, angleSign]);
130+
stack.push([x, y, angle, angleSign, profondeur]);
131+
profondeur += 1;
130132
} else if (ch === "]" && stack.length > 0) {
131-
[x, y, angle, angleSign] = stack.pop();
132-
points.push({ x, y, move: true });
133+
[x, y, angle, angleSign, profondeur] = stack.pop();
134+
points.push({ x, y, move: true, profondeur, angle });
133135
} else if (ch === "|") {
134136
angle += Math.PI;
135137
} else if (ch === "!") {

public/js/renderer-navigation.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export function encoderEtat(view, params) {
4242
if (params.coloringMode && params.coloringMode !== "standard") p.set("cm", params.coloringMode);
4343
if (params.palettePhase) p.set("ph", Number(params.palettePhase).toFixed(3));
4444
if (params.paletteContours) p.set("pc", "1");
45+
if (params.lsystemLineColor && params.lsystemLineColor !== "progression") p.set("lc", params.lsystemLineColor);
4546
if (params.deepZoomAutoIterations) p.set("azi", "1");
4647
if (params.deepZoomQuality && params.deepZoomQuality !== "standard") p.set("q", params.deepZoomQuality);
4748
if (params.studio3dMaterial && params.studio3dMaterial !== "lumineux") p.set("mat", params.studio3dMaterial);
@@ -99,6 +100,7 @@ export function decoderEtat(hash) {
99100
coloringMode: p.get("cm") ?? "standard",
100101
palettePhase: num("ph") ?? 0,
101102
paletteContours: p.get("pc") === "1",
103+
lsystemLineColor: p.get("lc") ?? "progression",
102104
deepZoomAutoIterations: p.get("azi") === "1",
103105
deepZoomQuality: p.get("q") ?? "standard",
104106
studio3dMaterial: p.get("mat") ?? "lumineux",

public/js/renderer.js

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
} from "./renderer-formule.js?v=app";
3636

3737
const WASM_URL = "mandelbrot.wasm?v=app";
38+
const MODES_COULEUR_TRAITS_LSYSTEME = new Set(["uniforme", "progression", "profondeur", "orientation"]);
3839

3940
// ============================================================
4041
// ÉTAT DE L'APPLICATION
@@ -64,6 +65,7 @@ const params = {
6465
coloringMode: "standard",
6566
palettePhase: 0.0,
6667
paletteContours: false,
68+
lsystemLineColor: "progression",
6769
deepZoomAutoIterations: false,
6870
deepZoomQuality: "standard",
6971
studio3dMaterial: "lumineux",
@@ -229,6 +231,8 @@ const customPaletteStops = document.getElementById("custom-palette-stops");
229231
const addPaletteStopButton = document.getElementById("btn-add-palette-stop");
230232
const paletteBackgroundInput = document.getElementById("palette-background");
231233
const paletteInteriorInput = document.getElementById("palette-interior");
234+
const lsystemLineColorGroup = document.getElementById("lsystem-line-color-group");
235+
const lsystemLineColorSelect = document.getElementById("lsystem-line-color-select");
232236
const btnReset = document.getElementById("btn-reset");
233237
const btnPanUp = document.getElementById("btn-pan-up");
234238
const btnPanLeft = document.getElementById("btn-pan-left");
@@ -830,15 +834,23 @@ function definirVisibiliteEditeurPalette(forceOuverture) {
830834
}
831835

832836
function synchroniserControlePalette() {
837+
if (!MODES_COULEUR_TRAITS_LSYSTEME.has(params.lsystemLineColor)) params.lsystemLineColor = "progression";
833838
paletteSelect.value = params.palette;
834839
if (paletteSelectCompact) paletteSelectCompact.value = params.palette;
840+
if (lsystemLineColorSelect) lsystemLineColorSelect.value = params.lsystemLineColor || "progression";
841+
mettreAJourVisibiliteCouleurTraits();
835842
paletteBackgroundInput.value = normaliserHexCouleur(params.paletteBackground, "#020008");
836843
paletteInteriorInput.value = normaliserHexCouleur(params.paletteInterior, "#fff5b4");
837844
params.paletteStops = normaliserStopsPalette(params.paletteStops, PALETTES.aurora.stops.map((stop) => rgbVersHex(stop)));
838845
definirVisibiliteEditeurPalette();
839846
mettreAJourResumeControles();
840847
}
841848

849+
function mettreAJourVisibiliteCouleurTraits() {
850+
const afficher = Boolean(params.lsystemProposalActive);
851+
if (lsystemLineColorGroup) lsystemLineColorGroup.classList.toggle("hidden", !afficher);
852+
}
853+
842854
function definirVisibiliteOptionsSpecifiques({ labels = [] } = {}) {
843855
const afficher = labels.length > 0;
844856
if (controlsSpecific) controlsSpecific.classList.toggle("hidden", !afficher);
@@ -1025,6 +1037,7 @@ function capturerVueCourante() {
10251037
coloringMode: params.coloringMode,
10261038
palettePhase: params.palettePhase,
10271039
paletteContours: params.paletteContours,
1040+
lsystemLineColor: params.lsystemLineColor,
10281041
deepZoomAutoIterations: params.deepZoomAutoIterations,
10291042
deepZoomQuality: params.deepZoomQuality,
10301043
studio3dMaterial: params.studio3dMaterial,
@@ -1090,6 +1103,7 @@ function clonerParamsExport(source = params) {
10901103
coloringMode: source.coloringMode ?? params.coloringMode,
10911104
palettePhase: source.palettePhase ?? params.palettePhase,
10921105
paletteContours: source.paletteContours ?? params.paletteContours,
1106+
lsystemLineColor: source.lsystemLineColor ?? params.lsystemLineColor,
10931107
deepZoomAutoIterations: source.deepZoomAutoIterations ?? params.deepZoomAutoIterations,
10941108
deepZoomQuality: source.deepZoomQuality ?? params.deepZoomQuality,
10951109
studio3dMaterial: source.studio3dMaterial ?? params.studio3dMaterial,
@@ -1767,6 +1781,7 @@ function appliquerPropositionLSysteme(config) {
17671781
view.centerY = 0.0;
17681782
view.pixelSize = 2.4 / Math.max(canvas.width, 1);
17691783
view.rotation = 0.0;
1784+
mettreAJourVisibiliteCouleurTraits();
17701785
mettreAJourOptionsSpecifiques();
17711786
explorationModes?.syncFromParams();
17721787
render();
@@ -1776,6 +1791,7 @@ function appliquerPropositionLSysteme(config) {
17761791

17771792
function effacerPropositionLSysteme() {
17781793
params.lsystemProposalActive = false;
1794+
mettreAJourVisibiliteCouleurTraits();
17791795
mettreAJourOptionsSpecifiques();
17801796
explorationModes?.syncFromParams();
17811797
render();
@@ -1988,6 +2004,13 @@ function creerTraceurMonde(ctxCible, w, h, vueCible) {
19882004
lineTo(x, y) {
19892005
ctxCible.lineTo((x - cx0) / vueCible.pixelSize, (y - cy0) / vueCible.pixelSize);
19902006
},
2007+
segmentColorie(x0, y0, x1, y1, couleur) {
2008+
ctxCible.beginPath();
2009+
ctxCible.strokeStyle = "rgb(" + couleur[0] + ", " + couleur[1] + ", " + couleur[2] + ")";
2010+
ctxCible.moveTo((x0 - cx0) / vueCible.pixelSize, (y0 - cy0) / vueCible.pixelSize);
2011+
ctxCible.lineTo((x1 - cx0) / vueCible.pixelSize, (y1 - cy0) / vueCible.pixelSize);
2012+
ctxCible.stroke();
2013+
},
19912014
};
19922015
}
19932016

@@ -2098,7 +2121,21 @@ function dessinerCommandeLineaireMonde(traceur, commands, x, y, angle, segment,
20982121
}
20992122
}
21002123

2101-
function dessinerPropositionLSystemeMonde(traceur, renduParams) {
2124+
function couleurSegmentLSysteme(segment, index, total, renduParams) {
2125+
const mode = renduParams.lsystemLineColor || "progression";
2126+
if (mode === "uniforme") {
2127+
return getColor(Math.min(renduParams.maxIter * 0.6, renduParams.maxIter - 1), renduParams.maxIter, renduParams);
2128+
}
2129+
let t = total <= 1 ? 0 : index / (total - 1);
2130+
if (mode === "profondeur") {
2131+
t = Math.min(0.999, (segment.profondeur || 0) / 7);
2132+
} else if (mode === "orientation") {
2133+
t = ((segment.angle % (Math.PI * 2)) + Math.PI * 2) / (Math.PI * 2);
2134+
}
2135+
return getColorFromRatio(t, renduParams);
2136+
}
2137+
2138+
function dessinerPropositionLSystemeMonde(traceur, renduParams, couleurParSegment = false) {
21022139
const { points } = pointsPropositionLSysteme({
21032140
axiom: renduParams.lsystemAxiom,
21042141
rules: renduParams.lsystemRules,
@@ -2118,11 +2155,28 @@ function dessinerPropositionLSystemeMonde(traceur, renduParams) {
21182155
const scale = 1.82 / span;
21192156
const centerX = (minX + maxX) * 0.5;
21202157
const centerY = (minY + maxY) * 0.5;
2121-
points.forEach((point, index) => {
2122-
const x = (point.x - centerX) * scale;
2123-
const y = -(point.y - centerY) * scale;
2124-
if (index === 0 || point.move) traceur.moveTo(x, y);
2125-
else traceur.lineTo(x, y);
2158+
const pointsMonde = points.map((point) => ({
2159+
x: (point.x - centerX) * scale,
2160+
y: -(point.y - centerY) * scale,
2161+
move: point.move,
2162+
profondeur: point.profondeur || 0,
2163+
angle: point.angle || 0,
2164+
}));
2165+
const segments = [];
2166+
pointsMonde.forEach((point, index) => {
2167+
const precedent = pointsMonde[index - 1];
2168+
if (index === 0 || point.move || !precedent) return;
2169+
segments.push({ x0: precedent.x, y0: precedent.y, x1: point.x, y1: point.y, profondeur: point.profondeur, angle: point.angle });
2170+
});
2171+
if (couleurParSegment && typeof traceur.segmentColorie === "function") {
2172+
segments.forEach((segment, index) => {
2173+
traceur.segmentColorie(segment.x0, segment.y0, segment.x1, segment.y1, couleurSegmentLSysteme(segment, index, segments.length, renduParams));
2174+
});
2175+
return;
2176+
}
2177+
pointsMonde.forEach((point, index) => {
2178+
if (index === 0 || point.move) traceur.moveTo(point.x, point.y);
2179+
else traceur.lineTo(point.x, point.y);
21262180
});
21272181
}
21282182

@@ -2355,9 +2409,12 @@ function dessinerFractaleLineaire(ctxCible, w, h, vueCible, renduParams, skipBac
23552409
ctxCible.lineWidth = Math.max(1, Math.min(2, w / 800));
23562410
ctxCible.beginPath();
23572411
const traceur = creerTraceurMonde(ctxCible, w, h, vueCible);
2412+
let renduColorie = false;
23582413

23592414
if (renduParams.lsystemProposalActive) {
2360-
dessinerPropositionLSystemeMonde(traceur, renduParams);
2415+
const contexteCanvas = typeof CanvasRenderingContext2D !== "undefined" && ctxCible instanceof CanvasRenderingContext2D;
2416+
renduColorie = contexteCanvas && renduParams.lsystemLineColor !== "uniforme";
2417+
dessinerPropositionLSystemeMonde(traceur, renduParams, renduColorie);
23612418
} else if (renduParams.fractal === "koch") {
23622419
const n = Math.max(0, Math.min(6, Math.floor((renduParams.maxIter - 64) / 128)));
23632420
const commands = kochGenerate(n);
@@ -2406,7 +2463,7 @@ function dessinerFractaleLineaire(ctxCible, w, h, vueCible, renduParams, skipBac
24062463
dessinerBranche(0.0, 1.35, Math.PI / 2, 0.42, profondeur);
24072464
}
24082465

2409-
ctxCible.stroke();
2466+
if (!renduColorie) ctxCible.stroke();
24102467
}
24112468

24122469
async function remplirFractalePonctuelle(w, h, data, cx0, cy0, ps, renduParams) {
@@ -3461,6 +3518,14 @@ if (paletteSelectCompact) {
34613518
});
34623519
}
34633520

3521+
if (lsystemLineColorSelect) {
3522+
lsystemLineColorSelect.addEventListener("change", () => {
3523+
params.lsystemLineColor = lsystemLineColorSelect.value;
3524+
render();
3525+
mettreAJourHash(view, params);
3526+
});
3527+
}
3528+
34643529
toggleCustomPaletteButton.classList.add("icon-btn");
34653530
toggleCustomPaletteButton.addEventListener("click", () => {
34663531
if (params.palette !== "personnalisee") return;
@@ -3860,6 +3925,7 @@ async function appliquerSignet(signet) {
38603925
params.coloringMode = signet.coloringMode ?? params.coloringMode;
38613926
params.palettePhase = signet.palettePhase ?? params.palettePhase;
38623927
params.paletteContours = signet.paletteContours ?? params.paletteContours;
3928+
params.lsystemLineColor = signet.lsystemLineColor ?? params.lsystemLineColor;
38633929
params.deepZoomAutoIterations = signet.deepZoomAutoIterations ?? params.deepZoomAutoIterations;
38643930
params.deepZoomQuality = signet.deepZoomQuality ?? params.deepZoomQuality;
38653931
params.studio3dMaterial = signet.studio3dMaterial ?? params.studio3dMaterial;
@@ -4024,6 +4090,7 @@ async function init() {
40244090
params.coloringMode = etatHash.coloringMode ?? params.coloringMode;
40254091
params.palettePhase = etatHash.palettePhase ?? params.palettePhase;
40264092
params.paletteContours = etatHash.paletteContours ?? params.paletteContours;
4093+
params.lsystemLineColor = etatHash.lsystemLineColor ?? params.lsystemLineColor;
40274094
params.deepZoomAutoIterations = etatHash.deepZoomAutoIterations ?? params.deepZoomAutoIterations;
40284095
params.deepZoomQuality = etatHash.deepZoomQuality ?? params.deepZoomQuality;
40294096
params.studio3dMaterial = etatHash.studio3dMaterial ?? params.studio3dMaterial;

public/schemas/deeplink-params.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@
6767
"enum": ["1"],
6868
"description": "Set to 1 to reinforce iteration contour lines in Palette Physics."
6969
},
70+
"lc": {
71+
"type": "string",
72+
"enum": ["uniforme", "progression", "profondeur", "orientation"],
73+
"description": "L-system line coloring mode for applied Atelier L-system proposals. Default: progression.",
74+
"default": "progression"
75+
},
7076
"azi": {
7177
"type": "string",
7278
"enum": ["1"],

public/tools.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@
203203
"maximum": 1,
204204
"description": "Optional palette phase, encoded as ph."
205205
},
206+
"lsystemLineColor": {
207+
"type": "string",
208+
"enum": ["uniforme", "progression", "profondeur", "orientation"],
209+
"description": "Optional L-system line coloring mode, encoded as lc.",
210+
"default": "progression"
211+
},
206212
"weatherOverlays": {
207213
"type": "string",
208214
"description": "Optional comma-separated mathematical weather overlays, encoded as ov. Supported values: grille, contours, orbite."
@@ -234,7 +240,7 @@
234240
},
235241
"endpoint": {
236242
"method": "NONE",
237-
"url_template": "https://multilingualprogramming.github.io/fractales/#f={fractal}&x={centerX}&y={centerY}&ps={pixelSize}&r={rotation}&i={maxIter}&p={palette}&jr={juliaCre}&ji={juliaCim}&cm={coloringMode}&ph={palettePhase}&ov={weatherOverlays}&lp=1&lg={lsystemProposal.generations}&la={lsystemProposal.angle}&lax={lsystemProposal.axiom}&lr={lsystemProposal.rules}&ls={lsystemProposal.seed}&fpa=1&ffi={formulaProposal.iteration}&ffe={formulaProposal.escapeRadius}&ffm={formulaProposal.mode}&ffa={formulaProposal.a}&ffb={formulaProposal.b}",
243+
"url_template": "https://multilingualprogramming.github.io/fractales/#f={fractal}&x={centerX}&y={centerY}&ps={pixelSize}&r={rotation}&i={maxIter}&p={palette}&jr={juliaCre}&ji={juliaCim}&cm={coloringMode}&ph={palettePhase}&lc={lsystemLineColor}&ov={weatherOverlays}&lp=1&lg={lsystemProposal.generations}&la={lsystemProposal.angle}&lax={lsystemProposal.axiom}&lr={lsystemProposal.rules}&ls={lsystemProposal.seed}&fpa=1&ffi={formulaProposal.iteration}&ffe={formulaProposal.escapeRadius}&ffm={formulaProposal.mode}&ffa={formulaProposal.a}&ffb={formulaProposal.b}",
238244
"note": "Assemble directly from parameters — no HTTP request required"
239245
},
240246
"examples": [

scripts/generate_api.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,10 @@
141141
{
142142
"id": "palette",
143143
"label": "Physique de palette",
144-
"description": "Additional coloring modes layered on top of existing palettes: histogram, phase, contours, and potential smoothing.",
145-
"deeplink_fields": ["cm", "ph", "pc"],
144+
"description": "Additional coloring modes layered on top of existing palettes: histogram, phase, contours, potential smoothing, and L-system line gradients.",
145+
"deeplink_fields": ["cm", "ph", "pc", "lc"],
146146
"coloring_modes": ["standard", "histogramme", "phase", "contours", "potentiel"],
147+
"lsystem_line_color_modes": ["uniforme", "progression", "profondeur", "orientation"],
147148
},
148149
{
149150
"id": "abysses",
@@ -411,6 +412,7 @@ def main() -> None:
411412
"default": "aurora",
412413
"palettes": palettes,
413414
"coloring_modes": ["standard", "histogramme", "phase", "contours", "potentiel"],
415+
"lsystem_line_color_modes": ["uniforme", "progression", "profondeur", "orientation"],
414416
}
415417
)
416418

0 commit comments

Comments
 (0)