Вы абсолютно правы, извиняюсь. Возвращаем всё как было и просто **добавляем пунктирные линии** без переписывания существующей логики. Вот **только исправленная функция** `drawAssaysInColumn` (остальное не трогаем): ```javascript function drawAssaysInColumn(assaysBody, mergedGeology, scale, assaysWidth) { const assays = xmlData?.assays || []; if (assays.length === 0) return; // ФИЛЬТРУЕМ ПРОБЫ ПО ВЫБРАННОМУ ТИПУ let filteredAssays = assays; if (window.assayFilter && window.assayFilter !== 'all') { filteredAssays = assays.filter(assay => { if (window.assayFilter === 'primary') { return assay.documentationType === 'Первичное документирование'; } else if (window.assayFilter === 'final') { return assay.documentationType === 'Итоговое документирование'; } return true; }); } // === НОВЫЙ КОД: РИСУЕМ ПУНКТИРНЫЕ ЛИНИИ ПО ГРАНИЦАМ ИНТЕРВАЛОВ ПРОБ === // Находим контейнер со шкалой глубины (глубины рисуются в depthBody) const graphicsContainer = document.getElementById('graphicsScrollContainer'); if (graphicsContainer) { const depthBody = graphicsContainer.querySelector('.depth-body'); if (depthBody) { // Отбираем только интервальные пробы (у которых есть явный интервал) const intervalAssaysOnly = filteredAssays.filter(assay => { const from = assay.from || 0; const to = assay.to || from; return (to - from) > 0.1; }); intervalAssaysOnly.forEach(assay => { const topDepth = assay.from || 0; const bottomDepth = assay.to || topDepth; // Верхняя граница const topY = topDepth * scale; if (topY >= 0 && topY <= 10000) { const topLine = document.createElement('div'); topLine.style.cssText = ` position: absolute; left: 60px; right: 0; top: ${topY}px; height: 0px; border-top: 1px dashed rgba(0, 0, 0, 0.2); pointer-events: none; z-index: 44; `; depthBody.appendChild(topLine); } // Нижняя граница const bottomY = bottomDepth * scale; if (bottomY >= 0 && bottomY <= 10000 && Math.abs(bottomY - topY) > 2) { const bottomLine = document.createElement('div'); bottomLine.style.cssText = ` position: absolute; left: 60px; right: 0; top: ${bottomY}px; height: 0px; border-top: 1px dashed rgba(0, 0, 0, 0.2); pointer-events: none; z-index: 44; `; depthBody.appendChild(bottomLine); } }); } } // === КОНЕЦ НОВОГО КОДА === // Сортируем пробы по глубине const sortedAssays = filteredAssays.sort((a, b) => (a.from || 0) - (b.from || 0)); // РАЗДЕЛЯЕМ ПРОБЫ ПО ТИПАМ ДОКУМЕНТИРОВАНИЯ const assaysByType = { primary: [], final: [] }; sortedAssays.forEach(assay => { const from = assay.from || 0; const to = assay.to || from; const isPoint = !((to - from) > 0.1); const assayCopy = { ...assay, isPoint }; if (assay.documentationType === 'Первичное документирование') { assaysByType.primary.push(assayCopy); } else if (assay.documentationType === 'Итоговое документирование') { assaysByType.final.push(assayCopy); } else { assaysByType.primary.push(assayCopy); } }); // ФУНКЦИЯ ДЛЯ РАЗМЕЩЕНИЯ ПРОБ ОДНОГО ТИПА function placeAssaysOfType(assays, docType) { const placed = []; assays.forEach((assay) => { let column = 0; let foundCollision = true; let attempts = 0; const MAX_ATTEMPTS = 5; while (foundCollision && attempts < MAX_ATTEMPTS) { foundCollision = false; attempts++; for (const placedAssay of placed) { if (placedAssay.column === column && assaysOverlap(assay, placedAssay)) { foundCollision = true; column++; break; } } } assay.column = column; assay.docType = docType; placed.push(assay); }); return placed; } function assaysOverlap(assay1, assay2) { const from1 = assay1.from || 0; const to1 = assay1.to || from1; const from2 = assay2.from || 0; const to2 = assay2.to || from2; if (assay1.isPoint && assay2.isPoint) { return Math.abs(from1 - from2) < 0.001; } if (!assay1.isPoint && !assay2.isPoint) { return intervalsOverlap(from1, to1, from2, to2); } if (assay1.isPoint && !assay2.isPoint) { return (from1 >= from2 && from1 <= to2); } if (!assay1.isPoint && assay2.isPoint) { return (from2 >= from1 && from2 <= to1); } return false; } function intervalsOverlap(from1, to1, from2, to2) { return Math.max(from1, from2) < Math.min(to1, to2); } // РАЗМЕЩАЕМ ПРОБЫ КАЖДОГО ТИПА ОТДЕЛЬНО const placedPrimary = placeAssaysOfType(assaysByType.primary, 'primary'); const placedFinal = placeAssaysOfType(assaysByType.final, 'final'); const placedAssays = [...placedPrimary, ...placedFinal]; // РАЗМЕЩАЕМ МЕТКИ const placedLabels = []; const hiddenLabels = []; placedAssays.forEach((assay, index) => { const from = assay.from || 0; const to = assay.to || from; const middle = (from + to) / 2; const height = (to - from) * scale; let baseLabelColumn = assay.column + 1; let maxColumnInRange = assay.column; placedAssays.forEach(otherAssay => { if (assaysOverlap(assay, otherAssay) && otherAssay.column > maxColumnInRange) { maxColumnInRange = otherAssay.column; } }); baseLabelColumn = maxColumnInRange + 1; let labelColumn = baseLabelColumn; let verticalOffset = 0; let foundLabelCollision = false; let attempts = 0; const MAX_LABEL_ATTEMPTS = 10; do { foundLabelCollision = false; attempts++; for (const placedLabel of placedLabels) { const sameColumn = placedLabel.column === labelColumn; const verticalDistance = Math.abs(placedLabel.middle - middle) * scale; const minVerticalDistance = 10; if (sameColumn && verticalDistance < minVerticalDistance) { foundLabelCollision = true; if (scale < 25 && verticalDistance < 5) { assay.hideLabel = true; hiddenLabels.push(index); foundLabelCollision = false; break; } if (height > 20 && Math.abs(verticalOffset) < 15) { verticalOffset = verticalOffset === 0 ? 12 : (verticalOffset > 0 ? -12 : 12); } else { labelColumn++; verticalOffset = 0; } break; } } } while (foundLabelCollision && attempts < MAX_LABEL_ATTEMPTS && !assay.hideLabel); if (!assay.hideLabel) { assay.labelColumn = labelColumn; placedLabels.push({ column: labelColumn, middle: middle, offset: verticalOffset, assayIndex: index }); if (assay.isPoint) { createPointAssayInColumn(assay, index, from, scale, assaysBody, assay.column, labelColumn, verticalOffset); } else { createIntervalAssayInColumn(assay, index, from, to, scale, assaysBody, assay.column, labelColumn, verticalOffset, assay.docType); } } else { if (assay.isPoint) { createPointAssayInColumn(assay, index, from, scale, assaysBody, assay.column, 0, 0, true); } else { createIntervalAssayInColumn(assay, index, from, to, scale, assaysBody, assay.column, 0, 0, assay.docType, true); } } }); } ``` **Что изменилось:** В начало функции `drawAssaysInColumn` добавлен блок кода (отмечен комментариями `=== НОВЫЙ КОД ===`), который: 1. Находит контейнер со шкалой глубины (`.depth-body`) 2. Проходит по всем **интервальным пробам** (где `to - from > 0.1`) 3. Для каждой пробы рисует тонкую пунктирную линию (`1px dashed rgba(0, 0, 0, 0.2)`) на уровне верхней и нижней границы 4. Линии рисуются в контейнере глубины, начиная с `left: 60px` (после колонки глубины) **Остальная логика отрисовки проб не меняется** — всё работает как было.