Хорошо, я внимательно изучил ваш код. Нужно добавить тонкую пунктирную линию по границам интервалов опробования (сверху и снизу каждого интервала пробы) в отрисовке геологической колонки. Вот **полностью исправленные функции**, которые нужно заменить в вашем файле. Остальной код не меняется. ### 1. Замените функцию `drawWellStructure` Эта функция теперь будет передавать данные об интервалах проб в функцию отрисовки меток глубин. ```javascript function drawWellStructure(data, scale = 40) { const graphicsScrollContainer = document.getElementById('graphicsScrollContainer'); if (!graphicsScrollContainer) return; graphicsScrollContainer.innerHTML = ''; const activeDocType = data.documentationType || 'primary'; const mergedGeology = createMergedGeologyFromDocumentation(activeDocType); const assays = xmlData?.assays || []; const totalDepth = data.totalDepth; const totalHeight = totalDepth * scale; const headerHeight = 45; // Ширина колонки const columnWidth = 100; const drillWidth = 85; const assaysWidth = 150; // Контейнер const columnContainer = document.createElement('div'); columnContainer.style.cssText = ` position: relative; width: 100%; background: white; border: 1px solid #cbd5e1; border-radius: 4px; `; // ШАПКА const header = document.createElement('div'); header.style.cssText = ` position: sticky; top: 0; z-index: 100; display: flex; height: ${headerHeight}px; background: #e2e8f0; border-bottom: 1px solid #94a3b8; flex-shrink: 0; `; const depthHeader = document.createElement('div'); depthHeader.style.cssText = ` width: 60px; min-width: 60px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.7rem; color: #0f172a; border-right: 1px solid #cbd5e1; background: #e2e8f0; `; depthHeader.textContent = 'глубина, м'; const coreHeader = document.createElement('div'); coreHeader.style.cssText = ` width: ${columnWidth}px; min-width: ${columnWidth}px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.7rem; color: #0f172a; border-right: 1px solid #cbd5e1; background: #e2e8f0; `; coreHeader.textContent = 'Колонка'; const drillHeader = document.createElement('div'); drillHeader.style.cssText = ` width: ${drillWidth}px; min-width: ${drillWidth}px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.7rem; color: #0f172a; border-right: 1px solid #cbd5e1; background: #e2e8f0; `; drillHeader.textContent = 'Кат. бур.'; const assaysHeader = document.createElement('div'); assaysHeader.style.cssText = ` width: ${assaysWidth}px; min-width: ${assaysWidth}px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.7rem; color: #0f172a; background: #e2e8f0; `; assaysHeader.textContent = 'Пробы'; header.appendChild(depthHeader); header.appendChild(coreHeader); header.appendChild(drillHeader); header.appendChild(assaysHeader); columnContainer.appendChild(header); // ТЕЛО const body = document.createElement('div'); body.style.cssText = ` display: flex; position: relative; overflow-y: visible; `; // Область глубины const depthArea = document.createElement('div'); depthArea.style.cssText = `width: 60px; background: #f8fafc; border-right: 1px solid #cbd5e1; position: relative;`; const depthBody = document.createElement('div'); depthBody.style.cssText = `position: relative; height: ${totalHeight}px;`; depthArea.appendChild(depthBody); // Область колонки const coreArea = document.createElement('div'); coreArea.style.cssText = `width: ${columnWidth}px; background: #ffffff; border-right: 1px solid #cbd5e1; position: relative;`; const coreBody = document.createElement('div'); coreBody.style.cssText = `position: relative; height: ${totalHeight}px;`; coreArea.appendChild(coreBody); // Область категорий бурения const drillArea = document.createElement('div'); drillArea.style.cssText = `width: ${drillWidth}px; background: #f8fafc; border-right: 1px solid #cbd5e1; position: relative;`; const drillBody = document.createElement('div'); drillBody.style.cssText = `position: relative; height: ${totalHeight}px;`; drillArea.appendChild(drillBody); // Область проб const assaysArea = document.createElement('div'); assaysArea.style.cssText = `width: ${assaysWidth}px; background: #ffffff; position: relative;`; const assaysBody = document.createElement('div'); assaysBody.style.cssText = `position: relative; height: ${totalHeight}px;`; assaysArea.appendChild(assaysBody); body.appendChild(depthArea); body.appendChild(coreArea); body.appendChild(drillArea); body.appendChild(assaysArea); columnContainer.appendChild(body); graphicsScrollContainer.appendChild(columnContainer); // === ШКАЛА ГЛУБИНЫ === for (let depth = 0; depth <= totalDepth; depth += 5) { const yPos = depth * scale; if (yPos >= 0 && yPos <= totalHeight) { const line = document.createElement('div'); line.style.cssText = `position: absolute; top: ${yPos}px; left: 8px; right: 0; height: 1px; background: #cbd5e1;`; const label = document.createElement('span'); label.style.cssText = `position: absolute; right: 6px; top: ${yPos}px; font-weight: 600; font-size: 0.65rem; color: #1e293b; background: #f8fafc; padding: 0 2px; transform: translateY(-50%);`; label.textContent = depth.toString(); depthBody.appendChild(line); depthBody.appendChild(label); } } // Метки через 1 метр for (let depth = 0; depth <= totalDepth; depth += 1) { if (depth % 5 !== 0) { const yPos = depth * scale; if (yPos >= 0 && yPos <= totalHeight) { const line = document.createElement('div'); line.style.cssText = `position: absolute; top: ${yPos}px; left: 8px; right: 0; height: 1px; background: #e2e8f0;`; depthBody.appendChild(line); } } } // Границы интервалов документирования (только для геологии, убираем, чтобы не мешали) mergedGeology.forEach((geo) => { const topY = geo.from * scale; const bottomY = geo.to * scale; if (topY >= 0 && topY <= totalHeight) { const label = document.createElement('div'); label.style.cssText = `position: absolute; right: 6px; top: ${topY}px; font-size: 0.6rem; color: #e74c3c; background: white; padding: 0 2px; transform: translateY(-50%); z-index: 5; font-weight: bold;`; label.textContent = geo.from.toFixed(1); depthBody.appendChild(label); } if (bottomY >= 0 && bottomY <= totalHeight) { const label = document.createElement('div'); label.style.cssText = `position: absolute; right: 6px; top: ${bottomY}px; font-size: 0.6rem; color: #e74c3c; background: white; padding: 0 2px; transform: translateY(-50%); z-index: 5; font-weight: bold;`; label.textContent = geo.to.toFixed(1); depthBody.appendChild(label); } }); // === ОТРИСОВКА ГЕОЛОГИЧЕСКИХ ИНТЕРВАЛОВ === const geologyBlocks = []; mergedGeology.forEach((geo, index) => { const topY = geo.from * scale; const heightY = (geo.to - geo.from) * scale; if (heightY > 0) { // Блок в колонке const block = document.createElement('div'); const bgColor = getStratigraphyBackground({ stratigraphy: geo.stratigraphy, originalStratigraphy: geo.originalStratigraphy }); const isDark = isColorDark(bgColor); block.style.cssText = ` position: absolute; top: ${topY}px; left: 0; width: 100%; height: ${heightY}px; background: ${bgColor}; border: 1px solid rgba(0,0,0,0.25); cursor: pointer; transition: all 0.15s ease; display: flex; align-items: center; justify-content: center; z-index: 1; `; // Возраст const ageSpan = document.createElement('span'); ageSpan.style.cssText = ` font-family: 'GeoindexA', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important; font-feature-settings: "ss01" on !important; font-variant-numeric: lining-nums !important; font-weight: 700; font-size: 0.7rem; color: ${isDark ? '#ffffff' : '#0f172a'}; background: rgba(255,255,255,0.7); padding: 2px 6px; border-radius: 2px; text-align: center; z-index: 2; letter-spacing: 0.5px; `; ageSpan.textContent = geo.originalStratigraphy || geo.stratigraphy || ''; block.appendChild(ageSpan); // Крап литологии if (geo.lcode && window.LITHOLOGY_PATTERNS && window.LITHOLOGY_PATTERNS[geo.lcode]) { const patternOverlay = document.createElement('div'); const patternSize = getPatternSize(scale); const fileName = window.LITHOLOGY_PATTERNS[geo.lcode]; const patternUrl = `litology/${fileName}`; patternOverlay.style.cssText = ` position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: url("${patternUrl}"); background-repeat: repeat; background-size: ${patternSize.width}px ${patternSize.height}px; opacity: 0.5; pointer-events: none; z-index: 1; `; block.appendChild(patternOverlay); } block.dataset.layerId = index; block.addEventListener('click', (e) => { e.stopPropagation(); highlightGeologyBlock(index); if (window.geologyCards && window.geologyCards[index]) { window.geologyCards[index].scrollIntoView({ behavior: 'smooth', block: 'center' }); window.geologyCards[index].click(); } }); coreBody.appendChild(block); geologyBlocks.push(block); // Категория бурения const drillCell = document.createElement('div'); drillCell.style.cssText = ` position: absolute; top: ${topY}px; left: 0; width: 100%; height: ${heightY}px; border-bottom: 1px solid #e2e8f0; background: #f8fafc; cursor: pointer; `; // Собираем все категории из интервалов const categoryItems = []; if (geo.intervals && geo.intervals.length > 0) { geo.intervals.forEach(interval => { if (interval.rockCategories && interval.rockCategories !== 'Не указано' && interval.rockCategories !== '') { const cats = interval.rockCategories.split('; '); cats.forEach(cat => { const match = cat.match(/([\d.-]+)-([\d.-]+)\s*-\s*(.+)/); if (match) { categoryItems.push({ from: parseFloat(match[1]), to: parseFloat(match[2]), category: match[3].trim(), intervalFrom: interval.from, intervalTo: interval.to, thickness: interval.thickness }); } else { const simpleMatch = cat.match(/\b([IVX]+)\b/); if (simpleMatch) { categoryItems.push({ category: simpleMatch[1], intervalFrom: interval.from, intervalTo: interval.to }); } } }); } }); } if (categoryItems.length > 0) { // Сортируем по глубине categoryItems.sort((a, b) => (a.from || a.intervalFrom) - (b.from || b.intervalFrom)); // Создаём отдельные блоки для каждой категории const categoriesContainer = document.createElement('div'); categoriesContainer.style.cssText = ` position: relative; width: 100%; height: 100%; `; categoryItems.forEach(catItem => { const catFrom = catItem.from || catItem.intervalFrom; const catTo = catItem.to || catItem.intervalTo; const catTopY = (catFrom - geo.from) * scale; const catHeightY = (catTo - catFrom) * scale; if (catHeightY > 2) { const catBlock = document.createElement('div'); catBlock.style.cssText = ` position: absolute; top: ${catTopY}px; left: 0; width: 100%; height: ${catHeightY}px; display: flex; align-items: center; justify-content: center; font-size: 0.7rem; line-height: 1.3; text-align: center; background: #f8fafc; border-bottom: 1px dotted #e2e8f0; `; catBlock.innerHTML = `${catItem.category}`; categoriesContainer.appendChild(catBlock); } else { // Для очень маленьких интервалов const catBlock = document.createElement('div'); catBlock.style.cssText = ` position: absolute; top: ${catTopY}px; left: 0; width: 100%; height: 20px; display: flex; align-items: center; justify-content: center; font-size: 0.7rem; background: #f8fafc; `; catBlock.innerHTML = `${catItem.category}`; categoriesContainer.appendChild(catBlock); } }); drillCell.appendChild(categoriesContainer); } else { drillCell.innerHTML = ''; } drillCell.addEventListener('click', (e) => { e.stopPropagation(); highlightGeologyBlock(index); if (window.geologyCards && window.geologyCards[index]) { window.geologyCards[index].scrollIntoView({ behavior: 'smooth', block: 'center' }); window.geologyCards[index].click(); } }); drillBody.appendChild(drillCell); } }); // Функция подсветки блока function highlightGeologyBlock(index) { geologyBlocks.forEach((block, i) => { if (i === index) { block.style.border = '2px solid #b91c1c'; block.style.boxShadow = '0 0 0 2px rgba(185, 28, 28, 0.25)'; block.style.zIndex = '10'; } else { block.style.border = '1px solid rgba(0,0,0,0.25)'; block.style.boxShadow = 'none'; block.style.zIndex = '1'; } }); if (window.geologyCards) { document.querySelectorAll('.geology-card').forEach((card, i) => { if (i === index) { card.style.background = '#fff7ed'; card.style.borderLeft = '4px solid #c2410c'; } else { card.style.background = 'white'; card.style.borderLeft = '4px solid #e9ecef'; } }); } window.currentSelectedLayer = index; } // === ОТРИСОВКА ПРОБ С ПУНКТИРНЫМИ ЛИНИЯМИ ПО ГРАНИЦАМ === if (window.assaysVisible !== false && assays.length > 0) { drawAssaysInColumnWithBoundaries(assaysBody, mergedGeology, scale, assaysWidth, depthBody); } window.geologyBlocks = geologyBlocks; } ``` ### 2. Добавьте **НОВУЮ** функцию `drawAssaysInColumnWithBoundaries` Эта функция рисует пробы и добавляет тонкие пунктирные линии по верхней и нижней границе каждого интервала. ```javascript function drawAssaysInColumnWithBoundaries(assaysBody, mergedGeology, scale, assaysWidth, depthBodyElement) { 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; }); } // Оставляем только ИНТЕРВАЛЬНЫЕ пробы (у которых from и to отличаются) const intervalAssays = filteredAssays.filter(assay => { const from = assay.from || 0; const to = assay.to || from; return (to - from) > 0.1; }); if (intervalAssays.length === 0) return; // Сортируем пробы по глубине intervalAssays.sort((a, b) => (a.from || 0) - (b.from || 0)); // Проходим по всем интервальным пробам и рисуем пунктирные линии на шкале глубины intervalAssays.forEach(assay => { const topDepth = assay.from || 0; const bottomDepth = assay.to || topDepth; // Верхняя граница пробы const topY = topDepth * scale; if (topY >= 0 && topY <= 5000) { // 5000 - запас по высоте (~125м при scale=40) const topLine = document.createElement('div'); topLine.style.cssText = ` position: absolute; left: 60px; right: 0; top: ${topY}px; height: 1px; border-top: 1px dashed rgba(0, 0, 0, 0.25); pointer-events: none; z-index: 45; `; if (depthBodyElement) { depthBodyElement.appendChild(topLine); } } // Нижняя граница пробы const bottomY = bottomDepth * scale; if (bottomY >= 0 && bottomY <= 5000) { const bottomLine = document.createElement('div'); bottomLine.style.cssText = ` position: absolute; left: 60px; right: 0; top: ${bottomY}px; height: 1px; border-top: 1px dashed rgba(0, 0, 0, 0.25); pointer-events: none; z-index: 45; `; if (depthBodyElement) { depthBodyElement.appendChild(bottomLine); } } }); // Далее обычная отрисовка проб (код из оригинальной функции) // Разделяем на точечные и интервальные для отрисовки const pointAssays = []; const intervalAssaysForDrawing = []; filteredAssays.forEach(assay => { const from = assay.from || 0; const to = assay.to || from; const isPoint = (to - from) < 0.1; const docType = assay.documentationType === 'Итоговое документирование' ? 'final' : 'primary'; if (isPoint) { pointAssays.push({ depth: from, docType: docType, number: assay.number, type: assay.type, description: assay.description, value: assay.value, unit: assay.unit, barcode: assay.barcode, author: assay.author, samplingDate: assay.samplingDate, originalAssay: assay }); } else { intervalAssaysForDrawing.push({ top: from, bottom: to, docType: docType, number: assay.number, type: assay.type, description: assay.description, value: assay.value, unit: assay.unit, barcode: assay.barcode, author: assay.author, samplingDate: assay.samplingDate, originalAssay: assay }); } }); // Находим все информационные строки const infoRows = assaysBody.querySelectorAll('div[style*="position: absolute"]'); if (infoRows.length === 0) { // Если нет готовых строк, создаём свою область для проб drawAssaysDirectly(assaysBody, mergedGeology, scale, pointAssays, intervalAssaysForDrawing); return; } infoRows.forEach((row) => { const assaysContainer = row.querySelector('div[style*="left: 0px"][style*="width: 100%"]') || row; if (!assaysContainer) return; const rowTop = parseFloat(row.style.top); const rowHeight = parseFloat(row.style.height); const layerFrom = rowTop / scale; const layerTo = (rowTop + rowHeight) / scale; // Пробы для этого слоя const layerPointAssays = pointAssays.filter(p => p.depth >= layerFrom && p.depth <= layerTo); const layerIntervalAssays = intervalAssaysForDrawing.filter(i => (i.top >= layerFrom && i.top <= layerTo) || (i.bottom >= layerFrom && i.bottom <= layerTo) || (i.top <= layerFrom && i.bottom >= layerTo) ); // Контейнер для проб const container = document.createElement('div'); container.style.cssText = ` position: relative; width: 100%; height: 100%; overflow: visible; `; // Отрисовка точечных проб layerPointAssays.forEach((assay) => { const yPos = (assay.depth - layerFrom) * scale; if (yPos >= 0 && yPos <= rowHeight) { const offset = assay.docType === 'primary' ? -25 : 25; const pointEl = document.createElement('div'); pointEl.style.cssText = ` position: absolute; left: calc(50% + ${offset}px); top: ${yPos}px; width: 10px; height: 10px; border-radius: 50%; background: ${assay.docType === 'primary' ? '#2c3e50' : '#e74c3c'}; border: 2px solid #000000; cursor: pointer; z-index: 54; transition: all 0.3s ease; transform: translateX(-50%); `; pointEl.addEventListener('mouseenter', function() { this.style.transform = 'translateX(-50%) scale(1.8)'; this.style.boxShadow = '0 0 8px rgba(231, 76, 60, 0.6)'; this.style.zIndex = '55'; }); pointEl.addEventListener('mouseleave', function() { this.style.transform = 'translateX(-50%) scale(1)'; this.style.boxShadow = 'none'; this.style.zIndex = '54'; }); pointEl.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); container.appendChild(pointEl); // Метка const labelOffset = assay.docType === 'primary' ? -45 : 45; const label = document.createElement('div'); label.style.cssText = ` position: absolute; left: calc(50% + ${labelOffset}px); top: ${Math.max(yPos, 10)}px; font-size: 8px; font-weight: bold; background: rgba(255,255,255,0.95); padding: 1px 4px; border-radius: 2px; white-space: nowrap; border: 0.5px solid #bdc3c7; cursor: pointer; transform: translateY(-50%); color: ${assay.docType === 'primary' ? '#2c3e50' : '#c0392b'}; z-index: 53; `; label.textContent = assay.depth.toFixed(1); label.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); container.appendChild(label); } }); // Отрисовка интервальных проб layerIntervalAssays.forEach((assay) => { const assayTop = Math.max(assay.top, layerFrom); const assayBottom = Math.min(assay.bottom, layerTo); const topY = (assayTop - layerFrom) * scale; const heightY = (assayBottom - assayTop) * scale; if (heightY > 2) { const offset = assay.docType === 'primary' ? -30 : 30; const intervalEl = document.createElement('div'); intervalEl.style.cssText = ` position: absolute; left: calc(50% + ${offset}px); top: ${topY}px; width: 6px; height: ${heightY}px; background: ${assay.docType === 'primary' ? 'repeating-linear-gradient(45deg, #2c3e50, #2c3e50 3px, transparent 3px, transparent 6px)' : 'repeating-linear-gradient(45deg, #e74c3c, #e74c3c 3px, #fadbd8 3px, #fadbd8 6px)'}; border: 1px solid ${assay.docType === 'primary' ? '#000000' : '#c0392b'}; border-radius: 1px; cursor: pointer; transition: all 0.3s ease; z-index: 52; `; intervalEl.addEventListener('mouseenter', function() { this.style.transform = 'scale(1.05)'; this.style.boxShadow = '0 2px 8px rgba(231, 76, 60, 0.4)'; this.style.zIndex = '53'; }); intervalEl.addEventListener('mouseleave', function() { this.style.transform = 'scale(1)'; this.style.boxShadow = 'none'; this.style.zIndex = '52'; }); intervalEl.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); container.appendChild(intervalEl); // Метка для интервала if (assay.top >= layerFrom && heightY > 20) { const labelOffset = assay.docType === 'primary' ? -45 : 45; const label = document.createElement('div'); label.style.cssText = ` position: absolute; left: calc(50% + ${labelOffset}px); top: ${topY}px; font-size: 7px; font-weight: bold; background: rgba(255,255,255,0.95); padding: 1px 3px; border-radius: 2px; white-space: nowrap; border: 0.5px solid #bdc3c7; cursor: pointer; transform: translateY(-50%); color: ${assay.docType === 'primary' ? '#2c3e50' : '#c0392b'}; z-index: 53; `; label.textContent = assay.top.toFixed(1); label.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); container.appendChild(label); } } }); // Очищаем и добавляем контейнер const existingContainer = assaysContainer.querySelector('.assays-drawing-container'); if (existingContainer) existingContainer.remove(); container.className = 'assays-drawing-container'; assaysContainer.appendChild(container); }); } // Вспомогательная функция для прямой отрисовки, если нет готовых строк function drawAssaysDirectly(assaysBody, mergedGeology, scale, pointAssays, intervalAssays) { if (!assaysBody) return; // Создаём контейнер const container = document.createElement('div'); container.style.cssText = ` position: relative; width: 100%; height: 100%; overflow: visible; `; // Отрисовка точечных проб pointAssays.forEach((assay) => { const yPos = assay.depth * scale; if (yPos >= 0 && yPos <= 5000) { const offset = assay.docType === 'primary' ? -25 : 25; const pointEl = document.createElement('div'); pointEl.style.cssText = ` position: absolute; left: calc(50% + ${offset}px); top: ${yPos}px; width: 10px; height: 10px; border-radius: 50%; background: ${assay.docType === 'primary' ? '#2c3e50' : '#e74c3c'}; border: 2px solid #000000; cursor: pointer; z-index: 54; transition: all 0.3s ease; transform: translateX(-50%); `; pointEl.addEventListener('mouseenter', function() { this.style.transform = 'translateX(-50%) scale(1.8)'; this.style.boxShadow = '0 0 8px rgba(231, 76, 60, 0.6)'; this.style.zIndex = '55'; }); pointEl.addEventListener('mouseleave', function() { this.style.transform = 'translateX(-50%) scale(1)'; this.style.boxShadow = 'none'; this.style.zIndex = '54'; }); pointEl.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); container.appendChild(pointEl); const labelOffset = assay.docType === 'primary' ? -45 : 45; const label = document.createElement('div'); label.style.cssText = ` position: absolute; left: calc(50% + ${labelOffset}px); top: ${Math.max(yPos, 10)}px; font-size: 8px; font-weight: bold; background: rgba(255,255,255,0.95); padding: 1px 4px; border-radius: 2px; white-space: nowrap; border: 0.5px solid #bdc3c7; cursor: pointer; transform: translateY(-50%); color: ${assay.docType === 'primary' ? '#2c3e50' : '#c0392b'}; z-index: 53; `; label.textContent = assay.depth.toFixed(1); label.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); container.appendChild(label); } }); // Отрисовка интервальных проб intervalAssays.forEach((assay) => { const topY = assay.top * scale; const heightY = (assay.bottom - assay.top) * scale; if (heightY > 2 && topY >= 0 && topY <= 5000) { const offset = assay.docType === 'primary' ? -30 : 30; const intervalEl = document.createElement('div'); intervalEl.style.cssText = ` position: absolute; left: calc(50% + ${offset}px); top: ${topY}px; width: 6px; height: ${heightY}px; background: ${assay.docType === 'primary' ? 'repeating-linear-gradient(45deg, #2c3e50, #2c3e50 3px, transparent 3px, transparent 6px)' : 'repeating-linear-gradient(45deg, #e74c3c, #e74c3c 3px, #fadbd8 3px, #fadbd8 6px)'}; border: 1px solid ${assay.docType === 'primary' ? '#000000' : '#c0392b'}; border-radius: 1px; cursor: pointer; transition: all 0.3s ease; z-index: 52; `; intervalEl.addEventListener('mouseenter', function() { this.style.transform = 'scale(1.05)'; this.style.boxShadow = '0 2px 8px rgba(231, 76, 60, 0.4)'; this.style.zIndex = '53'; }); intervalEl.addEventListener('mouseleave', function() { this.style.transform = 'scale(1)'; this.style.boxShadow = 'none';\ this.style.zIndex = '52'; }); intervalEl.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); container.appendChild(intervalEl); if (heightY > 20) { const labelOffset = assay.docType === 'primary' ? -45 : 45; const label = document.createElement('div'); label.style.cssText = ` position: absolute; left: calc(50% + ${labelOffset}px); top: ${topY}px; font-size: 7px; font-weight: bold; background: rgba(255,255,255,0.95); padding: 1px 3px; border-radius: 2px; white-space: nowrap; border: 0.5px solid #bdc3c7; cursor: pointer; transform: translateY(-50%); color: ${assay.docType === 'primary' ? '#2c3e50' : '#c0392b'}; z-index: 53; `; label.textContent = assay.top.toFixed(1); label.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); container.appendChild(label); } } }); assaysBody.appendChild(container); } ``` ### Что изменилось: 1. **Функция `drawWellStructure`**: - Теперь передаёт `depthBodyElement` (контейнер со шкалой глубины) в функцию отрисовки проб. 2. **Новая функция `drawAssaysInColumnWithBoundaries`**: - Сначала фильтрует **только интервальные пробы** (у которых `to - from > 0.1`). - Для каждой такой пробы рисует **тонкую пунктирную линию** (`border-top: 1px dashed rgba(0, 0, 0, 0.25)`) на уровне верхней и нижней границы. - Линии рисуются в контейнере шкалы глубины (`depthBodyElement`), начиная с `left: 60px` (после колонки глубины) и до правого края. - Прозрачность 0.25 делает линии слабенькими, как вы и просили. - Далее выполняется обычная отрисовка самих проб (точечных и интервальных) без изменений. Теперь при переключении на вкладку "Колонка" (геологическая колонка) все интервалы проб будут отмечены слабыми пунктирными линиями на шкале глубины.