{"id":8906,"date":"2026-08-19T06:23:21","date_gmt":"2026-08-19T06:23:21","guid":{"rendered":"https:\/\/aiper.com\/blogs\/?page_id=8906"},"modified":"2026-10-21T09:13:01","modified_gmt":"2026-10-21T09:13:01","slug":"visualiseur-espacement-plantes","status":"publish","type":"page","link":"https:\/\/aiper.com\/blogs\/fr\/visualiseur-espacement-plantes\/","title":{"rendered":"Visualiseur d&#8217;espacement des plantes \u2013 Planifiez votre jardin avant de planter"},"content":{"rendered":"\n<style>\n\/* ==========================================================================\n   Plant & Tree Spacing Visualizer \u2014 Brand: #1ecad3 + #9486f7 blended\n   Pure DOM\/CSS visualization (no Canvas). WAF-compatible: no inline styles.\n   ========================================================================== *\/\n#plant-visualizer {\n    max-width: 750px;\n    margin: 20px auto;\n    padding: 0 16px;\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n    box-sizing: border-box;\n}\n#plant-visualizer *, #plant-visualizer *::before, #plant-visualizer *::after { box-sizing: border-box; }\n\n\/* \u2500\u2500 Title \u2500\u2500 *\/\n.pv-title { text-align: center; font-size: 1.4rem; font-weight: 700; color: #1a202c; margin-bottom: 4px; }\n.pv-subtitle { text-align: center; font-size: 14px; color: #64748b; margin-bottom: 20px; }\n\n\/* \u2500\u2500 Unit Toggle \u2500\u2500 *\/\n.pv-unit-toggle { display: flex; justify-content: center; gap: 0; margin-bottom: 20px; }\n.pv-unit-btn {\n    padding: 10px 24px; font-size: 13px; font-weight: 600;\n    background: #f5f7fa; color: #64748b; border: 1px solid #e2e8f0;\n    cursor: pointer; transition: all 0.2s ease;\n}\n.pv-unit-btn:first-child { border-radius: 8px 0 0 8px; }\n.pv-unit-btn:last-child { border-radius: 0 8px 8px 0; }\n.pv-unit-btn:hover { background: #e8f7f8; color: #1ecad3; }\n.pv-unit-btn.active {\n    background: linear-gradient(135deg, #1ecad3, #9486f7);\n    color: #fff; border-color: transparent;\n}\n.pv-unit-btn:focus, .pv-unit-btn:active { outline: none; }\n\n\/* \u2500\u2500 Controls Grid \u2500\u2500 *\/\n.pv-controls {\n    display: grid; grid-template-columns: 1fr 1fr; gap: 14px;\n    margin-bottom: 16px;\n}\n.pv-field { display: flex; flex-direction: column; }\n.pv-field label {\n    font-size: 13px; font-weight: 600; color: #2c3e50; margin-bottom: 4px;\n}\n.pv-field input {\n    padding: 10px 12px; border: 1px solid #e2e8f0; border-radius: 8px;\n    font-size: 14px; color: #2c3e50; transition: border-color 0.2s ease;\n    background: #fff;\n}\n.pv-field input:focus { border-color: #9486f7; outline: none; box-shadow: 0 0 0 3px rgba(148,134,247,0.1); }\n.pv-unit-suffix { font-size: 11px; color: #94a3b8; margin-left: 2px; }\n\n\/* \u2500\u2500 Presets \u2500\u2500 *\/\n.pv-preset-label { font-size: 13px; font-weight: 600; color: #2c3e50; margin-bottom: 8px; }\n.pv-preset-buttons { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }\n.pv-preset-btn {\n    padding: 8px 14px; font-size: 12px; font-weight: 600;\n    border: 1px solid #e2e8f0; border-radius: 20px;\n    background: #fff; color: #475569; cursor: pointer;\n    transition: all 0.2s ease;\n}\n.pv-preset-btn:hover { border-color: #b2dfdb; background: #f8fdfc; }\n.pv-preset-btn:focus, .pv-preset-btn:active { color: #2c3e50; outline: none; }\n.pv-preset-btn.selected {\n    border-color: #9486f7; background: #f5f3ff; color: #2c3e50;\n    box-shadow: 0 0 0 2px rgba(148,134,247,0.12);\n}\n\n\/* \u2500\u2500 Pattern Selector \u2500\u2500 *\/\n.pv-pattern-label { font-size: 13px; font-weight: 600; color: #2c3e50; margin-bottom: 8px; }\n.pv-pattern-buttons { display: flex; gap: 10px; margin-bottom: 16px; }\n.pv-pattern-btn {\n    flex: 1; padding: 12px 16px; font-size: 13px; font-weight: 600;\n    border: 2px solid #e2e8f0; border-radius: 10px;\n    background: #fff; color: #475569; cursor: pointer;\n    transition: all 0.2s ease; text-align: center;\n}\n.pv-pattern-btn:hover { border-color: #b2dfdb; }\n.pv-pattern-btn:focus, .pv-pattern-btn:active { color: #2c3e50; outline: none; }\n.pv-pattern-btn.selected {\n    border-color: #9486f7; background: #f5f3ff; color: #2c3e50;\n}\n\n\/* \u2500\u2500 Bed Visualization Area \u2500\u2500 *\/\n.pv-canvas-wrapper {\n    text-align: center; margin-bottom: 16px;\n    background: #fafafa; border-radius: 12px; padding: 20px 16px 40px;\n    border: 1px solid #e2e8f0;\n}\n#pv-bed-frame {\n    position: relative; display: inline-block;\n    max-width: 100%;\n}\n#pv-dim-label {\n    text-align: center; font-size: 11px; color: #2c3e50; font-weight: 600;\n    margin-bottom: 6px;\n}\n#pv-bed-area {\n    position: relative; background: #f5ebe0;\n    border: 2px solid #8B7355; border-radius: 2px;\n    margin: 0 auto;\n}\n.pv-grid-line-h, .pv-grid-line-v {\n    position: absolute; pointer-events: none;\n}\n.pv-grid-line-h { left: 0; right: 0; border-top: 1px dashed #d5c4a1; }\n.pv-grid-line-v { top: 0; bottom: 0; border-left: 1px dashed #d5c4a1; }\n.pv-plant {\n    position: absolute; border-radius: 50%; transform: translate(-50%, -50%);\n}\n.pv-plant-canopy {\n    background: rgba(143, 188, 143, 0.45);\n    border: 1px solid #5b8c5a;\n}\n.pv-plant-stem {\n    background: #5b8c5a; z-index: 1;\n}\n#pv-scale-bar {\n    display: flex; align-items: center; justify-content: center;\n    margin-top: 10px; gap: 6px;\n}\n#pv-scale-line {\n    height: 1.5px; background: #2c3e50;\n}\n#pv-scale-label {\n    font-size: 11px; color: #2c3e50; white-space: nowrap;\n}\n\n\/* \u2500\u2500 Results \u2500\u2500 *\/\n.pv-results {\n    display: flex; flex-wrap: wrap; justify-content: center; gap: 12px 24px;\n    padding: 14px 20px; background: #f5f7fa; border-radius: 10px;\n    margin-bottom: 20px;\n}\n.pv-result-item { text-align: center; }\n.pv-result-value { font-size: 1.3rem; font-weight: 700; color: #1ecad3; }\n.pv-result-label { font-size: 11px; color: #64748b; text-transform: uppercase; letter-spacing: 0.5px; }\n\n\/* \u2500\u2500 Tool Links (cross-refs) \u2500\u2500 *\/\n.pv-tool-links { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-bottom: 20px; }\n.pv-tool-link {\n    display: inline-block; padding: 8px 16px;\n    background: #f5f7fa; color: #6b5ce7; border: 1px solid #e8e3fc;\n    border-radius: 22px; font-size: 12px; font-weight: 600;\n    text-decoration: none; transition: all 0.2s ease;\n}\n.pv-tool-link:hover { background: #ede9fe; color: #5a4bd1; border-color: #9486f7; }\n.pv-tool-link:focus, .pv-tool-link:active { color: #5a4bd1; outline: none; }\n\n\/* \u2500\u2500 CTA \u2500\u2500 *\/\n.pv-cta {\n    padding: 16px 20px; background: #f0fcfc; border-radius: 12px;\n    border: 1px solid #b2dfdb; text-align: center; margin-bottom: 20px;\n}\n.pv-cta p { margin: 0 0 8px; font-size: 13px; color: #475569; line-height: 1.5; }\n.pv-cta a {\n    display: inline-block; padding: 10px 24px;\n    background: linear-gradient(135deg, #1ecad3, #9486f7);\n    color: #fff; border-radius: 8px; text-decoration: none;\n    font-weight: 700; font-size: 14px;\n    transition: all 0.2s ease;\n}\n.pv-cta a:hover { transform: translateY(-1px); box-shadow: 0 4px 16px rgba(148,134,247,0.35); }\n\n\/* \u2500\u2500 Validation \u2500\u2500 *\/\n.pv-error { color: #e53e3e; font-size: 11px; margin-top: 2px; min-height: 16px; }\n\n\/* \u2500\u2500 JS Status \/ Error Banner \u2500\u2500 *\/\n#pv-js-status {\n    text-align: center; padding: 8px 12px; margin-bottom: 12px;\n    background: #fffbe6; border: 1px solid #f6e05e; border-radius: 8px;\n    font-size: 12px; font-weight: 600; color: #975a16;\n}\n#pv-error-msg {\n    display: none; padding: 12px 16px; background: #fff5f5;\n    border: 1px solid #feb2b2; border-radius: 8px;\n    color: #c53030; font-size: 13px; margin-bottom: 16px;\n}\n\n\/* \u2500\u2500 Responsive \u2500\u2500 *\/\n@media (max-width: 600px) {\n    .pv-controls { grid-template-columns: 1fr; }\n    .pv-preset-buttons { justify-content: center; }\n    .pv-preset-btn { flex: 1; min-width: 80px; text-align: center; }\n    .pv-results { flex-direction: column; gap: 8px; }\n    .pv-canvas-wrapper { padding: 12px 8px 32px; }\n    #plant-visualizer { padding: 0 8px; }\n}\n<\/style>\n\n<div id=\"plant-visualizer\">\n    <div id=\"pv-js-status\">LOADING&#8230; JS not yet executed<\/div>\n\n    <div class=\"pv-title\" id=\"pv-title\">Plant &amp; Tree Spacing Visualizer<\/div>\n    <div class=\"pv-subtitle\" id=\"pv-subtitle\">See how your plants will look before you dig<\/div>\n\n    <div id=\"pv-error-msg\"><\/div>\n\n    <!-- Unit Toggle -->\n    <div class=\"pv-unit-toggle\" role=\"radiogroup\" aria-label=\"Unit System\">\n        <button type=\"button\" class=\"pv-unit-btn active\" data-unit=\"imperial\" id=\"pv-unit-imp\">ft \/ in<\/button>\n        <button type=\"button\" class=\"pv-unit-btn\" data-unit=\"metric\" id=\"pv-unit-met\">m \/ cm<\/button>\n    <\/div>\n\n    <!-- Bed Dimensions -->\n    <div class=\"pv-controls\">\n        <div class=\"pv-field\">\n            <label for=\"pv-bed-width\" id=\"pv-label-width\">Bed Width (<span class=\"pv-unit-suffix\" id=\"pv-unit-w\">ft<\/span>)<\/label>\n            <input type=\"number\" id=\"pv-bed-width\" step=\"any\" min=\"0.1\" value=\"8\" placeholder=\"e.g. 8\" inputmode=\"decimal\">\n            <div class=\"pv-error\" id=\"pv-err-width\"><\/div>\n        <\/div>\n        <div class=\"pv-field\">\n            <label for=\"pv-bed-length\" id=\"pv-label-length\">Bed Length (<span class=\"pv-unit-suffix\" id=\"pv-unit-l\">ft<\/span>)<\/label>\n            <input type=\"number\" id=\"pv-bed-length\" step=\"any\" min=\"0.1\" value=\"12\" placeholder=\"e.g. 12\" inputmode=\"decimal\">\n            <div class=\"pv-error\" id=\"pv-err-length\"><\/div>\n        <\/div>\n    <\/div>\n\n    <!-- Plant Type Presets -->\n    <div class=\"pv-preset-label\" id=\"pv-preset-label\">Quick Presets<\/div>\n    <div class=\"pv-preset-buttons\" id=\"pv-presets\"><\/div>\n\n    <!-- Spacing + Canopy -->\n    <div class=\"pv-controls\">\n        <div class=\"pv-field\">\n            <label for=\"pv-spacing\" id=\"pv-label-spacing\">Plant Spacing (<span class=\"pv-unit-suffix\" id=\"pv-unit-s\">in<\/span>)<\/label>\n            <input type=\"number\" id=\"pv-spacing\" step=\"any\" min=\"0.1\" value=\"18\" placeholder=\"e.g. 18\" inputmode=\"decimal\">\n            <div class=\"pv-error\" id=\"pv-err-spacing\"><\/div>\n        <\/div>\n        <div class=\"pv-field\">\n            <label for=\"pv-canopy\" id=\"pv-label-canopy\">Canopy at Maturity (<span class=\"pv-unit-suffix\" id=\"pv-unit-c\">in<\/span>)<\/label>\n            <input type=\"number\" id=\"pv-canopy\" step=\"any\" min=\"0.1\" value=\"14\" placeholder=\"e.g. 14\" inputmode=\"decimal\">\n            <div class=\"pv-error\" id=\"pv-err-canopy\"><\/div>\n        <\/div>\n    <\/div>\n\n    <!-- Layout Pattern -->\n    <div class=\"pv-pattern-label\" id=\"pv-pattern-label\">Layout Pattern<\/div>\n    <div class=\"pv-pattern-buttons\" role=\"radiogroup\" aria-label=\"Layout Pattern\">\n        <button type=\"button\" class=\"pv-pattern-btn selected\" data-pattern=\"square\" id=\"pv-pat-square\">Square Grid<\/button>\n        <button type=\"button\" class=\"pv-pattern-btn\" data-pattern=\"staggered\" id=\"pv-pat-staggered\">Staggered \/ Triangular<\/button>\n    <\/div>\n\n    <!-- Bed Visualization (DOM-based, no Canvas) -->\n    <div class=\"pv-canvas-wrapper\">\n        <div id=\"pv-dim-label\"><\/div>\n        <div id=\"pv-bed-frame\">\n            <div id=\"pv-bed-area\"><\/div>\n        <\/div>\n        <div id=\"pv-scale-bar\">\n            <span id=\"pv-scale-left\"><\/span>\n            <span id=\"pv-scale-line\"><\/span>\n            <span id=\"pv-scale-right\"><\/span>\n            <span id=\"pv-scale-label\"><\/span>\n        <\/div>\n    <\/div>\n\n    <!-- Results -->\n    <div class=\"pv-results\" id=\"pv-results\">\n        <div class=\"pv-result-item\">\n            <div class=\"pv-result-value\" id=\"pv-val-total\">0<\/div>\n            <div class=\"pv-result-label\" id=\"pv-lbl-total\">Total Plants<\/div>\n        <\/div>\n        <div class=\"pv-result-item\">\n            <div class=\"pv-result-value\" id=\"pv-val-rows\">0<\/div>\n            <div class=\"pv-result-label\" id=\"pv-lbl-rows\">Rows<\/div>\n        <\/div>\n        <div class=\"pv-result-item\">\n            <div class=\"pv-result-value\" id=\"pv-val-cols\">0<\/div>\n            <div class=\"pv-result-label\" id=\"pv-lbl-cols\">Columns<\/div>\n        <\/div>\n        <div class=\"pv-result-item\">\n            <div class=\"pv-result-value\" id=\"pv-val-area\">0<\/div>\n            <div class=\"pv-result-label\" id=\"pv-lbl-area\">Bed Area<\/div>\n        <\/div>\n    <\/div>\n\n    <!-- CTA -->\n    <div class=\"pv-cta\" id=\"pv-cta\">\n        <p id=\"pv-cta-text\"><\/p>\n        <a href=\"https:\/\/aiper.com\/blogs\/plant-spacing-calculator\/\" id=\"pv-cta-link\">Calculate plant quantity<\/a>\n    <\/div>\n\n    <!-- Cross-reference tool links -->\n    <div class=\"pv-tool-links\" id=\"pv-tools\"><\/div>\n<\/div>\n\n<script>\n(function() {\n    \/\/ \u2500\u2500 Labels \u2500\u2500\n    var LABELS = {\"title\": \"Visualiseur d\u2019espacement des plantes\", \"subtitle\": \"Voyez \u00e0 quoi ressembleront vos plantes avant de creuser\", \"unitImperial\": \"pi \/ po\", \"unitMetric\": \"m \/ cm\", \"bedLabelWidth\": \"Largeur du massif\", \"bedLabelLength\": \"Longueur du massif\", \"bedLabelWidthM\": \"Largeur du massif (m)\", \"bedLabelLengthM\": \"Longueur du massif (m)\", \"unitFt\": \"pi\", \"unitIn\": \"po\", \"unitM\": \"m\", \"unitCm\": \"cm\", \"unitSqft\": \"pi\u00b2\", \"unitSqm\": \"m\u00b2\", \"presetLabel\": \"Presets rapides\", \"presetKeys\": [\"vegetables\", \"root_veg\", \"leafy\", \"hedge\", \"shrubs\", \"trees_small\", \"custom\"], \"presets\": {\"vegetables\": {\"label\": \"L\u00e9gumes (tomates, poivrons)\", \"spacing_in\": 24, \"canopy_in\": 18}, \"root_veg\": {\"label\": \"Racines (carottes, oignons)\", \"spacing_in\": 4, \"canopy_in\": 3}, \"leafy\": {\"label\": \"Feuillus (laitue, chou kale)\", \"spacing_in\": 12, \"canopy_in\": 10}, \"hedge\": {\"label\": \"Haies \/ bordures\", \"spacing_in\": 16, \"canopy_in\": 14}, \"shrubs\": {\"label\": \"Arbustes \/ buissons\", \"spacing_in\": 36, \"canopy_in\": 30}, \"trees_small\": {\"label\": \"Petits arbres (1,8-2,4 m)\", \"spacing_in\": 84, \"canopy_in\": 72}, \"custom\": {\"label\": \"Personnalis\u00e9\", \"spacing_in\": 0, \"canopy_in\": 0}}, \"spacingLabel\": \"Espacement des plantes\", \"canopyLabel\": \"Diam\u00e8tre du houppier\", \"patternLabel\": \"Motif de plantation\", \"patternSquare\": \"Grille carr\u00e9e\", \"patternStaggered\": \"En quinconce \/ triangulaire\", \"resultTotalPlants\": \"Total des plantes\", \"resultRows\": \"Rang\u00e9es\", \"resultCols\": \"Colonnes\", \"resultArea\": \"Surface du massif\", \"toolLinks\": {\"plantCalc\": {\"href\": \"https:\/\/aiper.com\/blogs\/fr\/calculateur-espacement-plantes\/\", \"label\": \"calculateur espacement plantes\"}, \"soilCalc\": {\"href\": \"https:\/\/aiper.com\/blogs\/fr\/calculateur-terre-jardin\/\", \"label\": \"calculateur terre jardin\"}, \"mulchCalc\": {\"href\": \"https:\/\/aiper.com\/blogs\/fr\/calculateur-paillis\/\", \"label\": \"calculateur paillis\"}, \"grassSeed\": {\"href\": \"https:\/\/aiper.com\/blogs\/fr\/calculateur-gazon\/\", \"label\": \"calculateur gazon\"}}, \"validationError\": \"Saisissez un nombre positif\", \"canopyWarning\": \"Diam\u00e8tre > \u00e9cartement\u00a0: les plantes pourraient se chevaucher\", \"canvasPlaceholder\": \"Entrez les dimensions du massif et l'espacement pour voir la disposition\", \"ctaText\": \"Pr\u00eat \u00e0 acheter? Utilisez notre <strong>calculateur d\u2019espacement des plantes<\/strong> pour obtenir le nombre exact de plantes, graines ou bulbes n\u00e9cessaires \u2014 avec marge incluse.\", \"ctaLink\": \"Calculer la quantit\u00e9 de plantes\"};\n\n    \/\/ \u2500\u2500 Helpers \u2500\u2500\n    var $ = function(id) { return document.getElementById(id); };\n    function L(path) {\n        var keys = path.split('.'), v = LABELS;\n        for (var i = 0; i < keys.length; i++) { v = v[keys[i]]; if (v === undefined) return ''; }\n        return v;\n    }\n    function setText(id, txt) { var e = $(id); if (e) e.textContent = txt; }\n    function _and() { var n = arguments.length; while (n--) { if (!arguments[n]) return false; } return true; }\n\n    \/\/ \u2500\u2500 State \u2500\u2500\n    var state = { unit: 'imperial', bedW: 8, bedL: 12, spacing: 18, canopy: 14, pattern: 'square' };\n\n    \/\/ \u2500\u2500 DOM refs \u2500\u2500\n    var bedAreaEl = $('pv-bed-area');\n    var bedFrameEl = $('pv-bed-frame');\n    var dimLabel = $('pv-dim-label');\n    var scaleBar = $('pv-scale-bar');\n    var scaleLine = $('pv-scale-line');\n    var scaleLabel = $('pv-scale-label');\n    var scaleLeft = $('pv-scale-left');\n    var scaleRight = $('pv-scale-right');\n\n    \/\/ \u2500\u2500 Set initial UI text \u2500\u2500\n    setText('pv-title', L('title'));\n    setText('pv-subtitle', L('subtitle'));\n    setText('pv-unit-imp', L('unitImperial'));\n    setText('pv-unit-met', L('unitMetric'));\n    setText('pv-preset-label', L('presetLabel'));\n    setText('pv-pattern-label', L('patternLabel'));\n    setText('pv-pat-square', L('patternSquare'));\n    setText('pv-pat-staggered', L('patternStaggered'));\n    setText('pv-lbl-total', L('resultTotalPlants'));\n    setText('pv-lbl-rows', L('resultRows'));\n    setText('pv-lbl-cols', L('resultCols'));\n    setText('pv-lbl-area', L('resultArea'));\n    var ctaTextEl = $('pv-cta-text');\n    if (ctaTextEl) ctaTextEl.innerHTML = L('ctaText');\n    setText('pv-cta-link', L('ctaLink'));\n\n    \/\/ \u2500\u2500 Build preset buttons \u2500\u2500\n    var presetDiv = $('pv-presets');\n    var pkeys = L('presetKeys');\n    if (_and(presetDiv, pkeys, pkeys.length)) {\n        presetDiv.innerHTML = '';\n        for (var i = 0; i < pkeys.length; i++) {\n            var key = pkeys[i];\n            var p = L('presets.' + key);\n            if (!p || !p.label) continue;\n            var btn = document.createElement('button');\n            btn.type = 'button';\n            btn.className = 'pv-preset-btn';\n            btn.setAttribute('data-preset', key);\n            btn.textContent = p.label;\n            btn.addEventListener('click', (function(k) { return function() { applyPreset(k); }; })(key));\n            presetDiv.appendChild(btn);\n        }\n    }\n\n    \/\/ \u2500\u2500 Build tool links \u2500\u2500\n    var toolsDiv = $('pv-tools');\n    if (toolsDiv) {\n        toolsDiv.innerHTML = '';\n        var tlKeys = Object.keys(LABELS.toolLinks || {});\n        for (var j = 0; j < tlKeys.length; j++) {\n            var tl = L('toolLinks.' + tlKeys[j]);\n            if (!tl || !tl.href) continue;\n            var a = document.createElement('a');\n            a.className = 'pv-tool-link';\n            a.href = tl.href;\n            a.textContent = tl.label;\n            toolsDiv.appendChild(a);\n        }\n    }\n\n    \/\/ \u2500\u2500 Unit labels \u2500\u2500\n    function updateUnitLabels() {\n        var isImp = state.unit === 'imperial';\n        var unitLen = isImp ? L('unitFt') : L('unitM');\n        var unitSmall = isImp ? L('unitIn') : L('unitCm');\n        var labelW = $('pv-label-width');\n        if (labelW) labelW.innerHTML = L('bedLabelWidth') + ' (<span class=\"pv-unit-suffix\" id=\"pv-unit-w\">' + unitLen + '<\/span>)';\n        var labelL = $('pv-label-length');\n        if (labelL) labelL.innerHTML = L('bedLabelLength') + ' (<span class=\"pv-unit-suffix\" id=\"pv-unit-l\">' + unitLen + '<\/span>)';\n        var spLabel = $('pv-label-spacing');\n        if (spLabel) spLabel.innerHTML = L('spacingLabel') + ' (<span class=\"pv-unit-suffix\" id=\"pv-unit-s\">' + unitSmall + '<\/span>)';\n        var cnLabel = $('pv-label-canopy');\n        if (cnLabel) cnLabel.innerHTML = L('canopyLabel') + ' (<span class=\"pv-unit-suffix\" id=\"pv-unit-c\">' + unitSmall + '<\/span>)';\n        var bwEl = $('pv-bed-width'); if (bwEl) bwEl.placeholder = isImp ? 'e.g. 8' : 'e.g. 2.5';\n        var blEl = $('pv-bed-length'); if (blEl) blEl.placeholder = isImp ? 'e.g. 12' : 'e.g. 3.5';\n        var spEl = $('pv-spacing'); if (spEl) spEl.placeholder = isImp ? 'e.g. 18' : 'e.g. 45';\n        var cnEl = $('pv-canopy'); if (cnEl) cnEl.placeholder = isImp ? 'e.g. 14' : 'e.g. 35';\n    }\n    updateUnitLabels();\n\n    \/\/ \u2500\u2500 Apply Preset \u2500\u2500\n    function applyPreset(key) {\n        var btns = document.querySelectorAll('.pv-preset-btn');\n        btns.forEach(function(b) { b.classList.remove('selected'); });\n        var active = document.querySelector('.pv-preset-btn[data-preset=\"' + key + '\"]');\n        if (active) active.classList.add('selected');\n        if (key === 'custom') return;\n        var p = L('presets.' + key);\n        if (!p) return;\n        if (state.unit === 'imperial') { state.spacing = p.spacing_in; state.canopy = p.canopy_in; }\n        else { state.spacing = Math.round(p.spacing_in * 2.54 * 10) \/ 10; state.canopy = Math.round(p.canopy_in * 2.54 * 10) \/ 10; }\n        var spEl = $('pv-spacing'); if (spEl) spEl.value = state.spacing;\n        var cnEl = $('pv-canopy'); if (cnEl) cnEl.value = state.canopy;\n        draw();\n    }\n\n    \/\/ \u2500\u2500 Unit Toggle \u2500\u2500\n    function setUnit(unit) {\n        if (state.unit === unit) return;\n        var wasImp = state.unit === 'imperial', nowImp = unit === 'imperial';\n        state.unit = unit;\n        var impBtn = $('pv-unit-imp'), metBtn = $('pv-unit-met');\n        if (impBtn) { impBtn.classList.remove('active'); if (nowImp) impBtn.classList.add('active'); }\n        if (metBtn) { metBtn.classList.remove('active'); if (!nowImp) metBtn.classList.add('active'); }\n        if (_and(wasImp, !nowImp)) {\n            state.bedW = Math.round(state.bedW * 0.3048 * 100) \/ 100;\n            state.bedL = Math.round(state.bedL * 0.3048 * 100) \/ 100;\n            state.spacing = Math.round(state.spacing * 2.54 * 10) \/ 10;\n            state.canopy = Math.round(state.canopy * 2.54 * 10) \/ 10;\n        } else if (_and(!wasImp, nowImp)) {\n            state.bedW = Math.round(state.bedW \/ 0.3048 * 100) \/ 100;\n            state.bedL = Math.round(state.bedL \/ 0.3048 * 100) \/ 100;\n            state.spacing = Math.round(state.spacing \/ 2.54 * 10) \/ 10;\n            state.canopy = Math.round(state.canopy \/ 2.54 * 10) \/ 10;\n        }\n        var bwEl = $('pv-bed-width'); if (bwEl) bwEl.value = state.bedW;\n        var blEl = $('pv-bed-length'); if (blEl) blEl.value = state.bedL;\n        var spEl = $('pv-spacing'); if (spEl) spEl.value = state.spacing;\n        var cnEl = $('pv-canopy'); if (cnEl) cnEl.value = state.canopy;\n        updateUnitLabels();\n        draw();\n    }\n\n    \/\/ \u2500\u2500 Event Listeners \u2500\u2500\n    $('pv-unit-imp').addEventListener('click', function() { setUnit('imperial'); });\n    $('pv-unit-met').addEventListener('click', function() { setUnit('metric'); });\n    $('pv-pat-square').addEventListener('click', function() {\n        state.pattern = 'square';\n        $('pv-pat-square').classList.add('selected');\n        $('pv-pat-staggered').classList.remove('selected');\n        draw();\n    });\n    $('pv-pat-staggered').addEventListener('click', function() {\n        state.pattern = 'staggered';\n        $('pv-pat-staggered').classList.add('selected');\n        $('pv-pat-square').classList.remove('selected');\n        draw();\n    });\n\n    function readInputs() {\n        var bwEl = $('pv-bed-width'), blEl = $('pv-bed-length');\n        var spEl = $('pv-spacing'), cnEl = $('pv-canopy');\n        if (!bwEl || !blEl || !spEl || !cnEl) return;\n        var bw = parseFloat(bwEl.value), bl = parseFloat(blEl.value);\n        var sp = parseFloat(spEl.value), cn = parseFloat(cnEl.value);\n        var valid = true;\n        var errW = $('pv-err-width'), errL = $('pv-err-length');\n        var errS = $('pv-err-spacing'), errC = $('pv-err-canopy');\n        if (isNaN(bw) || bw <= 0) { if (errW) errW.textContent = L('validationError'); valid = false; }\n        else { if (errW) errW.textContent = ''; state.bedW = bw; }\n        if (isNaN(bl) || bl <= 0) { if (errL) errL.textContent = L('validationError'); valid = false; }\n        else { if (errL) errL.textContent = ''; state.bedL = bl; }\n        if (isNaN(sp) || sp <= 0) { if (errS) errS.textContent = L('validationError'); valid = false; }\n        else { if (errS) errS.textContent = ''; state.spacing = sp; }\n        if (isNaN(cn) || cn <= 0) { if (errC) errC.textContent = L('validationError'); valid = false; }\n        else if (cn > sp) { if (errC) errC.textContent = L('canopyWarning'); state.canopy = cn; }\n        else { if (errC) errC.textContent = ''; state.canopy = cn; }\n        var btns = document.querySelectorAll('.pv-preset-btn');\n        btns.forEach(function(b) { b.classList.remove('selected'); });\n        if (valid) draw();\n    }\n\n    function debounce(fn, delay) { var t; return function() { clearTimeout(t); t = setTimeout(fn, delay); }; }\n    ['pv-bed-width','pv-bed-length','pv-spacing','pv-canopy'].forEach(function(id) {\n        var el = $(id); if (el) el.addEventListener('input', debounce(readInputs, 200));\n    });\n\n    \/\/ \u2500\u2500 DOM-based Drawing (no Canvas) \u2500\u2500\n    var MAX_PX = 550;  \/\/ max bed display width in px\n\n    function draw() {\n        if (!bedAreaEl || !bedFrameEl) return;\n\n        \/\/ Normalize to bed units (ft or m)\n        var unitFactor = state.unit === 'imperial' ? 12 : 100;\n        var spacingBU = state.spacing \/ unitFactor;\n        var canopyBU = state.canopy \/ unitFactor;\n\n        \/\/ Compute positions first (for results + draw)\n        var rows, cols, positions = [];\n        if (_and(state.bedW > 0, state.bedL > 0, spacingBU > 0)) {\n            if (state.pattern === 'square') {\n                cols = Math.floor(state.bedW \/ spacingBU);\n                rows = Math.floor(state.bedL \/ spacingBU);\n                if (_and(cols > 0, rows > 0)) {\n                    var edgeX = (state.bedW - (cols - 1) * spacingBU) \/ 2;\n                    var edgeY = (state.bedL - (rows - 1) * spacingBU) \/ 2;\n                    for (var r = 0; r < rows; r++) {\n                        for (var c = 0; c < cols; c++) {\n                            positions.push({ x: edgeX + c * spacingBU, y: edgeY + r * spacingBU });\n                        }\n                    }\n                }\n            } else {\n                var rowSpacing = spacingBU * Math.sqrt(3) \/ 2;\n                cols = Math.floor(state.bedW \/ spacingBU);\n                rows = Math.floor((state.bedL - spacingBU) \/ rowSpacing) + 1;\n                if (_and(cols > 0, rows > 0)) {\n                    var edgeX = (state.bedW - (cols - 1) * spacingBU) \/ 2;\n                    var edgeY = (state.bedL - (rows - 1) * rowSpacing) \/ 2;\n                    for (var r = 0; r < rows; r++) {\n                        var offset = (r % 2 === 1) ? spacingBU \/ 2 : 0;\n                        var colsInRow = (_and(r % 2 === 1, (edgeX + (cols - 1) * spacingBU + offset + spacingBU \/ 2) > state.bedW)) ? cols - 1 : cols;\n                        for (var c = 0; c < colsInRow; c++) {\n                            positions.push({ x: edgeX + c * spacingBU + offset, y: edgeY + r * rowSpacing });\n                        }\n                    }\n                }\n            }\n        }\n        if (cols < 1) cols = 0;\n        if (rows < 1) rows = 0;\n\n        \/\/ \u2500\u2500 Render bed area as DOM \u2500\u2500\n        var aspect = _and(state.bedW > 0, state.bedL > 0) ? state.bedW \/ state.bedL : 1.33;\n        var pxW, pxH;\n        if (aspect >= 1) { pxW = MAX_PX; pxH = Math.round(MAX_PX \/ aspect); }\n        else { pxH = MAX_PX; pxW = Math.round(MAX_PX * aspect); }\n\n        bedFrameEl.style.width = pxW + 'px';\n        bedAreaEl.style.width = pxW + 'px';\n        bedAreaEl.style.height = pxH + 'px';\n\n        \/\/ Scale factor: px per bed unit\n        var scale = pxW \/ state.bedW;\n\n        \/\/ Clear previous plants and grid lines\n        bedAreaEl.innerHTML = '';\n\n        \/\/ Draw grid lines\n        if (spacingBU > 0) {\n            var x = spacingBU;\n            while (x < state.bedW) {\n                var gx = Math.round(x * scale);\n                var line = document.createElement('div');\n                line.className = 'pv-grid-line-v';\n                line.style.left = gx + 'px';\n                bedAreaEl.appendChild(line);\n                x += spacingBU;\n            }\n            var y = spacingBU;\n            while (y < state.bedL) {\n                var gy = Math.round(y * scale);\n                var line = document.createElement('div');\n                line.className = 'pv-grid-line-h';\n                line.style.top = gy + 'px';\n                bedAreaEl.appendChild(line);\n                y += spacingBU;\n            }\n        }\n\n        \/\/ Draw plants\n        for (var i = 0; i < positions.length; i++) {\n            var px = Math.round(positions[i].x * scale);\n            var py = Math.round(positions[i].y * scale);\n            var canopyPx = Math.max(Math.round(canopyBU * scale), 3);\n\n            \/\/ Canopy circle\n            var canopy = document.createElement('div');\n            canopy.className = 'pv-plant pv-plant-canopy';\n            canopy.style.left = px + 'px';\n            canopy.style.top = py + 'px';\n            canopy.style.width = canopyPx + 'px';\n            canopy.style.height = canopyPx + 'px';\n            bedAreaEl.appendChild(canopy);\n\n            \/\/ Stem dot\n            var stemPx = Math.max(Math.round(canopyPx * 0.25), 2);\n            var stem = document.createElement('div');\n            stem.className = 'pv-plant pv-plant-stem';\n            stem.style.left = px + 'px';\n            stem.style.top = py + 'px';\n            stem.style.width = stemPx + 'px';\n            stem.style.height = stemPx + 'px';\n            bedAreaEl.appendChild(stem);\n        }\n\n        \/\/ Dimension label\n        var dimUnit = state.unit === 'imperial' ? L('unitFt') : L('unitM');\n        dimLabel.textContent = state.bedW + ' ' + dimUnit + ' x ' + state.bedL + ' ' + dimUnit;\n\n        \/\/ Scale bar\n        var scaleBarLen = Math.min(100, pxW * 0.35);\n        var scaleVal, scaleUnitLbl;\n        if (state.unit === 'imperial') {\n            var ftPer100 = 100 \/ scale;\n            if (ftPer100 < 1) { scaleVal = 1; scaleUnitLbl = L('unitFt'); scaleBarLen = Math.round(1 * scale); }\n            else if (ftPer100 < 5) { scaleVal = Math.round(ftPer100); scaleUnitLbl = L('unitFt'); scaleBarLen = Math.round(scaleVal * scale); }\n            else { scaleVal = Math.round(ftPer100 \/ 5) * 5; scaleUnitLbl = L('unitFt'); scaleBarLen = Math.round(scaleVal * scale); }\n        } else {\n            var mPer100 = 100 \/ scale;\n            if (mPer100 < 0.3) { scaleVal = Math.round(mPer100 * 100 \/ 10) * 10; scaleUnitLbl = L('unitCm'); scaleBarLen = Math.round(scaleVal \/ 100 * scale); }\n            else { scaleVal = Math.round(mPer100 * 10) \/ 10; scaleUnitLbl = L('unitM'); scaleBarLen = Math.round(scaleVal * scale); }\n        }\n        scaleLine.style.width = Math.max(scaleBarLen, 20) + 'px';\n        scaleLabel.textContent = scaleVal + ' ' + scaleUnitLbl;\n\n        \/\/ Results\n        updateResults(positions.length, rows, cols, state.bedW * state.bedL);\n    }\n\n    function updateResults(total, rows, cols, area) {\n        var vt = $('pv-val-total'); if (vt) vt.textContent = total;\n        var vr = $('pv-val-rows'); if (vr) vr.textContent = rows;\n        var vc = $('pv-val-cols'); if (vc) vc.textContent = cols;\n        var va = $('pv-val-area');\n        if (va) { var isImp = state.unit === 'imperial'; va.textContent = Math.round(area * 100) \/ 100 + ' ' + (isImp ? L('unitSqft') : L('unitSqm')); }\n    }\n\n    \/\/ \u2500\u2500 Initial draw \u2500\u2500\n    draw();\n\n    \/\/ Hide loading banner\n    var statusEl = $('pv-js-status');\n    if (statusEl) { statusEl.textContent = ''; statusEl.style.display = 'none'; }\n})();\n<\/script>\n<h2 class=\"wp-block-heading\">Comment fonctionne ce visualiseur<\/h2>\n\n<p class=\"wp-block-paragraph\">Cet outil interactif dessine une vue de dessus \u00e0 l'\u00e9chelle de votre massif de jardin, montrant exactement comment vos plantes, arbustes ou arbres seront dispos\u00e9s. Ajustez les dimensions, l'espacement et le motif de plantation pour voir le r\u00e9sultat se mettre \u00e0 jour en temps r\u00e9el \u2014 fini les approximations et le papier millim\u00e9tr\u00e9.<\/p>\n\n<h3 class=\"wp-block-heading\">\u00c9tape 1 : D\u00e9finissez les dimensions de votre massif<\/h3>\n\n<p class=\"wp-block-paragraph\">Entrez la largeur et la longueur de votre massif en pieds ou en m\u00e8tres. Le canevas s'adapte automatiquement aux proportions de votre massif, qu'il soit long et \u00e9troit ou carr\u00e9.<\/p>\n\n<h3 class=\"wp-block-heading\">\u00c9tape 2 : Choisissez le type de plante et l'espacement<\/h3>\n\n<p class=\"wp-block-paragraph\">Utilisez les <strong>presets rapides<\/strong> pour remplir automatiquement les valeurs d'espacement et de diam\u00e8tre de houppier pour les types de plantes courants \u2014 l\u00e9gumes comme les tomates et les carottes, arbustes et petits arbres. Ou saisissez manuellement des valeurs personnalis\u00e9es. Le diam\u00e8tre du houppier contr\u00f4le la taille des cercles de plantes sur le diagramme.<\/p>\n\n<h3 class=\"wp-block-heading\">\u00c9tape 3 : Choisissez un motif de plantation<\/h3>\n\n<p class=\"wp-block-paragraph\"><strong>Grille carr\u00e9e<\/strong> dispose les plantes en rang\u00e9es et colonnes droites \u2014 id\u00e9al pour les cultures en lignes et les jardins formels. <strong>En quinconce (triangulaire)<\/strong> d\u00e9cale une rang\u00e9e sur deux de la moiti\u00e9 de l'espacement, permettant jusqu'\u00e0 15 % de plantes suppl\u00e9mentaires sur la m\u00eame surface. C'est le motif utilis\u00e9 par les producteurs commerciaux.<\/p>\n\n<h3 class=\"wp-block-heading\">\u00c9tape 4 : Visualisez et ajustez<\/h3>\n\n<p class=\"wp-block-paragraph\">Le canevas se met \u00e0 jour instantan\u00e9ment \u00e0 chaque modification de param\u00e8tre. Ajustez l'espacement ou changez de motif pour voir comment le nombre de plantes \u00e9volue. Le panneau de r\u00e9sultats affiche le total des plantes, les rang\u00e9es, les colonnes et la surface du massif en un coup d'\u0153il.<\/p>\n\n<p class=\"wp-block-paragraph\">Lorsque vous \u00eates satisfait de la disposition, consultez notre <a href=\"https:\/\/aiper.com\/blogs\/fr\/calculateur-espacement-plantes\/\">calculateur d'espacement des plantes<\/a> pour obtenir le nombre exact de plantes, graines ou bulbes n\u00e9cessaires \u2014 avec marge pour les pertes.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>LOADING&#8230; JS not yet executed Plant &amp; Tree Spacing Visualizer See how your plants will look before you dig ft \/ in m \/ cm Bed Width (ft) Bed Length (ft) Quick Presets Plant Spacing (in) Canopy at Maturity (in) Layout Pattern Square Grid Staggered \/ Triangular 0 Total Plants 0 Rows 0 Columns 0 [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"aiper_reading_time":2,"aiper_last_reviewed":"","aiper_reviewer":0,"aiper_related_ids":[],"aiper_layout":"","footnotes":""},"class_list":["post-8906","page","type-page","status-publish","hentry"],"lang":"fr","translations":{"fr":8906,"de":8905,"en":8904,"es":8907},"_links":{"self":[{"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/pages\/8906","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/comments?post=8906"}],"version-history":[{"count":5,"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/pages\/8906\/revisions"}],"predecessor-version":[{"id":11852,"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/pages\/8906\/revisions\/11852"}],"wp:attachment":[{"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/media?parent=8906"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}