baselode 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/baselode.js +1477 -1158
- package/dist/baselode.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/baselode.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baselode.js","sources":["../src/data/datamodel.js","../src/data/keying.js","../src/data/assayFieldSets.js","../src/data/dataErrorUtils.js","../src/data/assayLoader.js","../src/data/csvRowUtils.js","../src/data/traceGridConfig.js","../src/data/columnMeta.js","../src/data/assayDataLoader.js","../src/data/desurvey.js","../src/data/desurveyMethods.js","../src/data/drillholeLoader.js","../src/data/datasetLoader.js","../src/data/blockModelLoader.js","../src/data/structuralLoader.js","../src/data/unifiedLoader.js","../src/data/intercepts.js","../src/data/structuralPositions.js","../src/viz/baselodeTemplate.js","../src/viz/baselodeDarkTemplate.js","../src/viz/colourMap.js","../src/viz/drillholeViz.js","../src/viz/structuralViz.js","../src/viz/TracePlot.jsx","../src/viz/useDrillholeTraceGrid.jsx","../src/viz/assayColorScale.js","../src/viz/view2d.js","../src/viz/view3dPayload.js","../src/viz/sceneSelectables.js","../src/viz/structuralScene.js","../src/viz/rasterOverlayScene.js","../src/viz/baselode3dCameraControls.js","../src/viz/drillholeScene.js","../src/viz/selectionGlow.js","../src/viz/blockModelScene.js","../src/viz/sceneClickHandler.js","../src/viz/baselode3dScene.js","../src/viz/Baselode3DControls.jsx","../src/viz/BlockModelWidget.jsx","../src/viz/corePhotoViz.js","../src/viz/CorePhotoTable.jsx","../src/viz/CorePhotoViewer.jsx","../src/grade_blocks/gradeBlockLoader.js"],"sourcesContent":["/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Baselode Open Data Model for JavaScript/TypeScript\n *\n * Provides a consistent schema for data handling throughout the library.\n *\n * Individual data loaders apply common column mapping, but also accept user-provided column maps to handle variations in source data.\n */\n\n// Standard field names\nexport const HOLE_ID = \"hole_id\";\nexport const LATITUDE = \"latitude\";\nexport const LONGITUDE = \"longitude\";\nexport const ELEVATION = \"elevation\";\nexport const AZIMUTH = \"azimuth\";\nexport const DIP = \"dip\";\nexport const FROM = \"from\";\nexport const TO = \"to\";\nexport const MID = \"mid\";\nexport const PROJECT_ID = \"project_id\";\nexport const EASTING = \"easting\";\nexport const NORTHING = \"northing\";\nexport const CRS = \"crs\";\nexport const DEPTH = \"depth\";\nexport const ALPHA = \"alpha\";\nexport const BETA = \"beta\";\nexport const STRIKE = \"strike\";\nexport const GEOLOGY_CODE = \"geology_code\";\nexport const GEOLOGY_DESCRIPTION = \"geology_description\";\n\n/**\n * Minimum expected columns for drillhole collars\n * The collar forms the basis for hole_id and spatial location, so it is expected to exist in all datasets and be standardized as much as possible.\n */\nexport const BASELODE_DATA_MODEL_DRILL_COLLAR = {\n // A unique hole identifier across the entire dataset and all future data sets\n [HOLE_ID]: \"string\",\n // The hole ID from the original collar source\n \"datasource_hole_id\": \"string\",\n // The project ID or project code from the original collar source, if available\n [PROJECT_ID]: \"string\",\n // The latitude of the collar, in decimal degrees (WGS84)\n [LATITUDE]: \"number\",\n // The longitude of the collar, in decimal degrees (WGS84)\n [LONGITUDE]: \"number\",\n // The elevation of the collar, in meters above sea level (WGS84)\n [ELEVATION]: \"number\",\n // The easting coordinate of the collar, in meters (projected CRS)\n [EASTING]: \"number\",\n // The northing coordinate of the collar, in meters (projected CRS)\n [NORTHING]: \"number\",\n // The coordinate reference system of the collar coordinates for easting/northing, as an EPSG code or proj string\n [CRS]: \"string\"\n};\n\nexport const BASELODE_DATA_MODEL_DRILL_SURVEY = {\n // The unique hole id that maps to the collar and any other data tables\n [HOLE_ID]: \"string\",\n // The depth along the hole where the survey measurement was taken / started\n [DEPTH]: \"number\",\n // The depth along the hole where the survey measurement ended, if applicable (some surveys are point measurements and may not have a 'to' depth)\n [TO]: \"number\",\n // The azimuth of the hole at the survey depth, in degrees from north\n [AZIMUTH]: \"number\",\n // The dip of the hole at the survey depth, in degrees from horizontal (negative values indicate downward inclination)\n [DIP]: \"number\"\n};\n\nexport const BASELODE_DATA_MODEL_DRILL_ASSAY = {\n // The unique hole id that maps to the collar and any other data tables\n [HOLE_ID]: \"string\",\n // The depth along the hole where the assay interval starts\n [FROM]: \"number\",\n // The depth along the hole where the assay interval ends\n [TO]: \"number\",\n // The midpoint depth of the assay interval\n [MID]: \"number\",\n // assay value columns are variable and not standardized here. \n // Assays may be flattened (one column per assay type) or long (one row per assay type with an additional 'assay_type' column)\n};\n\nexport const BASELODE_DATA_MODEL_DRILL_GEOLOGY = {\n [HOLE_ID]: \"string\",\n [FROM]: \"number\",\n [TO]: \"number\",\n [MID]: \"number\",\n [GEOLOGY_CODE]: \"string\",\n [GEOLOGY_DESCRIPTION]: \"string\"\n};\n\n/**\n * Structural point data model schema\n */\nexport const BASELODE_DATA_MODEL_STRUCTURAL_POINT = {\n [HOLE_ID]: \"string\",\n [DEPTH]: \"number\",\n [DIP]: \"number\",\n [AZIMUTH]: \"number\",\n [ALPHA]: \"number\",\n [BETA]: \"number\",\n \"comments\": \"string\",\n};\n\n/**\n * This column map is used to make a 'best guess' for mapping common variations in source column names to the baselode data model.\n * It is applied in the standardizeColumns function, but users can also provide their own column map to override or extend this mapping as needed.\n * The keys from the input source are normalized to lowercase and stripped of whitespace for more robust matching.\n * This dictionary is stored for human readability, then pivoted to make lookup quicker in code.\n * Be cautious of not mapping a source column to multiple baselode columns, as this can lead to unpredictable results.\n */\nexport const DEFAULT_COLUMN_MAP = {\n [HOLE_ID]: [\"hole_id\", \"holeid\", \"hole id\", \"hole-id\"],\n \"datasource_hole_id\": [\"datasource_hole_id\", \"datasourceholeid\", \"datasource hole id\", \"datasource-hole-id\", \"company_hole_id\", \"companyholeid\", \"company hole id\", \"company-hole-id\"],\n [PROJECT_ID]: [\"project_id\", \"projectid\", \"project id\", \"project-id\", \"project_code\", \"projectcode\", \"project code\", \"project-code\", \"companyId\", \"company_id\", \"companyid\", \"company id\", \"company-id\", \"dataset\", \"project\"],\n [LATITUDE]: [\"latitude\", \"lat\"],\n [LONGITUDE]: [\"longitude\", \"lon\"],\n [ELEVATION]: [\"elevation\", \"rl\", \"elev\", \"z\"],\n [EASTING]: [\"easting\", \"x\"],\n [NORTHING]: [\"northing\", \"y\"],\n [CRS]: [\"crs\", \"epsg\", \"projection\"],\n [FROM]: [\"from\", \"depth_from\", \"from_depth\", \"samp_from\", \"sample_from\", \"sampfrom\", \"fromdepth\"],\n [TO]: [\"to\", \"depth_to\", \"to_depth\", \"samp_to\", \"sample_to\", \"sampto\", \"todepth\"],\n [GEOLOGY_CODE]: [\n \"geology_code\",\n \"geologycode\",\n \"lith1\",\n \"lith1code\",\n \"lith1_code\",\n \"lithology\",\n \"plot_lithology\",\n \"rock1\"\n ],\n [GEOLOGY_DESCRIPTION]: [\n \"geology_description\",\n \"geologydescription\",\n \"geology_comment\",\n \"geologycomment\",\n \"geology comment\",\n \"lithology_comment\",\n \"lithology comment\",\n \"description\",\n \"comments\"\n ],\n [AZIMUTH]: [\"azimuth\", \"az\", \"dip_direction\", \"dipdir\", \"dip direction\", \"dipdrn\", \"dipdirection\", \"dip_dir\", \"computed_plane_azimuth\", \"calc_dipdir\", \"calc_dipdir_deg\", \"dipdir_calc\", \"dipdirect_calc\"],\n [DIP]: [\"dip\", \"computed_plane_dip\", \"calc_dip\", \"calc_dip_deg\", \"dip_calc\"],\n [ALPHA]: [\"alpha\", \"alpha_angle\", \"alpha_angle_deg\", \"alpha_2\"],\n [BETA]: [\"beta\", \"beta_angle\", \"beta_angle_deg\", \"beta_2\"],\n \"declination\": [\"declination\", \"dec\"],\n [DEPTH]: [\"depth\", \"survey_depth\", \"surveydepth\"],\n [STRIKE]: [\"strike\", \"str\"]\n};\n\n/**\n * Pivot the DEFAULT_COLUMN_MAP for efficient reverse lookup\n * Maps normalized column names -> standardized baselode column names\n * @private\n */\nexport const _COLUMN_LOOKUP = {};\nfor (const [standardCol, variations] of Object.entries(DEFAULT_COLUMN_MAP)) {\n for (const variation of variations) {\n const normalized = variation.toLowerCase().trim();\n _COLUMN_LOOKUP[normalized] = standardCol;\n }\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { _COLUMN_LOOKUP, DEFAULT_COLUMN_MAP, HOLE_ID } from './datamodel.js';\n\n/**\n * Normalize a field name to lowercase with underscores\n * @param {string} name - The field name to normalize\n * @returns {string} - The normalized field name\n */\nexport function normalizeFieldName(name) {\n return (name || '').toString().trim().toLowerCase().replace(/\\s+/g, '_');\n}\n\n/**\n * Standardize column names in a row object using the baselode data model\n * @param {Object} row - The row object with arbitrary column names\n * @param {Object} [columnMap] - Optional column map to use (defaults to DEFAULT_COLUMN_MAP)\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Object} - The row object with standardized column names\n */\nexport function standardizeColumns(row, columnMap = null, sourceColumnMap = null) {\n const lookup = { ..._COLUMN_LOOKUP };\n \n // Add user-provided column mappings\n if (sourceColumnMap) {\n for (const [rawName, expectedName] of Object.entries(sourceColumnMap)) {\n if (rawName != null && expectedName != null) {\n const normalizedKey = normalizeFieldName(rawName);\n const normalizedValue = normalizeFieldName(expectedName);\n lookup[normalizedKey] = normalizedValue;\n }\n }\n }\n\n const renamed = {};\n for (const [col, value] of Object.entries(row)) {\n const key = normalizeFieldName(col);\n const mapped = lookup[key] || key;\n renamed[mapped] = value;\n }\n\n return renamed;\n}\n\n/**\n * Standardize an array of rows (e.g., from CSV parsing)\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map to use\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeRowArray(rows, columnMap = null, sourceColumnMap = null) {\n return rows.map(row => standardizeColumns(row, columnMap, sourceColumnMap));\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nexport const ASSAY_NON_VALUE_FIELDS = new Set([\n 'hole_id',\n 'holeid',\n 'id',\n 'holeId',\n 'project_code',\n 'project',\n 'latitude',\n 'longitude',\n 'lat',\n 'lng',\n 'elevation',\n 'dip',\n 'azimuth',\n 'holetype',\n 'shape',\n 'anumber',\n 'collarid',\n 'companyholeid',\n 'company_hole_id',\n 'samp_from',\n 'samp_to',\n 'sample_from',\n 'sample_to',\n 'from',\n 'to',\n 'depth_from',\n 'depth_to',\n 'fromdepth',\n 'todepth',\n 'comment',\n 'z'\n]);","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nconst DATA_LOG_PREFIX = '[baselode:data]';\n\n/**\n * Convert any value to an Error object\n * @param {*} error - Value to convert (Error, string, or other)\n * @param {string} fallbackMessage - Message to use if error is not Error or string\n * @returns {Error} Error object\n */\nexport function toError(error, fallbackMessage = 'Unknown error') {\n if (error instanceof Error) return error;\n const message = typeof error === 'string' && error.trim() ? error : fallbackMessage;\n return new Error(message);\n}\n\n/**\n * Wrap an error with contextual information about where it occurred\n * @param {string} context - Context description (e.g., function name)\n * @param {*} error - Original error\n * @param {string} fallbackMessage - Fallback message if error cannot be parsed\n * @returns {Error} Wrapped error with context and original as cause\n */\nexport function withDataErrorContext(context, error, fallbackMessage = 'Operation failed') {\n const baseError = toError(error, fallbackMessage);\n const wrapped = new Error(`${context}: ${baseError.message}`);\n wrapped.cause = baseError;\n return wrapped;\n}\n\n/**\n * Log a warning message with optional error object\n * @param {string} message - Warning message\n * @param {Error} [error] - Optional error object to log\n */\nexport function logDataWarning(message, error) {\n if (error !== undefined) {\n console.warn(`${DATA_LOG_PREFIX} ${message}`, error);\n return;\n }\n console.warn(`${DATA_LOG_PREFIX} ${message}`);\n}\n\n/**\n * Log an informational message\n * @param {string} message - Info message\n */\nexport function logDataInfo(message) {\n console.info(`${DATA_LOG_PREFIX} ${message}`);\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { ASSAY_NON_VALUE_FIELDS } from './assayFieldSets.js';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, FROM, TO, PROJECT_ID } from './datamodel.js';\n\n/**\n * Normalize a raw CSV row to use standardized column names\n * @private\n */\nconst normalizeRow = (rawRow, sourceColumnMap = null) => standardizeColumns(rawRow, null, sourceColumnMap);\n\n/**\n * Extract hole ID from a normalized row\n * @private\n * @param {Object} row - Normalized row object\n * @returns {{holeId: string}} Object containing hole ID\n */\nfunction extractIdFields(row) {\n const holeId = row[HOLE_ID];\n return { holeId };\n}\n\n/**\n * Extract and validate assay interval data from a row\n * @private\n * @param {Object} row - Normalized row object\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object|null} Interval object or null if invalid\n */\nfunction extractInterval(row, sourceColumnMap = null) {\n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n if (!holeId) return null;\n\n const project = row[PROJECT_ID] || row.project || row.project_code;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) return null;\n\n return {\n holeId,\n project,\n from,\n to,\n ...row\n };\n}\n\n/**\n * Convert array of intervals to a hole object with points\n * @private\n * @param {string} holeId - Hole identifier\n * @param {Array<Object>} intervals - Array of interval objects\n * @returns {{id: string, project: string, points: Array<Object>}} Hole object\n */\nfunction intervalsToHole(holeId, intervals) {\n const sorted = intervals.sort((a, b) => a.from - b.from);\n const points = [];\n sorted.forEach((iv) => {\n const { from, to, project, ...rest } = iv;\n const pointData = {\n z: from,\n from,\n to,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n ...rest\n };\n points.push(pointData);\n points.push({ ...pointData, z: to });\n });\n return { id: holeId, project: sorted[0]?.project, points };\n}\n\n/**\n * Parse assay CSV to extract unique hole IDs (quick pass, no interval data)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<string>>} Array of unique hole IDs\n */\nexport function parseAssayHoleIds(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const holeIds = new Set();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const hid = row[HOLE_ID];\n if (hid !== undefined && `${hid}`.trim() !== '') {\n holeIds.add(`${hid}`.trim());\n }\n },\n complete: () => resolve(Array.from(holeIds)),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIds', error))\n });\n });\n}\n\n/**\n * Check if a row has at least one non-null assay value\n * @private\n * @param {Object} row - Normalized row object\n * @returns {boolean} True if row contains assay data\n */\nfunction hasAssayValue(row) {\n return Object.entries(row || {}).some(([k, v]) => {\n if (ASSAY_NON_VALUE_FIELDS.has(k)) return false;\n if (v === undefined || v === null) return false;\n if (typeof v === 'string' && v.trim() === '') return false;\n return true;\n });\n}\n\n/**\n * Parse assay CSV to extract hole IDs that have at least one assay value (quick pass)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<{holeId: string}>>} Array of objects with hole IDs that have assay data\n */\nexport function parseAssayHoleIdsWithAssays(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const byHole = new Map();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n if (!hasAssayValue(row)) return;\n const ids = extractIdFields(row);\n const hid = ids.holeId;\n if (hid !== undefined && `${hid}`.trim() !== '') {\n const key = `${hid}`.trim();\n if (!byHole.has(key)) {\n byHole.set(key, {\n holeId: key\n });\n }\n }\n },\n complete: () => resolve(Array.from(byHole.values())),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIdsWithAssays', error))\n });\n });\n}\n\n/**\n * Parse assay CSV for a single hole's intervals\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to extract\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object|null>} Hole object with intervals or null if not found\n */\nexport function parseAssayHole(file, holeId, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const wanted = `${holeId}`.trim();\n if (!wanted) {\n reject(withDataErrorContext('parseAssayHole', new Error('Missing hole id')));\n return;\n }\n const intervals = [];\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (`${interval.holeId}`.trim() !== wanted) return;\n intervals.push(interval);\n },\n complete: () => {\n if (!intervals.length) {\n resolve(null);\n return;\n }\n const hole = intervalsToHole(wanted, intervals);\n resolve(hole);\n },\n error: (error) => reject(withDataErrorContext('parseAssayHole', error))\n });\n });\n}\n\n/**\n * Parse complete assay CSV file with all holes and intervals (eager load)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<{holes: Array<Object>}>} Object containing array of all holes with intervals\n */\nexport function parseAssaysCSV(file, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow) => {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (!byHole.has(interval.holeId)) byHole.set(interval.holeId, []);\n byHole.get(interval.holeId).push(interval);\n });\n\n const holes = Array.from(byHole.entries()).map(([hid, intervals]) => intervalsToHole(hid, intervals));\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseAssaysCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { normalizeFieldName, standardizeColumns } from './keying.js';\n\n/**\n * Normalize a CSV row by converting all keys to lowercase with underscores\n * @deprecated Use standardizeColumns from keying.js instead for full data model support\n */\nexport function normalizeCsvRow(row = {}) {\n const normalized = {};\n Object.entries(row || {}).forEach(([key, value]) => {\n if (!key) return;\n normalized[normalizeFieldName(key)] = value;\n });\n return normalized;\n}\n\n/**\n * Pick the first present value from a list of keys in a normalized row\n * @param {Object} normalized - The normalized row object\n * @param {Array<string>} keys - Array of keys to check\n * @param {*} fallback - Fallback value if none found\n * @returns {*} - The first present value or fallback\n */\nexport function pickFirstPresent(normalized = {}, keys = [], fallback) {\n for (const key of keys) {\n const value = normalized[key];\n if (value !== undefined && value !== null && `${value}`.trim() !== '') {\n return value;\n }\n }\n return fallback;\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/** Default number of trace plots in a grid */\nexport const DEFAULT_TRACE_PLOT_COUNT = 4;\n\n/**\n * Reorder hole IDs to place a focused hole first\n * @param {Array<string>} ids - Array of hole IDs\n * @param {string} focusId - Hole ID to place first\n * @returns {Array<string>} Reordered array with focusId first if found\n */\nexport function reorderHoleIds(ids = [], focusId = '') {\n if (!ids.length) return [];\n if (!focusId) return ids;\n const matchIdx = ids.findIndex((id) => id === focusId);\n if (matchIdx === -1) return ids;\n const selected = ids[matchIdx];\n const rest = ids.filter((_, idx) => idx !== matchIdx);\n return [selected, ...rest];\n}\n\n/**\n * Determine appropriate chart type for a property.\n * Comment columns always use 'comment'; categorical → 'categorical'; numeric → requested or default.\n *\n * @param {Object} options - Configuration options\n * @param {string} options.property - Property name\n * @param {string} options.chartType - Requested chart type\n * @param {Array<string>} options.categoricalProps - List of categorical property names\n * @param {Array<string>} options.commentProps - List of comment property names\n * @param {string} options.numericDefaultChartType - Default chart type for numeric properties\n * @returns {string} Coerced chart type ('comment', 'categorical', 'line', 'markers+line', etc.)\n */\nexport function coerceChartTypeForProperty({\n property = '',\n chartType = '',\n categoricalProps = [],\n commentProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n if (!property) return chartType || numericDefaultChartType;\n if (commentProps.includes(property)) return 'comment';\n if (categoricalProps.includes(property)) return 'categorical';\n if (property === 'dip') return 'tadpole';\n if (!chartType || chartType === 'categorical' || chartType === 'comment' || chartType === 'tadpole') return numericDefaultChartType;\n return chartType;\n}\n\n/**\n * Build trace plot configuration objects for a grid of hole IDs\n * @param {Object} options - Configuration options\n * @param {Array<string>} options.holeIds - Array of hole IDs\n * @param {string} options.focusedHoleId - Hole ID to focus (place first)\n * @param {number} options.plotCount - Number of plots in grid\n * @param {string} options.defaultProp - Default property to display\n * @param {Array<string>} options.categoricalProps - List of categorical properties\n * @param {Array<string>} options.commentProps - List of comment properties\n * @param {string} options.numericDefaultChartType - Default chart type for numeric props\n * @returns {Array<{holeId: string, property: string, chartType: string}>} Array of trace configurations\n */\nexport function buildTraceConfigsForHoleIds({\n holeIds = [],\n focusedHoleId = '',\n plotCount = DEFAULT_TRACE_PLOT_COUNT,\n defaultProp = '',\n categoricalProps = [],\n commentProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n const ordered = reorderHoleIds(holeIds, focusedHoleId);\n return Array.from({ length: plotCount }).map((_, idx) => {\n const holeId = ordered[idx] || holeIds[idx] || '';\n const chartType = coerceChartTypeForProperty({\n property: defaultProp,\n chartType: '',\n categoricalProps,\n commentProps,\n numericDefaultChartType\n });\n return {\n holeId,\n property: defaultProp,\n chartType\n };\n });\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Column display type classification for strip log visualization.\n *\n * Every loaded column is assigned a display type that drives:\n * - which columns appear in strip log property dropdowns\n * - which chart type options are offered for a given column\n */\n\n/** Numeric measurement column — rendered as bar, marker, or line */\nexport const DISPLAY_NUMERIC = 'numeric';\n\n/** Categorical string column — rendered as coloured interval bands */\nexport const DISPLAY_CATEGORICAL = 'categorical';\n\n/** Free-text comment column — rendered as labelled interval boxes */\nexport const DISPLAY_COMMENT = 'comment';\n\n/** Hidden column — not shown in strip log dropdowns */\nexport const DISPLAY_HIDDEN = 'hidden';\n\n/** Tadpole log — dip head + azimuth tail, shows both dip and azimuth */\nexport const DISPLAY_TADPOLE = 'tadpole';\n\n/**\n * Available chart type options for each display type.\n * Used to populate the chart-type dropdown in TracePlot.\n */\nexport const CHART_OPTIONS = {\n [DISPLAY_NUMERIC]: [\n { value: 'bar', label: 'Bars' },\n { value: 'markers', label: 'Markers' },\n { value: 'markers+line', label: 'Markers + Line' },\n { value: 'line', label: 'Line only' },\n ],\n [DISPLAY_CATEGORICAL]: [\n { value: 'categorical', label: 'Categorical bands' },\n ],\n [DISPLAY_COMMENT]: [\n { value: 'comment', label: 'Comments' },\n ],\n [DISPLAY_TADPOLE]: [\n { value: 'tadpole', label: 'Tadpole' },\n ],\n [DISPLAY_HIDDEN]: [],\n};\n\n/**\n * Column names (lowercased) that are always hidden from strip log views.\n * Covers hole IDs, project codes, coordinates, depth/interval fields, and geometry.\n */\nexport const HIDDEN_COLUMNS = new Set([\n // Hole identifiers\n 'hole_id', 'holeid', 'id', 'holetype',\n 'datasource_hole_id',\n 'anumber', 'collarid', 'companyholeid', 'company_hole_id', 'company_id',\n // Project codes\n 'project_id', 'project_code', 'project', 'projectcode', 'projectid',\n // Geographic coordinates\n 'latitude', 'longitude', 'lat', 'lon', 'lng',\n 'easting', 'northing', 'x', 'y', 'z',\n 'elevation', 'elev', 'rl',\n // Depth / interval columns\n 'from', 'to', 'mid', 'depth', 'md',\n 'samp_from', 'samp_to', 'sample_from', 'sample_to',\n 'depth_from', 'depth_to', 'fromdepth', 'todepth',\n // Geometry / CRS\n 'shape', 'geometry', 'crs', 'epsg',\n // Internal / synthetic columns\n 'data_source', '_hole_key', '_hole_id_key',\n]);\n\n/**\n * Column names (lowercased) that map to the comment display type.\n * These are free-text description columns rendered as labelled interval boxes.\n */\nexport const COMMENT_COLUMN_NAMES = new Set([\n 'comments', 'comment', 'notes', 'note',\n 'description', 'remarks', 'remark',\n 'log_description', 'struct_comment', 'structcomment',\n 'geology_description',\n]);\n\n/**\n * Classify columns in a dataset by their display type.\n *\n * Rules applied in order:\n * 1. Columns in HIDDEN_COLUMNS → DISPLAY_HIDDEN\n * 2. Columns in COMMENT_COLUMN_NAMES with ≥1 non-empty value → DISPLAY_COMMENT\n * 3. All-null/empty columns → DISPLAY_HIDDEN (silently dropped)\n * 4. Columns with at least one finite number → DISPLAY_NUMERIC\n * 5. Remaining non-empty columns → DISPLAY_CATEGORICAL\n *\n * @param {Array<Object>} rows - Flat array of row objects (assay or structural points)\n * @returns {{\n * byType: Object<string, string>,\n * numericCols: string[],\n * categoricalCols: string[],\n * commentCols: string[],\n * }}\n */\nexport function classifyColumns(rows) {\n if (!rows?.length) {\n return { byType: {}, numericCols: [], categoricalCols: [], commentCols: [] };\n }\n\n // Collect all column names across all rows\n const allCols = new Set(rows.flatMap((r) => Object.keys(r || {})));\n const byType = {};\n\n for (const col of allCols) {\n const normalized = col.toLowerCase().trim();\n\n // Always hidden: ID / coordinate / depth columns\n if (HIDDEN_COLUMNS.has(normalized) || HIDDEN_COLUMNS.has(col)) {\n byType[col] = DISPLAY_HIDDEN;\n continue;\n }\n\n // Comment-type: named text-description columns\n if (COMMENT_COLUMN_NAMES.has(normalized)) {\n const hasValue = rows.some((r) => {\n const v = r[col];\n return v != null && String(v).trim() !== '' && String(v) !== 'null';\n });\n byType[col] = hasValue ? DISPLAY_COMMENT : DISPLAY_HIDDEN;\n continue;\n }\n\n // Classify by content: all-empty → hidden; numeric → numeric; else → categorical\n let hasNumeric = false;\n let hasValue = false;\n for (const r of rows) {\n const v = r[col];\n if (v == null || (typeof v === 'string' && v.trim() === '')) continue;\n hasValue = true;\n if (typeof v === 'number' && Number.isFinite(v)) {\n hasNumeric = true;\n break;\n }\n }\n\n if (!hasValue) {\n byType[col] = DISPLAY_HIDDEN;\n } else if (hasNumeric) {\n byType[col] = DISPLAY_NUMERIC;\n } else {\n byType[col] = DISPLAY_CATEGORICAL;\n }\n }\n\n return {\n byType,\n numericCols: Object.entries(byType).filter(([, t]) => t === DISPLAY_NUMERIC).map(([k]) => k),\n categoricalCols: Object.entries(byType).filter(([, t]) => t === DISPLAY_CATEGORICAL).map(([k]) => k),\n commentCols: Object.entries(byType).filter(([, t]) => t === DISPLAY_COMMENT).map(([k]) => k),\n };\n}\n\n/**\n * Get the available chart type options for a given display type.\n * Returns NUMERIC options as a fallback for unknown types.\n *\n * @param {string} displayType - One of the DISPLAY_* constants\n * @returns {Array<{value: string, label: string}>}\n */\nexport function getChartOptions(displayType) {\n return CHART_OPTIONS[displayType] ?? CHART_OPTIONS[DISPLAY_NUMERIC];\n}\n\n/**\n * Get the default chart type value for a display type.\n *\n * @param {string} displayType\n * @returns {string}\n */\nexport function defaultChartType(displayType) {\n const opts = getChartOptions(displayType);\n if (!opts.length) return 'markers+line';\n if (displayType === DISPLAY_NUMERIC) return 'line';\n return opts[0].value;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { parseAssayHole, parseAssayHoleIdsWithAssays, parseAssaysCSV } from './assayLoader.js';\nimport { buildTraceConfigsForHoleIds, reorderHoleIds } from './traceGridConfig.js';\nimport { classifyColumns } from './columnMeta.js';\n\nexport { reorderHoleIds };\n\n/**\n * Derive property names from hole data using column display-type classification.\n *\n * @param {Array<Object>} holes - Array of hole objects with points containing assay data\n * @returns {{\n * numericProps: string[],\n * categoricalProps: string[],\n * commentProps: string[],\n * columnMeta: Object,\n * defaultProp: string\n * }} Property classification\n */\nexport function deriveAssayProps(holes = []) {\n const points = holes.flatMap((h) => h.points || []);\n const { numericCols, categoricalCols, commentCols, byType } = classifyColumns(points);\n\n const defaultProp = numericCols[0] || categoricalCols[0] || '';\n\n return {\n numericProps: numericCols,\n categoricalProps: categoricalCols,\n commentProps: commentCols,\n columnMeta: byType,\n defaultProp,\n };\n}\n\n/**\n * Load metadata (hole IDs) from an assay CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Array<{holeId: string}>>} Array of hole IDs with assay data\n */\nexport async function loadAssayMetadata(file, config = null) {\n const holeIds = await parseAssayHoleIdsWithAssays(file);\n return holeIds;\n}\n\n/**\n * Load assay intervals for a specific hole from CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to load\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Object|null>} Hole object with assay intervals or null if not found\n */\nexport async function loadAssayHole(file, holeId, config = null) {\n const hole = await parseAssayHole(file, holeId);\n return hole;\n}\n\n/**\n * Build complete assay state from hole data including property analysis and trace configs\n * @param {Array<Object>} holes - Array of hole objects with assay data\n * @param {string} focusedHoleId - Hole ID to focus on (will be first in trace configs)\n * @returns {Object|null} Assay state object with holes, properties, and trace configs\n */\nexport function buildAssayState(holes = [], focusedHoleId = '') {\n if (!holes.length) return null;\n const { numericProps, categoricalProps, commentProps, columnMeta, defaultProp } = deriveAssayProps(holes);\n const holeIds = holes.map((h) => h.id || h.holeId).filter(Boolean);\n const traceConfigs = buildTraceConfigsForHoleIds({\n holeIds,\n focusedHoleId,\n plotCount: 4,\n defaultProp,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'line'\n });\n return {\n holes,\n numericProps,\n categoricalProps,\n commentProps,\n columnMeta,\n defaultProp,\n traceConfigs\n };\n}\n\n/**\n * Load and parse complete assay CSV file into state object\n * @param {File|Blob} file - Assay CSV file\n * @param {string} focusedHoleId - Hole ID to focus on\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object>} Complete assay state\n * @throws {Error} If no valid assay intervals found\n */\nexport async function loadAssayFile(file, focusedHoleId = '', sourceColumnMap = null) {\n const { holes } = await parseAssaysCSV(file, sourceColumnMap);\n const state = buildAssayState(holes, focusedHoleId);\n if (!state) throw new Error('No valid assay intervals found.');\n return state;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, LATITUDE, LONGITUDE, AZIMUTH, DIP, DEPTH, PROJECT_ID } from './datamodel.js';\n\n/**\n * Parse survey CSV file containing downhole survey measurements\n * Expected columns: hole_id, depth, azimuth, dip\n * @param {File|Blob} file - Survey CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<Object>>} Array of normalized survey rows\n */\nexport function parseSurveyCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = results.data\n .map((row) => normalizeRow(row, sourceColumnMap))\n .filter((row) => row[HOLE_ID] && Number.isFinite(row[DEPTH]) && Number.isFinite(row[DIP]) && Number.isFinite(row[AZIMUTH]));\n resolve(rows);\n },\n error: (err) => reject(withDataErrorContext('parseSurveyCSV', err))\n });\n });\n}\n\n/**\n * Normalize a survey row to standardized field names\n * @private\n * @param {Object} row - Raw survey row\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object} Normalized row with standardized field names\n */\nfunction normalizeRow(row, sourceColumnMap = null) {\n const norm = standardizeColumns(row, null, sourceColumnMap);\n\n const holeId = norm[HOLE_ID];\n const project = norm[PROJECT_ID] || norm.project || norm.project_code;\n const lat = toNumber(norm[LATITUDE]);\n const lng = toNumber(norm[LONGITUDE]);\n const surveyDepth = toNumber(norm[DEPTH]);\n const dip = toNumber(norm[DIP]);\n const azimuth = toNumber(norm[AZIMUTH]);\n const maxDepth = toNumber(norm.maxdepth);\n\n return {\n raw: norm,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n [LATITUDE]: lat,\n [LONGITUDE]: lng,\n [DEPTH]: surveyDepth,\n [DIP]: dip,\n [AZIMUTH]: azimuth,\n maxdepth: maxDepth,\n // Legacy field names for backwards compatibility\n project_code: project,\n latitude: lat,\n longitude: lng,\n surveydepth: surveyDepth\n };\n}\n\n/**\n * Convert value to number, returning undefined if not finite\n * @private\n * @param {*} v - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nconst toNumber = (v) => {\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n};\n\n/**\n * Desurvey drillhole traces using minimum curvature method\n * Converts survey measurements (azimuth, dip, depth) to 3D coordinates (x, y, z)\n * @param {Array<Object>} collars - Array of collar objects with location data\n * @param {Array<Object>} surveys - Array of survey measurement objects\n * @returns {Array<{id: string, project: string, points: Array<{x: number, y: number, z: number, md: number, azimuth: number, dip: number, lat: number, lng: number}>, collar: Object}>} Array of desurveyed holes with 3D points\n */\nexport function desurveyTraces(collars, surveys) {\n const collarByKey = new Map();\n collars.forEach((c) => {\n const holeId = (c[HOLE_ID] || c.holeId || c.id || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!collarByKey.has(key)) {\n collarByKey.set(key, c);\n }\n });\n\n const refLat = collars[0]?.lat ?? collars[0]?.[LATITUDE] ?? 0;\n const refLng = collars[0]?.lng ?? collars[0]?.[LONGITUDE] ?? 0;\n const refMetersPerDegLat = 111132;\n const refMetersPerDegLon = 111320 * Math.cos((refLat * Math.PI) / 180);\n\n const grouped = new Map();\n surveys.forEach((s) => {\n const holeId = (s[HOLE_ID] || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(s);\n });\n\n const holes = [];\n grouped.forEach((stations, key) => {\n const collar = collarByKey.get(key);\n if (!collar) return;\n const sorted = stations\n .filter((s) => Number.isFinite(s[DEPTH] ?? s.surveydepth))\n .sort((a, b) => (a[DEPTH] ?? a.surveydepth) - (b[DEPTH] ?? b.surveydepth));\n if (!sorted.length) return;\n\n const lat0 = collar.lat ?? collar[LATITUDE];\n const lng0 = collar.lng ?? collar[LONGITUDE];\n const metersPerDegLat = 111132;\n const metersPerDegLon = 111320 * Math.cos((lat0 * Math.PI) / 180);\n const baseX = (lng0 - refLng) * refMetersPerDegLon;\n const baseY = (lat0 - refLat) * refMetersPerDegLat;\n\n const points = [];\n let accX = 0;\n let accY = 0;\n let accZ = 0;\n\n for (let i = 0; i < sorted.length; i += 1) {\n const curr = sorted[i];\n const prev = sorted[i - 1];\n const currDepth = curr[DEPTH] ?? curr.surveydepth;\n const currAzimuth = curr[AZIMUTH] ?? curr.azimuth;\n const currDip = curr[DIP] ?? curr.dip;\n \n if (!prev) {\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: 0,\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n continue;\n }\n\n const prevDepth = prev[DEPTH] ?? prev.surveydepth;\n const prevAzimuth = prev[AZIMUTH] ?? prev.azimuth;\n const prevDip = prev[DIP] ?? prev.dip;\n \n const deltaMD = currDepth - prevDepth;\n if (deltaMD <= 0) continue;\n\n const inc1 = toInclination(prevDip);\n const inc2 = toInclination(currDip);\n const az1 = degToRad(prevAzimuth);\n const az2 = degToRad(currAzimuth);\n\n const beta = Math.acos(\n Math.sin(inc1) * Math.sin(inc2) * Math.cos(az1 - az2) +\n Math.cos(inc1) * Math.cos(inc2)\n );\n\n const rf = beta > 1e-6 ? (2 / beta) * Math.tan(beta / 2) : 1;\n\n const dx = 0.5 * deltaMD * (Math.sin(inc1) * Math.cos(az1) + Math.sin(inc2) * Math.cos(az2)) * rf;\n const dy = 0.5 * deltaMD * (Math.sin(inc1) * Math.sin(az1) + Math.sin(inc2) * Math.sin(az2)) * rf;\n const dzDown = 0.5 * deltaMD * (Math.cos(inc1) + Math.cos(inc2)) * rf; // down is positive\n\n accX += dx;\n accY += dy;\n accZ += dzDown;\n\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: -accZ, // render with z up; depth down\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n }\n\n // convert local meters back to approx lat/lon for map overlay if needed\n const pointsWithGeo = points.map((p) => ({\n ...p,\n lat: lat0 + (p.y / metersPerDegLat),\n lng: lng0 + (p.x / metersPerDegLon)\n }));\n\n holes.push({\n id: collar[HOLE_ID] || collar.holeId || key,\n project: collar[PROJECT_ID] || collar.project_id || collar.project || '',\n points: pointsWithGeo,\n collar\n });\n });\n\n return holes;\n}\n\n/**\n * Convert degrees to radians\n * @private\n * @param {number} d - Degrees\n * @returns {number} Radians\n */\nconst degToRad = (d) => (d * Math.PI) / 180;\n\n/**\n * Convert dip (negative downward from horizontal) to inclination from vertical\n * @private\n * @param {number} dipDeg - Dip in degrees (negative = downward)\n * @returns {number} Inclination in radians from vertical (0 = vertical down)\n */\nconst toInclination = (dipDeg) => {\n const val = Number(dipDeg);\n const incDeg = 90 + (Number.isFinite(val) ? val : 0); // dip -60 => inc 30\n const clamped = Math.min(180, Math.max(0, incDeg));\n return degToRad(clamped);\n};\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { withDataErrorContext } from './dataErrorUtils.js';\n/**\n * Convert value to number with optional fallback\n * @private\n */function toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Normalize hole ID value to trimmed string\n * @private\n */\nfunction normalizeHoleIdValue(value) {\n if (value === undefined || value === null) return '';\n return `${value}`.trim();\n}\n\n/**\n * Canonicalize hole ID column across rows with varying column names\n * @private\n */\nfunction canonicalizeHoleIdRows(rows = [], holeIdCol = null) {\n const preferred = holeIdCol || 'hole_id';\n const candidates = [preferred, 'hole_id', 'holeId', 'id'];\n const resolved = candidates.find((col) => rows.some((row) => normalizeHoleIdValue(row?.[col])));\n if (!resolved) {\n throw withDataErrorContext('canonicalizeHoleIdRows', new Error(`hole id column '${preferred}' not found`));\n }\n return {\n aliasCol: resolved,\n rows: rows.map((row) => ({\n ...row,\n hole_id: normalizeHoleIdValue(row?.[resolved])\n }))\n };\n}\n\n/**\n * Convert degrees to radians\n * @private\n */\nfunction degToRad(angle) {\n return (Number(angle) * Math.PI) / 180;\n}\n\n/**\n * Calculate direction cosines from azimuth and dip\n * @private\n */\nfunction directionCosines(azimuth, dip) {\n const azRad = degToRad(azimuth);\n const dipRad = degToRad(dip);\n const ca = Math.cos(dipRad) * Math.sin(azRad);\n const cb = Math.cos(dipRad) * Math.cos(azRad);\n const cc = Math.sin(dipRad) * -1;\n return { ca, cb, cc };\n}\n\n/**\n * Calculate displacement vector for a survey segment using specified method\n * @private\n */\nfunction segmentDisplacement(deltaMd, az0, dip0, az1, dip1, method = 'minimum_curvature') {\n const dc0 = directionCosines(az0, dip0);\n const dc1 = directionCosines(az1, dip1);\n\n if (method === 'tangential') {\n return {\n dx: deltaMd * dc0.ca,\n dy: deltaMd * dc0.cb,\n dz: deltaMd * dc0.cc,\n azimuth: az0,\n dip: dip0\n };\n }\n\n if (method === 'balanced_tangential') {\n const azAvg = 0.5 * (az0 + az1);\n const dipAvg = 0.5 * (dip0 + dip1);\n const dcAvg = directionCosines(azAvg, dipAvg);\n return {\n dx: deltaMd * dcAvg.ca,\n dy: deltaMd * dcAvg.cb,\n dz: deltaMd * dcAvg.cc,\n azimuth: azAvg,\n dip: dipAvg\n };\n }\n\n const dot = (dc0.ca * dc1.ca) + (dc0.cb * dc1.cb) + (dc0.cc * dc1.cc);\n const dogleg = Math.acos(Math.max(-1, Math.min(1, dot)));\n const rf = dogleg > 1e-6 ? (2 * Math.tan(dogleg / 2)) / dogleg : 1;\n\n return {\n dx: 0.5 * deltaMd * (dc0.ca + dc1.ca) * rf,\n dy: 0.5 * deltaMd * (dc0.cb + dc1.cb) * rf,\n dz: 0.5 * deltaMd * (dc0.cc + dc1.cc) * rf,\n azimuth: az1,\n dip: dip1\n };\n}\n\nfunction desurvey(rowsCollars = [], rowsSurveys = [], options = {}) {\n const {\n step = 1,\n holeIdCol = null,\n method = 'minimum_curvature'\n } = options;\n\n const safeStep = Number.isFinite(Number(step)) && Number(step) > 0 ? Number(step) : 1;\n\n const collarsCanonical = canonicalizeHoleIdRows(rowsCollars, holeIdCol);\n const surveysCanonical = canonicalizeHoleIdRows(rowsSurveys, holeIdCol || collarsCanonical.aliasCol);\n\n if (!collarsCanonical.rows.length || !surveysCanonical.rows.length) return [];\n\n const collarsByHole = new Map();\n collarsCanonical.rows.forEach((row) => {\n if (!row.hole_id || collarsByHole.has(row.hole_id)) return;\n collarsByHole.set(row.hole_id, row);\n });\n\n const surveysByHole = new Map();\n surveysCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!surveysByHole.has(row.hole_id)) surveysByHole.set(row.hole_id, []);\n surveysByHole.get(row.hole_id).push(row);\n });\n\n const out = [];\n surveysByHole.forEach((stations, holeId) => {\n const collar = collarsByHole.get(holeId);\n if (!collar) return;\n\n const sorted = [...stations]\n .map((row) => ({\n ...row,\n from: toNumber(row.from),\n azimuth: toNumber(row.azimuth),\n dip: toNumber(row.dip)\n }))\n .filter((row) => Number.isFinite(row.from) && Number.isFinite(row.azimuth) && Number.isFinite(row.dip))\n .sort((a, b) => a.from - b.from);\n\n if (!sorted.length) return;\n\n let x = toNumber(collar.x, 0);\n let y = toNumber(collar.y, 0);\n let z = toNumber(collar.z, 0);\n let mdCursor = sorted[0].from;\n const azPrev = sorted[0].azimuth;\n const dipPrev = sorted[0].dip;\n\n const firstRecord = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: azPrev,\n dip: dipPrev\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n firstRecord[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(firstRecord);\n\n for (let idx = 0; idx < sorted.length - 1; idx += 1) {\n const s0 = sorted[idx];\n const s1 = sorted[idx + 1];\n const md0 = s0.from;\n const md1 = s1.from;\n const deltaMd = md1 - md0;\n if (deltaMd <= 0) continue;\n\n const segmentSteps = Math.max(1, Math.ceil(deltaMd / safeStep));\n const mdIncrement = deltaMd / segmentSteps;\n\n for (let stepIdx = 0; stepIdx < segmentSteps; stepIdx += 1) {\n mdCursor += mdIncrement;\n const weight = (mdCursor - md0) / deltaMd;\n const azInterp = s0.azimuth + weight * (s1.azimuth - s0.azimuth);\n const dipInterp = s0.dip + weight * (s1.dip - s0.dip);\n\n const disp = segmentDisplacement(mdIncrement, s0.azimuth, s0.dip, s1.azimuth, s1.dip, method);\n x += disp.dx;\n y += disp.dy;\n z += disp.dz;\n\n const record = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: method === 'minimum_curvature' ? azInterp : disp.azimuth,\n dip: method === 'minimum_curvature' ? dipInterp : disp.dip\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n record[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(record);\n }\n }\n });\n\n return out;\n}\n\n/**\n * Desurvey drillholes using minimum curvature method\n * @param {Array<Object>} collars - Collar data with hole_id, lat, lng\n * @param {Array<Object>} surveys - Survey data with hole_id, depth, azimuth, dip\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points with x, y, z, md coordinates\n */\nexport function minimumCurvatureDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'minimum_curvature' });\n}\n\n/**\n * Desurvey drillholes using tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function tangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'tangential' });\n}\n\n/**\n * Desurvey drillholes using balanced tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function balancedTangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'balanced_tangential' });\n}\n\n/**\n * Build desurveyed traces using minimum curvature method (alias for minimumCurvatureDesurvey)\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function buildTraces(collars, surveys, options = {}) {\n return minimumCurvatureDesurvey(collars, surveys, options);\n}\n\n/**\n * Find nearest trace point by measured depth\n * @private\n */\nfunction nearestByMeasuredDepth(traceRows, midMd) {\n if (!traceRows.length || !Number.isFinite(midMd)) return null;\n let best = null;\n let bestDist = Infinity;\n for (let i = 0; i < traceRows.length; i += 1) {\n const row = traceRows[i];\n const md = toNumber(row.md);\n if (!Number.isFinite(md)) continue;\n const dist = Math.abs(md - midMd);\n if (dist < bestDist) {\n bestDist = dist;\n best = row;\n }\n }\n return best;\n}\n\n/**\n * Attach 3D position data from desurveyed traces to assay intervals\n * Finds nearest trace point by mid-depth of each assay interval\n * @param {Array<Object>} assays - Assay data with hole_id, from, to\n * @param {Array<Object>} traces - Desurveyed trace data with hole_id, md, x, y, z\n * @param {Object} options - Options\n * @param {string} options.holeIdCol - Hole ID column name (default: 'hole_id')\n * @returns {Array<Object>} Assay rows enriched with x, y, z, md, azimuth, dip from nearest trace point\n */\nexport function attachAssayPositions(assays = [], traces = [], options = {}) {\n const holeIdCol = options.holeIdCol || 'hole_id';\n const assaysCanonical = canonicalizeHoleIdRows(assays, holeIdCol);\n const tracesCanonical = canonicalizeHoleIdRows(traces, holeIdCol);\n\n if (!assaysCanonical.rows.length || !tracesCanonical.rows.length) return [...assaysCanonical.rows];\n\n const tracesByHole = new Map();\n tracesCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!tracesByHole.has(row.hole_id)) tracesByHole.set(row.hole_id, []);\n tracesByHole.get(row.hole_id).push(row);\n });\n tracesByHole.forEach((rows, holeId) => {\n tracesByHole.set(holeId, [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0)));\n });\n\n return assaysCanonical.rows.map((assay) => {\n const from = toNumber(assay.from);\n const to = toNumber(assay.to);\n const midMd = Number.isFinite(from) && Number.isFinite(to) ? 0.5 * (from + to) : undefined;\n if (!assay.hole_id || !Number.isFinite(midMd)) return { ...assay };\n\n const nearest = nearestByMeasuredDepth(tracesByHole.get(assay.hole_id) || [], midMd);\n if (!nearest) return { ...assay };\n\n const merged = { ...assay };\n ['md', 'x', 'y', 'z', 'azimuth', 'dip'].forEach((key) => {\n if (nearest[key] === undefined) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = nearest[key];\n } else {\n merged[key] = nearest[key];\n }\n });\n return merged;\n });\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, EASTING, NORTHING, ELEVATION } from './datamodel.js';\n\n/**\n * Parse drillholes CSV with desurveyed trace points\n * Expect CSV columns: hole_id, x (easting), y (northing), z (elevation), order (optional) plus any attributes per point\n * @param {File|Blob|string} file - CSV file or data\n * @param {Object} [sourceColumnMap] - Optional user-provided column mappings\n * @returns {Promise<{holes: Array}>} - Parsed drillhole data\n */\nexport function parseDrillholesCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow, idx) => {\n const row = standardizeColumns(rawRow, null, sourceColumnMap);\n \n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n const x = row[EASTING] ?? row.x;\n const y = row[NORTHING] ?? row.y;\n const z = row[ELEVATION] ?? row.z;\n const order = row.order ?? idx;\n\n if (!holeId || x === null || x === undefined || y === null || y === undefined || z === null || z === undefined) return;\n\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push({\n ...row,\n holeId,\n order,\n x: Number(x) ?? 0,\n y: Number(y) ?? 0,\n z: Number(z) ?? 0\n });\n });\n\n const holes = Array.from(byHole.entries()).map(([holeId, pts]) => ({\n id: holeId,\n points: pts\n .sort((a, b) => a.order - b.order)\n .map((p) => ({\n ...p,\n x: Number(p.x) || 0,\n y: Number(p.y) || 0,\n z: Number(p.z) || 0\n }))\n }));\n\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseDrillholesCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { standardizeColumns as standardizeRow } from './keying.js';\nimport {\n HOLE_ID,\n LATITUDE,\n LONGITUDE,\n ELEVATION,\n AZIMUTH,\n DIP,\n FROM,\n TO,\n MID,\n PROJECT_ID,\n EASTING,\n NORTHING,\n CRS,\n DEPTH,\n GEOLOGY_CODE,\n GEOLOGY_DESCRIPTION,\n BASELODE_DATA_MODEL_DRILL_COLLAR,\n BASELODE_DATA_MODEL_DRILL_SURVEY,\n BASELODE_DATA_MODEL_DRILL_ASSAY,\n BASELODE_DATA_MODEL_DRILL_GEOLOGY\n} from './datamodel.js';\n\n// Re-export for backwards compatibility\nexport { DEFAULT_COLUMN_MAP } from './datamodel.js';\n\n/**\n * Convert source to array\n * @private\n */\nfunction toArray(source) {\n if (!source) return [];\n if (Array.isArray(source)) return [...source];\n return [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Sort rows by multiple columns\n * @private\n */\nfunction sortByColumns(rows = [], columns = []) {\n const out = [...rows];\n out.sort((a, b) => {\n for (let i = 0; i < columns.length; i += 1) {\n const col = columns[i];\n const av = a?.[col];\n const bv = b?.[col];\n if (av === bv) continue;\n if (av === undefined || av === null) return 1;\n if (bv === undefined || bv === null) return -1;\n if (typeof av === 'number' && typeof bv === 'number') return av - bv;\n return `${av}`.localeCompare(`${bv}`);\n }\n return 0;\n });\n return out;\n}\n\nfunction validateNoOverlappingIntervals(rows = [], label = 'Intervals') {\n if (!rows.length) return;\n const ordered = sortByColumns(rows, [HOLE_ID, FROM, TO]);\n const prevToByHole = new Map();\n\n ordered.forEach((row) => {\n const holeId = `${row?.[HOLE_ID] ?? ''}`.trim();\n const fromValue = Number(row?.[FROM]);\n const toValue = Number(row?.[TO]);\n if (!holeId || !Number.isFinite(fromValue) || !Number.isFinite(toValue)) return;\n\n const prevTo = prevToByHole.get(holeId);\n if (Number.isFinite(prevTo) && fromValue < prevTo) {\n throw withDataErrorContext(\n 'validateNoOverlappingIntervals',\n new Error(`${label} intervals overlap for hole '${holeId}': from=${fromValue} is less than previous to=${prevTo}`)\n );\n }\n prevToByHole.set(holeId, toValue);\n });\n}\n\n/**\n * Parse CSV data using PapaParse\n * @private\n */\nfunction parseCsv(source, papaParseConfig = {}) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n ...papaParseConfig,\n complete: (results) => resolve(Array.isArray(results?.data) ? results.data : []),\n error: (error) => reject(withDataErrorContext('loadTable(csv)', error))\n });\n });\n}\n\n/**\n * Standardize column names in an array of rows using the baselode data model\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map (unused, for backwards compatibility)\n * @param {Object} [sourceColumnMap] - Optional source column map for user-provided mappings\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeColumns(rows = [], columnMap = null, sourceColumnMap = null) {\n return rows.map((row) => standardizeRow(row, columnMap, sourceColumnMap));\n}\n\nexport async function loadTable(source, options = {}) {\n const {\n kind = 'csv',\n columnMap = null,\n sourceColumnMap = null,\n papaParseConfig = {}\n } = options;\n\n let rows;\n if (Array.isArray(source)) {\n rows = toArray(source);\n } else if (kind === 'csv') {\n rows = await parseCsv(source, papaParseConfig);\n } else if (kind === 'parquet' || kind === 'sql') {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind in JS runtime: ${kind}`));\n } else {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind: ${kind}`));\n }\n\n return standardizeColumns(rows, columnMap, sourceColumnMap);\n}\n\n/**\n * Load and validate drillhole collar data\n * Requires hole_id and coordinates (either lat/lng or easting/northing)\n * @param {File|Blob|Array<Object>|string} source - Collar data source\n * @param {Object} options - Loading options\n * @param {string} options.crs - Coordinate reference system (optional)\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated collar rows\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadCollars(source, options = {}) {\n const {\n crs = null,\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Check for hole_id\n const hasHoleId = standardized.some(row => HOLE_ID in row);\n if (!hasHoleId) {\n throw withDataErrorContext('loadCollars', new Error(`Collar table missing column: ${HOLE_ID}`));\n }\n\n // Check for required coordinate columns\n const hasXY = standardized.some(row => EASTING in row && NORTHING in row);\n const hasLatLon = standardized.some(row => LATITUDE in row && LONGITUDE in row);\n\n if (!hasXY && !hasLatLon) {\n throw withDataErrorContext('loadCollars', new Error('Collar table missing coordinate columns (need easting/northing or latitude/longitude)'));\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert coordinates to numbers\n if (LATITUDE in result) result[LATITUDE] = toNumber(result[LATITUDE]);\n if (LONGITUDE in result) result[LONGITUDE] = toNumber(result[LONGITUDE]);\n if (ELEVATION in result) result[ELEVATION] = toNumber(result[ELEVATION]);\n if (EASTING in result) result[EASTING] = toNumber(result[EASTING]);\n if (NORTHING in result) result[NORTHING] = toNumber(result[NORTHING]);\n\n // If datasource_hole_id is missing, copy from hole_id\n if (!('datasource_hole_id' in result) && HOLE_ID in result) {\n result.datasource_hole_id = result[HOLE_ID];\n }\n\n return result;\n });\n\n // Validate required fields\n const required = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (hasLatLon && (!Number.isFinite(row[LATITUDE]) || !Number.isFinite(row[LONGITUDE]))) {\n return false;\n }\n if (hasXY && !hasLatLon && (!Number.isFinite(row[EASTING]) || !Number.isFinite(row[NORTHING]))) {\n return false;\n }\n return true;\n });\n\n if (!required) {\n throw withDataErrorContext('loadCollars', new Error('Collar table has missing required values'));\n }\n\n return normalized;\n}\n\n/**\n * Load and validate drillhole survey data\n * Requires hole_id, depth, azimuth, and dip columns\n * @param {File|Blob|Array<Object>|string} source - Survey data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated survey rows, sorted by hole_id and depth\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadSurveys(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, DEPTH, AZIMUTH, DIP];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadSurveys', new Error(`Survey table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (DEPTH in result) result[DEPTH] = toNumber(result[DEPTH]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n if (AZIMUTH in result) result[AZIMUTH] = toNumber(result[AZIMUTH]);\n if (DIP in result) result[DIP] = toNumber(result[DIP]);\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[DEPTH])) return false;\n if (!Number.isFinite(row[AZIMUTH])) return false;\n if (!Number.isFinite(row[DIP])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadSurveys', new Error('Survey table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, DEPTH]);\n}\n\n/**\n * Load and validate assay/interval data\n * Requires hole_id, from, and to columns. Calculates mid depth automatically.\n * @param {File|Blob|Array<Object>|string} source - Assay data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated assay rows, sorted by hole_id, from, to\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadAssays(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, FROM, TO];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadAssays', new Error(`Assay table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (FROM in result) result[FROM] = toNumber(result[FROM]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n \n // Calculate midpoint\n if (FROM in result && TO in result && Number.isFinite(result[FROM]) && Number.isFinite(result[TO])) {\n result[MID] = 0.5 * (result[FROM] + result[TO]);\n }\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[FROM])) return false;\n if (!Number.isFinite(row[TO])) return false;\n if (!(row[TO] > row[FROM])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadAssays', new Error('Assay table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, FROM, TO]);\n}\n\n/**\n * Load and validate geology interval data for categorical strip-log plotting.\n * Requires hole_id, from, to and at least one geology categorical field.\n * @param {File|Blob|Array<Object>|string} source - Geology data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @returns {Promise<Array<Object>>} Array of validated geology rows, sorted by hole_id, from, to\n */\nexport async function loadGeology(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n\n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n const required = [HOLE_ID, FROM, TO];\n for (const col of required) {\n const hasColumn = standardized.some((row) => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadGeology', new Error(`Geology table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n\n if (FROM in result) result[FROM] = toNumber(result[FROM]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n\n if (FROM in result && TO in result && Number.isFinite(result[FROM]) && Number.isFinite(result[TO])) {\n // Expand equal bounds to a 1mm interval (mirrors Python loader behaviour)\n if (result[TO] === result[FROM]) {\n result[FROM] = Math.round(result[FROM] * 1000) / 1000;\n result[TO] = result[FROM] + 0.001;\n }\n result[MID] = 0.5 * (result[FROM] + result[TO]);\n }\n\n const hasCode = result[GEOLOGY_CODE] !== undefined && result[GEOLOGY_CODE] !== null && `${result[GEOLOGY_CODE]}`.trim() !== '';\n const hasDescription = result[GEOLOGY_DESCRIPTION] !== undefined && result[GEOLOGY_DESCRIPTION] !== null && `${result[GEOLOGY_DESCRIPTION]}`.trim() !== '';\n if (!hasCode && hasDescription) {\n result[GEOLOGY_CODE] = result[GEOLOGY_DESCRIPTION];\n }\n if (hasCode && !hasDescription) {\n result[GEOLOGY_DESCRIPTION] = result[GEOLOGY_CODE];\n }\n\n return result;\n });\n\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[FROM])) return false;\n if (!Number.isFinite(row[TO])) return false;\n if (!(row[TO] > row[FROM])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadGeology', new Error('Geology table has missing or invalid interval values'));\n }\n\n const hasCategory = normalized.some((row) => {\n const code = row[GEOLOGY_CODE];\n const description = row[GEOLOGY_DESCRIPTION];\n return (code !== undefined && code !== null && `${code}`.trim() !== '') ||\n (description !== undefined && description !== null && `${description}`.trim() !== '');\n });\n\n if (!hasCategory) {\n throw withDataErrorContext('loadGeology', new Error(`Geology table missing categorical columns: ${GEOLOGY_CODE} or ${GEOLOGY_DESCRIPTION}`));\n }\n\n validateNoOverlappingIntervals(normalized, 'Geology');\n\n if (!keepAll) {\n const keep = new Set(Object.keys(BASELODE_DATA_MODEL_DRILL_GEOLOGY));\n return sortByColumns(\n normalized.map((row) => Object.fromEntries(Object.entries(row).filter(([k]) => keep.has(k)))),\n [HOLE_ID, FROM, TO]\n );\n }\n\n return sortByColumns(normalized, [HOLE_ID, FROM, TO]);\n}\n\n/**\n * Join assay data to desurveyed trace data by matching key columns\n * Adds trace fields to assay rows, suffixing with '_trace' if field already exists\n * @param {Array<Object>} assays - Array of assay rows\n * @param {Array<Object>} traces - Array of trace/desurvey rows\n * @param {Object} options - Join options\n * @param {Array<string>} options.onCols - Columns to join on (default: [hole_id])\n * @returns {Array<Object>} Assay rows enriched with trace data\n */\nexport function joinAssaysToTraces(assays = [], traces = [], options = {}) {\n const onCols = Array.isArray(options.onCols) && options.onCols.length ? options.onCols : [HOLE_ID];\n if (!traces.length) return [...assays];\n\n const keyOf = (row) => onCols.map((col) => `${row?.[col] ?? ''}`).join('|');\n const tracesByKey = new Map();\n traces.forEach((trace) => {\n tracesByKey.set(keyOf(trace), trace);\n });\n\n return assays.map((assay) => {\n const trace = tracesByKey.get(keyOf(assay));\n if (!trace) return { ...assay };\n const merged = { ...assay };\n Object.entries(trace).forEach(([key, value]) => {\n if (onCols.includes(key)) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = value;\n } else {\n merged[key] = value;\n }\n });\n return merged;\n });\n}\n\n/**\n * Filter rows by project ID\n * @param {Array<Object>} rows - Array of rows with project_id field\n * @param {string|null} projectId - Project ID to filter by (null returns all rows)\n * @returns {Array<Object>} Filtered rows\n */\nexport function filterByProject(rows = [], projectId = null) {\n if (projectId === null || projectId === undefined) return [...rows];\n if (!rows.length) return [];\n \n // Check if rows have project_id column\n const hasProjectId = rows.some(row => PROJECT_ID in row);\n if (!hasProjectId) return [...rows];\n \n return rows.filter((row) => row?.[PROJECT_ID] === projectId);\n}\n\n/**\n * Coerce specified columns to numeric (finite number or undefined)\n * @param {Array<Object>} rows - Array of rows\n * @param {Array<string>} columns - Column names to coerce to numeric\n * @returns {Array<Object>} Rows with coerced numeric columns\n */\nexport function coerceNumeric(rows = [], columns = []) {\n return rows.map((row) => {\n const next = { ...row };\n columns.forEach((column) => {\n if (!(column in next)) return;\n const n = toNumber(next[column]);\n next[column] = n;\n });\n return next;\n });\n}\n\n/**\n * Assemble a complete drillhole dataset from component tables\n * @param {Object} components - Dataset components\n * @param {Array<Object>} components.collars - Collar data\n * @param {Array<Object>} components.surveys - Survey data\n * @param {Array<Object>} components.assays - Assay data\n * @param {Array<Object>} components.geology - Geology interval data\n * @param {Array<Object>} components.structures - Structural data (optional)\n * @param {Object} components.metadata - Dataset metadata (optional)\n * @returns {{collars: Array, surveys: Array, assays: Array, geology: Array, structures: Array, metadata: Object}} Complete dataset\n */\nexport function assembleDataset({\n collars = [],\n surveys = [],\n assays = [],\n geology = [],\n structures = [],\n metadata = {}\n} = {}) {\n return {\n collars: toArray(collars),\n surveys: toArray(surveys),\n assays: toArray(assays),\n geology: toArray(geology),\n structures: toArray(structures),\n metadata: metadata || {}\n };\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { withDataErrorContext } from './dataErrorUtils.js';\n\n// ---------------------------------------------------------------------------\n// Column-name normalisation\n// ---------------------------------------------------------------------------\n\n/** Canonical geometry columns expected on every block row. */\nconst GEOMETRY_COLS = ['x', 'y', 'z', 'dx', 'dy', 'dz'];\n\n/** Maps accepted source column name variants to canonical names. */\nconst BLOCK_COL_MAP = {\n x: ['x', 'easting', 'center_x', 'xc', 'xcentre', 'xcenter', 'x_centre', 'x_center', 'cx'],\n y: ['y', 'northing', 'center_y', 'yc', 'ycentre', 'ycenter', 'y_centre', 'y_center', 'cy'],\n z: ['z', 'elevation', 'center_z', 'zc', 'zcentre', 'zcenter', 'z_centre', 'z_center', 'cz'],\n dx: ['dx', 'size_x', 'sx', 'sizex', 'dim_x', 'block_size_x'],\n dy: ['dy', 'size_y', 'sy', 'sizey', 'dim_y', 'block_size_y'],\n dz: ['dz', 'size_z', 'sz', 'sizez', 'dim_z', 'block_size_z'],\n};\n\n/** Reverse lookup: lower-cased source name → canonical name */\nconst _blockColLookup = {};\nObject.entries(BLOCK_COL_MAP).forEach(([canon, variants]) => {\n variants.forEach((v) => { _blockColLookup[v.toLowerCase()] = canon; });\n});\n\n/**\n * Rename a block row's keys from source column names to canonical names.\n * Unrecognised keys are kept as-is.\n * @param {Object} row - Raw parsed row object\n * @returns {Object} Row with canonical geometry key names\n */\nexport function normalizeBlockRow(row) {\n const out = {};\n Object.entries(row).forEach(([key, value]) => {\n const canon = _blockColLookup[key.toLowerCase().trim()] || key;\n out[canon] = value;\n });\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// CSV parsing\n// ---------------------------------------------------------------------------\n\n/**\n * Parse block model CSV data.\n *\n * Accepts columns named ``x/y/z/dx/dy/dz`` (baselode canonical) or the\n * legacy ``center_x/center_y/center_z/size_x/size_y/size_z`` variants.\n * Both forms are normalised to the canonical names.\n *\n * @param {File|Blob|string} file - Block model CSV source\n * @returns {Promise<{data: Array<Object>, properties: Array<string>}>}\n * Parsed blocks (canonical column names) and property column names.\n */\nexport function parseBlockModelCSV(file) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const normalized = (results.data || []).map(normalizeBlockRow);\n const data = normalized.filter(\n (row) => row.x !== null && row.y !== null && row.z !== null\n );\n\n const propertyColumns = Object.keys(data[0] || {}).filter(\n (key) => !GEOMETRY_COLS.includes(key)\n );\n\n resolve({ data, properties: propertyColumns });\n },\n error: (error) => {\n reject(withDataErrorContext('parseBlockModelCSV', error));\n }\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Metadata\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a block model metadata JSON string or plain object.\n *\n * The metadata object may contain: ``name``, ``description``, ``crs``,\n * ``origin``, ``max_block_size``, ``min_block_size``, ``bbox_3d``,\n * ``outline_2d``, ``attributes``, ``extra``.\n *\n * @param {string|Object} source - JSON string or already-parsed object\n * @returns {Object} Parsed metadata object\n */\nexport function loadBlockModelMetadata(source) {\n if (typeof source === 'string') {\n try {\n return JSON.parse(source);\n } catch (err) {\n throw withDataErrorContext('loadBlockModelMetadata', err);\n }\n }\n if (source && typeof source === 'object') return source;\n throw withDataErrorContext('loadBlockModelMetadata', new Error('Invalid metadata source'));\n}\n\n// ---------------------------------------------------------------------------\n// Property statistics\n// ---------------------------------------------------------------------------\n\n/**\n * Calculate statistics for a property column in block model data.\n *\n * @param {Array<Object>} data - Block model data array\n * @param {string} property - Property column name to analyse\n * @returns {{type: 'numeric'|'categorical', min?: number, max?: number,\n * values: Array, categories?: Array}} Property statistics\n */\nexport function calculatePropertyStats(data, property) {\n const values = data\n .map((row) => row[property])\n .filter((v) => v !== null && v !== undefined);\n\n const isNumeric = values.length > 0 && values.every((v) => typeof v === 'number');\n\n if (isNumeric) {\n const min = Math.min(...values);\n const max = Math.max(...values);\n return { type: 'numeric', min, max, values };\n }\n\n const uniqueValues = [...new Set(values)];\n return { type: 'categorical', categories: uniqueValues, values };\n}\n\n/**\n * Compute per-property statistics for all non-geometry columns.\n *\n * @param {Array<Object>} data - Block model data array\n * @returns {Object} Map of ``propertyName → stats`` (same shape as\n * :func:`calculatePropertyStats` return value)\n */\nexport function getBlockStats(data) {\n if (!data || data.length === 0) return {};\n const propertyKeys = Object.keys(data[0]).filter(\n (key) => !GEOMETRY_COLS.includes(key)\n );\n const result = {};\n propertyKeys.forEach((key) => {\n result[key] = calculatePropertyStats(data, key);\n });\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Filtering and volume\n// ---------------------------------------------------------------------------\n\n/**\n * Filter block model rows by attribute criteria.\n *\n * @param {Array<Object>} data - Block model data array\n * @param {Object} criteria - Map of ``columnName → condition``.\n * Each condition may be:\n * - A scalar (exact equality)\n * - An object with operator keys: ``gt``, ``gte``, ``lt``, ``lte``,\n * ``eq``, ``ne``, ``in`` (array)\n * @returns {Array<Object>} Filtered array of block rows\n */\nexport function filterBlocks(data, criteria) {\n if (!criteria || typeof criteria !== 'object') return data;\n return data.filter((row) =>\n Object.entries(criteria).every(([col, condition]) => {\n const val = row[col];\n if (condition === null || condition === undefined) return true;\n if (typeof condition !== 'object' || Array.isArray(condition)) {\n return val === condition;\n }\n if ('gt' in condition && !(val > condition.gt)) return false;\n if ('gte' in condition && !(val >= condition.gte)) return false;\n if ('lt' in condition && !(val < condition.lt)) return false;\n if ('lte' in condition && !(val <= condition.lte)) return false;\n if ('eq' in condition && val !== condition.eq) return false;\n if ('ne' in condition && val === condition.ne) return false;\n if ('in' in condition && !condition.in.includes(val)) return false;\n return true;\n })\n );\n}\n\n/**\n * Calculate total block volume, optionally filtered by criteria.\n *\n * @param {Array<Object>} data - Block model data array (canonical column names)\n * @param {Object|null} [criteria] - Optional filter criteria (see :func:`filterBlocks`)\n * @returns {number} Sum of dx * dy * dz for matching blocks\n */\nexport function calculateBlockVolume(data, criteria = null) {\n const subset = criteria ? filterBlocks(data, criteria) : data;\n return subset.reduce((sum, row) => {\n const dx = Number(row.dx) || 0;\n const dy = Number(row.dy) || 0;\n const dz = Number(row.dz) || 0;\n return sum + dx * dy * dz;\n }, 0);\n}\n\n// ---------------------------------------------------------------------------\n// Colorisation\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a color for a property value based on its statistics.\n *\n * @param {*} value - Property value to colourise\n * @param {{type: 'numeric'|'categorical', min?: number, max?: number,\n * categories?: Array}|null} stats - Property statistics\n * @param {Object} THREEInstance - THREE.js instance for creating Color objects\n * @returns {THREE.Color} Color object for the value\n */\nexport function getColorForValue(value, stats, THREEInstance) {\n if (!stats) return new THREEInstance.Color('#888888');\n\n if (stats.type === 'numeric') {\n const range = stats.max - stats.min;\n const normalized = range === 0 ? 0.5 : (value - stats.min) / range;\n const hue = (1 - normalized) * 240; // blue=240, red=0\n return new THREEInstance.Color().setHSL(hue / 360, 0.8, 0.5);\n }\n\n const index = stats.categories.indexOf(value);\n const hue = (index / Math.max(stats.categories.length, 1)) * 360;\n return new THREEInstance.Color().setHSL(hue / 360, 0.7, 0.5);\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, FROM, TO, DEPTH, DIP, AZIMUTH } from './datamodel.js';\n\n/**\n * Normalize a raw CSV row to standardized column names.\n * @private\n */\nconst normalizeRow = (rawRow, sourceColumnMap = null) => standardizeColumns(rawRow, null, sourceColumnMap);\n\n/**\n * Determine if a set of rows represents point or interval data.\n * @private\n * @param {Array<Object>} rows - Normalized rows\n * @returns {'point'|'interval'|null}\n */\nfunction detectSchema(rows) {\n if (!rows.length) return null;\n const first = rows[0];\n const hasInterval = FROM in first && TO in first;\n const hasPoint = DEPTH in first && !hasInterval;\n if (hasInterval) return 'interval';\n if (hasPoint) return 'point';\n return null;\n}\n\n/**\n * Coerce a value to a finite number or null.\n * @private\n */\nfunction toNumber(v) {\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Normalize a single structural point row.\n * @private\n */\nfunction extractStructuralPoint(row) {\n const holeId = row[HOLE_ID] !== undefined ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) return null;\n const depth = toNumber(row[DEPTH]);\n if (depth === null) return null;\n\n return {\n [HOLE_ID]: holeId,\n [DEPTH]: depth,\n [DIP]: toNumber(row[DIP]),\n [AZIMUTH]: toNumber(row[AZIMUTH]),\n comments: row.comments != null ? `${row.comments}` : null,\n ...row,\n };\n}\n\n/**\n * Normalize a single structural interval row.\n * @private\n */\nfunction extractStructuralInterval(row) {\n const holeId = row[HOLE_ID] !== undefined ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) return null;\n const from = toNumber(row[FROM]);\n const to = toNumber(row[TO]);\n if (from === null || to === null || to <= from) return null;\n\n const mid = 0.5 * (from + to);\n return {\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n mid,\n [DIP]: toNumber(row[DIP]),\n [AZIMUTH]: toNumber(row[AZIMUTH]),\n classification: row.classification != null ? `${row.classification}` : null,\n comments: row.comments != null ? `${row.comments}` : null,\n ...row,\n };\n}\n\n/**\n * Validate an array of structural point rows.\n * Returns an object with valid rows and error details.\n *\n * @param {Array<Object>} rows - Normalized structural point rows\n * @returns {{ valid: Array<Object>, errors: Array<{row: Object, message: string}> }}\n */\nexport function validateStructuralPoints(rows) {\n const valid = [];\n const errors = [];\n\n for (const row of rows) {\n const messages = [];\n const dip = toNumber(row[DIP]);\n const az = toNumber(row[AZIMUTH]);\n\n if (dip !== null && (dip < 0 || dip > 90)) {\n messages.push(`dip ${dip} out of range [0, 90]`);\n }\n if (az !== null && (az < 0 || az >= 360)) {\n messages.push(`azimuth ${az} out of range [0, 360)`);\n }\n if (messages.length) {\n errors.push({ row, message: messages.join('; ') });\n } else {\n valid.push(row);\n }\n }\n\n return { valid, errors };\n}\n\n/**\n * Parse a structural points CSV (point schema: hole_id, depth, dip, azimuth, ...).\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<Array<Object>>} Array of structural point objects\n */\nexport function parseStructuralPointsCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const opts = {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = [];\n for (const rawRow of results.data) {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const point = extractStructuralPoint(row);\n if (point) rows.push(point);\n }\n resolve(rows);\n },\n error: (error) => reject(withDataErrorContext('parseStructuralPointsCSV', error)),\n };\n\n if (typeof source === 'string' && !source.startsWith('data:') && source.includes('\\n')) {\n Papa.parse(source, opts);\n } else {\n Papa.parse(source, opts);\n }\n });\n}\n\n/**\n * Parse a structural intervals CSV (interval schema: hole_id, from, to, dip, azimuth, ...).\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<Array<Object>>} Array of structural interval objects\n */\nexport function parseStructuralIntervalsCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = [];\n for (const rawRow of results.data) {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const interval = extractStructuralInterval(row);\n if (interval) rows.push(interval);\n }\n resolve(rows);\n },\n error: (error) => reject(withDataErrorContext('parseStructuralIntervalsCSV', error)),\n });\n });\n}\n\n/**\n * Group a flat array of structural rows into hole objects keyed by hole_id.\n *\n * The resulting hole objects use the same shape as assay holes\n * ({ holeId, points }) so they can be merged into useDrillholeTraceGrid.\n *\n * @param {Array<Object>} rows - Flat array of structural rows (already normalized)\n * @param {string} [holeIdCol='hole_id'] - Column name containing the hole identifier\n * @returns {Array<{ holeId: string, points: Array<Object> }>}\n */\nexport function groupRowsByHole(rows, holeIdCol = HOLE_ID) {\n const byId = new Map();\n for (const row of rows) {\n const id = row[holeIdCol] != null ? String(row[holeIdCol]).trim() : '';\n if (!id) continue;\n if (!byId.has(id)) byId.set(id, { holeId: id, points: [] });\n byId.get(id).points.push(row);\n }\n return Array.from(byId.values());\n}\n\n/**\n * Parse a structural CSV, auto-detecting point vs interval schema.\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<{ schema: 'point'|'interval', rows: Array<Object> }>}\n */\nexport function parseStructuralCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const normalized = results.data.map(r => normalizeRow(r, sourceColumnMap));\n const schema = detectSchema(normalized);\n\n if (!schema) {\n reject(withDataErrorContext('parseStructuralCSV',\n new Error(\"Structural CSV requires either 'depth' (point) or 'from'/'to' (interval) columns\")));\n return;\n }\n\n const rows = [];\n for (const row of normalized) {\n const parsed = schema === 'interval'\n ? extractStructuralInterval(row)\n : extractStructuralPoint(row);\n if (parsed) rows.push(parsed);\n }\n resolve({ schema, rows });\n },\n error: (error) => reject(withDataErrorContext('parseStructuralCSV', error)),\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { HOLE_ID, FROM, TO, MID, DEPTH, DIP, AZIMUTH } from './datamodel.js';\nimport { parseStructuralCSV, groupRowsByHole } from './structuralLoader.js';\n\n\n/**\n * Parse assay CSV text into an array of hole objects.\n *\n * Each interval row gets a pre-computed `mid` field (= (from + to) / 2) stored\n * under both the `mid` and `depth` keys so it can serve as the unified y-axis\n * depth value when rendered alongside structural point data.\n *\n * @param {string} csvText - Assay CSV content as a text string\n * @returns {Promise<Array<{holeId: string, points: Array<Object>}>>}\n */\nexport function parseAssayCsvTextToHoles(csvText) {\n return new Promise((resolve) => {\n Papa.parse(csvText, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n for (const rawRow of results.data) {\n const row = standardizeColumns(rawRow);\n const holeId = row[HOLE_ID] != null ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) continue;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const mid = (from + to) / 2;\n // Strip structural-domain columns that some assay exports (e.g. GSWA) include\n // as per-interval hole-orientation values — these are borehole survey attributes,\n // not structural plane measurements, and must not appear as selectable properties\n // alongside true structural data.\n // eslint-disable-next-line no-unused-vars\n const { [DIP]: _dip, [AZIMUTH]: _az, ...rowWithoutStructural } = row;\n const point = {\n ...rowWithoutStructural,\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n [MID]: mid,\n [DEPTH]: mid, // unified depth field for y-axis rendering\n _source: 'assay',\n };\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push(point);\n }\n const holes = Array.from(byHole.entries()).map(([holeId, points]) => ({\n holeId,\n points: points.sort((a, b) => a[FROM] - b[FROM]),\n }));\n resolve(holes);\n },\n });\n });\n}\n\n/**\n * Parse geology CSV text into an array of hole objects, keyed by hole_id.\n *\n * @param {string} csvText - Geology CSV content as a text string\n * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport function parseGeologyCsvText(csvText) {\n return new Promise((resolve) => {\n Papa.parse(csvText, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n for (const rawRow of results.data) {\n const row = standardizeColumns(rawRow);\n const holeId = (row[HOLE_ID] ?? '').toString().trim();\n if (!holeId) continue;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const mid = (from + to) / 2;\n // eslint-disable-next-line no-unused-vars\n const { [DIP]: _dip, [AZIMUTH]: _az, ...rest } = row;\n const point = {\n ...rest,\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n [MID]: mid,\n [DEPTH]: mid,\n _source: 'geology',\n };\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push(point);\n }\n resolve({\n holes: Array.from(byHole.entries()).map(([holeId, points]) => ({\n holeId,\n points: points.sort((a, b) => a[FROM] - b[FROM]),\n })),\n });\n },\n });\n });\n}\n\n/**\n * Build a unified drillhole dataset by merging assay intervals and structural\n * point/interval measurements from their respective CSV text sources.\n *\n * The result is a flat, holeId-keyed collection of hole objects. Each hole's\n * `points` array contains rows from both sources:\n * - Assay rows carry all geochemical columns; `depth` is set to the interval\n * midpoint ((from + to) / 2) pre-computed at load time.\n * - Structural rows carry dip/azimuth/description columns; `depth` comes\n * directly from the structural CSV (point schema) or the interval midpoint\n * (interval schema).\n * - Rows from each source are tagged with `_source: 'assay'|'structural'`.\n *\n * The returned holes can be passed directly to `useDrillholeTraceGrid` as\n * `extraHoles` (without a `sourceFile`) since all data is eager-loaded.\n *\n * @param {Object} options\n * @param {string} [options.assayCsv] - Assay CSV text\n * @param {string} [options.structuralCsv] - Structural CSV text\n * @param {string} [options.geologyCsv] - Geology CSV text\n * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport async function parseUnifiedDataset({ assayCsv, structuralCsv, geologyCsv } = {}) {\n const [assayHoles, structuralHoles, geologyHoles] = await Promise.all([\n assayCsv ? parseAssayCsvTextToHoles(assayCsv) : Promise.resolve([]),\n structuralCsv\n ? parseStructuralCSV(structuralCsv).then(({ rows }) =>\n groupRowsByHole(rows.map((r) => ({ ...r, _source: 'structural' })))\n )\n : Promise.resolve([]),\n geologyCsv ? parseGeologyCsvText(geologyCsv).then(({ holes }) => holes) : Promise.resolve([]),\n ]);\n\n // Merge holes from all sources by holeId\n const byId = new Map(assayHoles.map((h) => [h.holeId, { ...h, points: [...h.points] }]));\n for (const sh of [...structuralHoles, ...geologyHoles]) {\n const id = sh.holeId;\n if (!id) continue;\n if (byId.has(id)) {\n const existing = byId.get(id);\n byId.set(id, { ...existing, points: [...existing.points, ...(sh.points || [])] });\n } else {\n byId.set(id, sh);\n }\n }\n\n return { holes: Array.from(byId.values()) };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Calculate significant intercepts from assay interval data.\n *\n * A significant intercept is a contiguous downhole run of assay intervals where\n * every interval meets or exceeds {@link minGrade}, and the total run length is\n * at least {@link minLength}.\n *\n * @param {Array<Object>} intervals - Assay interval rows, each containing at\n * minimum the hole ID, from-depth, to-depth, and assay field columns.\n * @param {string} assayField - Name of the assay value property on each row\n * (e.g. `\"CU_PCT\"` or `\"AU_PPM\"`).\n * @param {number} minGrade - Minimum grade threshold. Intervals below this\n * value are excluded.\n * @param {number} minLength - Minimum contiguous downhole length required to\n * report an intercept.\n * @param {Object} [options={}]\n * @param {string} [options.fromCol=\"from\"] - Property name for the from-depth.\n * @param {string} [options.toCol=\"to\"] - Property name for the to-depth.\n * @param {string} [options.holeCol=\"hole_id\"] - Property name for the hole ID.\n * @returns {Array<Object>} One object per significant intercept with properties:\n * `hole_id`, `assay_field`, `from`, `to`, `length`, `avg_grade`,\n * `n_samples`, `label`.\n */\nexport function significantIntercepts(intervals, assayField, minGrade, minLength, options = {}) {\n const fromCol = options.fromCol || 'from';\n const toCol = options.toCol || 'to';\n const holeCol = options.holeCol || 'hole_id';\n\n if (!intervals || !intervals.length) return [];\n\n // Group by hole ID\n const byHole = {};\n for (const row of intervals) {\n const holeId = row[holeCol];\n if (holeId == null) continue;\n if (!byHole[holeId]) byHole[holeId] = [];\n byHole[holeId].push(row);\n }\n\n const results = [];\n\n for (const [holeId, holeIntervals] of Object.entries(byHole)) {\n // Sort by from depth\n const sorted = [...holeIntervals].sort((a, b) => Number(a[fromCol]) - Number(b[fromCol]));\n\n // Filter to above-threshold intervals with finite grades\n const qualifying = sorted.filter((row) => {\n const grade = Number(row[assayField]);\n return Number.isFinite(grade) && grade >= minGrade;\n });\n\n if (!qualifying.length) continue;\n\n // Group qualifying intervals into contiguous runs\n const runs = [];\n let currentRun = [];\n let prevTo = null;\n\n for (const row of qualifying) {\n const f = Number(row[fromCol]);\n const t = Number(row[toCol]);\n\n if (prevTo === null || Math.abs(f - prevTo) > 1e-6) {\n if (currentRun.length) runs.push(currentRun);\n currentRun = [row];\n } else {\n currentRun.push(row);\n }\n prevTo = t;\n }\n if (currentRun.length) runs.push(currentRun);\n\n for (const run of runs) {\n const totalFrom = Number(run[0][fromCol]);\n const totalTo = Number(run[run.length - 1][toCol]);\n const totalLength = totalTo - totalFrom;\n\n if (totalLength < minLength) continue;\n\n let weightedSum = 0;\n let totalWeight = 0;\n for (const row of run) {\n const grade = Number(row[assayField]);\n const len = Number(row[toCol]) - Number(row[fromCol]);\n weightedSum += grade * len;\n totalWeight += len;\n }\n const avgGrade = weightedSum / totalWeight;\n const nSamples = run.length;\n const label = `${totalLength.toFixed(1)} m @ ${avgGrade.toFixed(2)} ${assayField}`;\n\n results.push({\n [holeCol]: holeId,\n assay_field: assayField,\n [fromCol]: totalFrom,\n [toCol]: totalTo,\n length: totalLength,\n avg_grade: avgGrade,\n n_samples: nSamples,\n label,\n });\n }\n }\n\n return results;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Interpolate a 3D position and drill direction along a desurveyed trace.\n *\n * @param {Array<{hole_id: string, md: number, x: number, y: number, z: number, azimuth?: number, dip?: number}>} sortedTraceRows\n * Trace points sorted ascending by md.\n * @param {number} targetMd - Measured depth to interpolate at.\n * @returns {{x: number, y: number, z: number, dx: number, dy: number, dz: number}|null}\n * Position and unit drill direction (dz negative = downward), or null if not possible.\n */\nexport function interpolateTrace(sortedTraceRows, targetMd) {\n if (!sortedTraceRows || sortedTraceRows.length === 0) return null;\n if (!Number.isFinite(targetMd)) return null;\n\n const n = sortedTraceRows.length;\n\n if (n === 1) {\n const p = sortedTraceRows[0];\n return { x: Number(p.x), y: Number(p.y), z: Number(p.z), dx: 0, dy: 0, dz: -1 };\n }\n\n // Find enclosing segment\n let segIdx = -1;\n for (let i = 0; i < n - 1; i++) {\n const md0 = Number(sortedTraceRows[i].md);\n const md1 = Number(sortedTraceRows[i + 1].md);\n if (targetMd >= md0 && targetMd <= md1) {\n segIdx = i;\n break;\n }\n }\n\n let p0, p1, t;\n\n if (segIdx === -1) {\n if (targetMd < Number(sortedTraceRows[0].md)) {\n // Before first point — extrapolate using first segment\n p0 = sortedTraceRows[0];\n p1 = sortedTraceRows[1];\n } else {\n // Beyond last point — extend using last segment\n p0 = sortedTraceRows[n - 2];\n p1 = sortedTraceRows[n - 1];\n }\n const md0 = Number(p0.md);\n const md1 = Number(p1.md);\n const segLen = md1 - md0;\n t = segLen > 0 ? (targetMd - md0) / segLen : (targetMd < md0 ? 0 : 1);\n } else {\n p0 = sortedTraceRows[segIdx];\n p1 = sortedTraceRows[segIdx + 1];\n const md0 = Number(p0.md);\n const md1 = Number(p1.md);\n const segLen = md1 - md0;\n t = segLen > 0 ? (targetMd - md0) / segLen : 0;\n }\n\n const x = Number(p0.x) + t * (Number(p1.x) - Number(p0.x));\n const y = Number(p0.y) + t * (Number(p1.y) - Number(p0.y));\n const z = Number(p0.z) + t * (Number(p1.z) - Number(p0.z));\n\n // Compute drill direction: prefer azimuth/dip, fall back to finite differences\n let dx, dy, dz;\n const az0 = Number(p0.azimuth);\n const dip0 = Number(p0.dip);\n const az1 = Number(p1.azimuth);\n const dip1 = Number(p1.dip);\n\n if (Number.isFinite(az0) && Number.isFinite(dip0)) {\n const az = Number.isFinite(az1) && Number.isFinite(dip1)\n ? az0 + t * (az1 - az0)\n : az0;\n const dip = Number.isFinite(az1) && Number.isFinite(dip1)\n ? dip0 + t * (dip1 - dip0)\n : dip0;\n const azRad = (az * Math.PI) / 180;\n const dipRad = (dip * Math.PI) / 180;\n // Match directionCosines convention: dz = -sin(dip) (downward positive)\n dx = Math.cos(dipRad) * Math.sin(azRad);\n dy = Math.cos(dipRad) * Math.cos(azRad);\n dz = -Math.sin(dipRad);\n } else {\n // Finite difference from segment\n const ddx = Number(p1.x) - Number(p0.x);\n const ddy = Number(p1.y) - Number(p0.y);\n const ddz = Number(p1.z) - Number(p0.z);\n const len = Math.sqrt(ddx * ddx + ddy * ddy + ddz * ddz);\n if (len < 1e-10) return { x, y, z, dx: 0, dy: 0, dz: -1 };\n dx = ddx / len;\n dy = ddy / len;\n dz = ddz / len;\n }\n\n // Normalize direction\n const dlen = Math.sqrt(dx * dx + dy * dy + dz * dz);\n if (dlen < 1e-10) return { x, y, z, dx: 0, dy: 0, dz: -1 };\n return { x, y, z, dx: dx / dlen, dy: dy / dlen, dz: dz / dlen };\n}\n\n/**\n * Convert core-relative alpha/beta angles to a geographic plane normal.\n *\n * The drill frame:\n * D = unit drill direction (positive = downward along hole)\n * R = normalize(cross(U, D)) where U = (0,0,1) or fallback (0,1,0)\n * B = normalize(cross(D, R))\n *\n * Sanity checks:\n * alpha=90 → N perpendicular to D\n * alpha=0 → N parallel to D\n *\n * @param {number} alphaDeg - Alpha angle in degrees; 90 = plane perpendicular to drill axis.\n * @param {number} betaDeg - Beta angle in degrees; rotation of reference mark around drill axis.\n * @param {{dx: number, dy: number, dz: number}} drillDir - Unit drill direction vector.\n * @param {Object} [opts]\n * @param {'R'|'B'} [opts.betaZeroAxis='B'] - Frame axis corresponding to beta=0.\n * @param {number} [opts.betaHandedness=1] - +1 for right-hand rotation, -1 for left-hand.\n * @returns {{nx: number, ny: number, nz: number}} Unit normal vector in ENU coordinates.\n */\nexport function alphaBetaToNormal(alphaDeg, betaDeg, drillDir, opts = {}) {\n const { betaZeroAxis = 'B', betaHandedness = 1 } = opts;\n const { dx, dy, dz } = drillDir;\n const D = [dx, dy, dz];\n\n // Up vector with fallback when D is nearly vertical\n let U = [0, 0, 1];\n const dotDU = D[0] * U[0] + D[1] * U[1] + D[2] * U[2];\n if (Math.abs(dotDU) > 0.99) {\n U = [0, 1, 0];\n }\n\n // R = normalize(cross(U, D))\n const crossUD = [\n U[1] * D[2] - U[2] * D[1],\n U[2] * D[0] - U[0] * D[2],\n U[0] * D[1] - U[1] * D[0],\n ];\n const crossUDLen = Math.sqrt(crossUD[0] ** 2 + crossUD[1] ** 2 + crossUD[2] ** 2);\n const R = crossUDLen > 1e-10\n ? [crossUD[0] / crossUDLen, crossUD[1] / crossUDLen, crossUD[2] / crossUDLen]\n : [1, 0, 0];\n\n // B = normalize(cross(D, R))\n const crossDR = [\n D[1] * R[2] - D[2] * R[1],\n D[2] * R[0] - D[0] * R[2],\n D[0] * R[1] - D[1] * R[0],\n ];\n const crossDRLen = Math.sqrt(crossDR[0] ** 2 + crossDR[1] ** 2 + crossDR[2] ** 2);\n const B = crossDRLen > 1e-10\n ? [crossDR[0] / crossDRLen, crossDR[1] / crossDRLen, crossDR[2] / crossDRLen]\n : [0, 1, 0];\n\n // Starting axis for beta rotation\n const N_perp0 = betaZeroAxis === 'R' ? R : B;\n\n // Rodrigues rotation: rotate N_perp0 around D by (beta * betaHandedness) radians\n const betaRad = (betaDeg * Math.PI) / 180 * betaHandedness;\n const cosB = Math.cos(betaRad);\n const sinB = Math.sin(betaRad);\n const dotND = N_perp0[0] * D[0] + N_perp0[1] * D[1] + N_perp0[2] * D[2];\n const crossDN = [\n D[1] * N_perp0[2] - D[2] * N_perp0[1],\n D[2] * N_perp0[0] - D[0] * N_perp0[2],\n D[0] * N_perp0[1] - D[1] * N_perp0[0],\n ];\n const N_perp = [\n N_perp0[0] * cosB + crossDN[0] * sinB + D[0] * dotND * (1 - cosB),\n N_perp0[1] * cosB + crossDN[1] * sinB + D[1] * dotND * (1 - cosB),\n N_perp0[2] * cosB + crossDN[2] * sinB + D[2] * dotND * (1 - cosB),\n ];\n\n // theta = (90 - alpha) * pi/180; N = normalize(cos(theta)*N_perp + sin(theta)*D)\n const theta = ((90 - alphaDeg) * Math.PI) / 180;\n const cosT = Math.cos(theta);\n const sinT = Math.sin(theta);\n const Nx = cosT * N_perp[0] + sinT * D[0];\n const Ny = cosT * N_perp[1] + sinT * D[1];\n const Nz = cosT * N_perp[2] + sinT * D[2];\n\n const NLen = Math.sqrt(Nx * Nx + Ny * Ny + Nz * Nz);\n if (NLen < 1e-10) return { nx: 0, ny: 0, nz: 1 };\n return { nx: Nx / NLen, ny: Ny / NLen, nz: Nz / NLen };\n}\n\n/**\n * Compute 3D positions and plane normals for structural measurements.\n *\n * Prioritises alpha/beta core angles over geographic dip/azimuth.\n * Rows with no matching trace or missing required fields are excluded.\n *\n * @param {Array<Object>} structures - Flat rows with {hole_id, depth?, mid?, alpha?, beta?, dip?, azimuth?, ...}\n * @param {Array<Object>} traceRows - Flat rows with {hole_id, md, x, y, z, azimuth?, dip?}\n * @param {Object} [opts] - Options forwarded to alphaBetaToNormal.\n * @returns {Array<Object>} Structure rows enriched with {x, y, z, nx, ny, nz}.\n */\nexport function computeStructuralPositions(structures, traceRows, opts = {}) {\n if (!structures?.length || !traceRows?.length) return [];\n\n // Group trace rows by hole_id (lowercase) sorted ascending by md\n const tracesByHole = new Map();\n for (const row of traceRows) {\n const holeId = row.hole_id != null ? `${row.hole_id}`.trim().toLowerCase() : '';\n if (!holeId) continue;\n if (!tracesByHole.has(holeId)) tracesByHole.set(holeId, []);\n tracesByHole.get(holeId).push(row);\n }\n for (const [, rows] of tracesByHole) {\n rows.sort((a, b) => Number(a.md) - Number(b.md));\n }\n\n const result = [];\n for (const s of structures) {\n const holeId = s.hole_id != null ? `${s.hole_id}`.trim().toLowerCase() : '';\n if (!holeId) continue;\n\n const holeTrace = tracesByHole.get(holeId);\n if (!holeTrace || holeTrace.length === 0) continue;\n\n const depth = s.depth != null ? Number(s.depth) : (s.mid != null ? Number(s.mid) : null);\n if (!Number.isFinite(depth)) continue;\n\n const pos = interpolateTrace(holeTrace, depth);\n if (!pos) continue;\n\n const { x, y, z, dx, dy, dz } = pos;\n\n let nx, ny, nz;\n const alpha = s.alpha != null ? Number(s.alpha) : null;\n const beta = s.beta != null ? Number(s.beta) : null;\n\n if (Number.isFinite(alpha)) {\n const betaVal = Number.isFinite(beta) ? beta : 0;\n const n = alphaBetaToNormal(alpha, betaVal, { dx, dy, dz }, opts);\n nx = n.nx;\n ny = n.ny;\n nz = n.nz;\n } else {\n const dip = s.dip != null ? Number(s.dip) : null;\n const az = s.azimuth != null ? Number(s.azimuth) : null;\n if (!Number.isFinite(dip) || !Number.isFinite(az)) continue;\n // Geographic formula matching dipAzimuthToNormal in structuralScene.js\n const dipRad = (dip * Math.PI) / 180;\n const azRad = (az * Math.PI) / 180;\n nx = Math.sin(azRad) * Math.sin(dipRad);\n ny = Math.cos(azRad) * Math.sin(dipRad);\n nz = Math.cos(dipRad);\n }\n\n result.push({ ...s, x, y, z, nx, ny, nz });\n }\n\n return result;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Baselode Light Plotly template.\n *\n * Defines the Baselode visual identity for Plotly charts. Apply this template\n * by including it in the layout object passed to Plotly:\n *\n * ```js\n * import { BASELODE_TEMPLATE } from 'baselode';\n * Plotly.react(el, data, { ...layout, template: BASELODE_TEMPLATE });\n * ```\n *\n * Baselode plotting helpers apply this template by default. Pass a different\n * template as an option to override the visual style.\n */\n\n/** @type {string} Name of the Baselode Plotly template. */\nexport const BASELODE_TEMPLATE_NAME = 'baselode';\n\n/** @type {string} Alias for the Baselode Light template name. */\nexport const BASELODE_LIGHT_TEMPLATE_NAME = BASELODE_TEMPLATE_NAME;\n\n/** @type {string[]} Default colorway used across Baselode charts. */\nexport const BASELODE_COLORWAY = [\n '#8b1e3f',\n '#2563eb',\n '#16a34a',\n '#f59e0b',\n '#7c3aed',\n '#0ea5e9',\n '#ef4444',\n '#10b981',\n '#f97316',\n '#8b5cf6',\n];\n\n/** @type {Object} Baselode Light colour palette. */\nexport const BASELODE_LIGHT = {\n bg: '#ffffff',\n panel: '#f8fafc',\n ink: '#1e293b',\n ink_soft: '#64748b',\n grid: '#e8e8e8',\n line: '#d0d0d0',\n accent: '#f59e0b',\n accent_2: '#fcd34d',\n muted_1: '#94a3b8',\n muted_2: '#cbd5e1',\n muted_3: '#e2e8f0',\n primary: '#8b1e3f',\n primary_2: '#a8324f',\n};\n\n/**\n * Baselode Plotly template object.\n *\n * This object can be passed directly as the ``template`` property of a Plotly\n * layout to apply Baselode's default visual style. Baselode plotting helpers\n * include this template in their returned layout objects automatically.\n *\n * @type {Object}\n */\nexport const BASELODE_TEMPLATE = {\n layout: {\n paper_bgcolor: BASELODE_LIGHT.bg,\n plot_bgcolor: BASELODE_LIGHT.bg,\n colorway: BASELODE_COLORWAY,\n font: {\n family: 'Inter, system-ui, sans-serif',\n size: 12,\n color: BASELODE_LIGHT.ink,\n },\n title: {\n font: { size: 14, color: BASELODE_LIGHT.ink },\n x: 0.05,\n },\n hovermode: 'x unified',\n hoverlabel: {\n bgcolor: BASELODE_LIGHT.bg,\n bordercolor: BASELODE_LIGHT.line,\n font: { size: 12, color: BASELODE_LIGHT.ink },\n },\n legend: {\n bgcolor: 'rgba(255,255,255,0.9)',\n bordercolor: BASELODE_LIGHT.muted_3,\n borderwidth: 1,\n font: { size: 11, color: BASELODE_LIGHT.ink },\n orientation: 'h',\n yanchor: 'bottom',\n y: 1.02,\n xanchor: 'left',\n x: 0.0,\n },\n xaxis: {\n showline: true,\n linewidth: 1,\n linecolor: BASELODE_LIGHT.line,\n mirror: false,\n ticks: 'outside',\n tickwidth: 1,\n tickcolor: BASELODE_LIGHT.line,\n ticklen: 4,\n showgrid: true,\n gridcolor: BASELODE_LIGHT.grid,\n gridwidth: 1,\n zeroline: false,\n title_font: { color: BASELODE_LIGHT.ink, size: 12 },\n tickfont: { color: BASELODE_LIGHT.ink_soft, size: 10 },\n },\n yaxis: {\n showline: true,\n linewidth: 1,\n linecolor: BASELODE_LIGHT.line,\n mirror: false,\n ticks: 'outside',\n tickwidth: 1,\n tickcolor: BASELODE_LIGHT.line,\n ticklen: 4,\n showgrid: true,\n gridcolor: BASELODE_LIGHT.grid,\n gridwidth: 1,\n zeroline: false,\n title_font: { color: BASELODE_LIGHT.ink, size: 12 },\n tickfont: { color: BASELODE_LIGHT.ink_soft, size: 10 },\n },\n modebar: {\n remove: ['select2d', 'lasso2d', 'autoScale2d'],\n },\n bargap: 0.18,\n bargroupgap: 0.08,\n },\n data: {\n scatter: [{\n mode: 'lines+markers',\n line: { width: 2, color: BASELODE_LIGHT.primary },\n marker: {\n size: 7,\n color: BASELODE_LIGHT.primary_2,\n line: { width: 1.5, color: BASELODE_LIGHT.bg },\n },\n }],\n bar: [{\n marker: {\n color: BASELODE_LIGHT.primary,\n line: { color: BASELODE_LIGHT.bg, width: 0 },\n },\n }],\n histogram: [{\n marker: {\n color: BASELODE_LIGHT.primary,\n line: { color: BASELODE_LIGHT.bg, width: 0 },\n },\n }],\n box: [{\n fillcolor: BASELODE_LIGHT.accent,\n line: { color: BASELODE_LIGHT.ink, width: 1.5 },\n marker: { color: BASELODE_LIGHT.ink },\n }],\n violin: [{\n fillcolor: BASELODE_LIGHT.accent,\n line: { color: BASELODE_LIGHT.ink, width: 1.5 },\n marker: { color: BASELODE_LIGHT.ink },\n }],\n heatmap: [{\n colorscale: [\n [0.00, '#ffffff'],\n [0.20, '#f1f5f9'],\n [0.40, '#cbd5e1'],\n [0.60, '#94a3b8'],\n [0.80, '#475569'],\n [1.00, '#1e293b'],\n ],\n colorbar: {\n outlinecolor: BASELODE_LIGHT.line,\n tickcolor: BASELODE_LIGHT.line,\n tickfont: { color: BASELODE_LIGHT.ink_soft },\n },\n }],\n contour: [{\n colorscale: [\n [0.00, '#ffffff'],\n [0.25, '#fef3c7'],\n [0.50, '#f59e0b'],\n [0.75, '#92400e'],\n [1.00, '#1e293b'],\n ],\n colorbar: {\n outlinecolor: BASELODE_LIGHT.line,\n tickcolor: BASELODE_LIGHT.line,\n tickfont: { color: BASELODE_LIGHT.ink_soft },\n },\n }],\n },\n};\n\n/** @type {Object} Alias for {@link BASELODE_TEMPLATE} — the Baselode Light theme. */\nexport const BASELODE_LIGHT_TEMPLATE = BASELODE_TEMPLATE;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Baselode Dark Plotly template.\n *\n * Applies the Baselode Dark visual identity: dark warm backgrounds, Inter\n * typography, subtle warm grid lines, and light ink primary colours accented\n * with the signature highlight yellow.\n *\n * ```js\n * import { BASELODE_DARK_TEMPLATE } from 'baselode';\n * Plotly.react(el, data, { ...layout, template: BASELODE_DARK_TEMPLATE });\n * ```\n */\n\n/** @type {Object} Baselode Dark colour palette. */\nexport const BASELODE_DARK = {\n bg: '#1b1b1f',\n panel: '#25252a',\n ink: '#f0f0e4',\n ink_soft: '#c8c8b8',\n grid: '#2a2a26',\n line: '#3a3a34',\n accent: '#ffffbb',\n accent_2: '#f3ef9b',\n muted_1: '#8a8a80',\n muted_2: '#5e5e56',\n muted_3: '#3a3a34',\n};\n\n/** @type {string} Name key for the Baselode Dark template. */\nexport const BASELODE_DARK_TEMPLATE_NAME = 'baselode-dark';\n\n/**\n * Baselode Dark Plotly template object.\n *\n * Pass directly as the ``template`` property of a Plotly layout to apply\n * the Baselode Dark visual style.\n *\n * @type {Object}\n */\nexport const BASELODE_DARK_TEMPLATE = {\n layout: {\n font: {\n family: 'Inter, Arial, sans-serif',\n color: BASELODE_DARK.ink,\n size: 14,\n },\n title: {\n x: 0.02,\n xanchor: 'left',\n font: {\n family: 'Inter, Arial, sans-serif',\n size: 22,\n color: BASELODE_DARK.ink,\n },\n },\n paper_bgcolor: BASELODE_DARK.bg,\n plot_bgcolor: BASELODE_DARK.bg,\n colorway: [\n BASELODE_DARK.ink,\n BASELODE_DARK.accent,\n BASELODE_DARK.muted_1,\n BASELODE_DARK.accent_2,\n BASELODE_DARK.muted_2,\n BASELODE_DARK.muted_3,\n ],\n margin: { l: 70, r: 30, t: 70, b: 60 },\n hovermode: 'x unified',\n hoverlabel: {\n bgcolor: BASELODE_DARK.panel,\n bordercolor: BASELODE_DARK.accent,\n font: {\n family: 'Inter, Arial, sans-serif',\n color: BASELODE_DARK.ink,\n size: 13,\n },\n },\n legend: {\n bgcolor: 'rgba(37,37,42,0.88)',\n bordercolor: BASELODE_DARK.line,\n borderwidth: 1,\n font: {\n family: 'Inter, Arial, sans-serif',\n color: BASELODE_DARK.ink,\n size: 12,\n },\n orientation: 'h',\n yanchor: 'bottom',\n y: 1.02,\n xanchor: 'left',\n x: 0.0,\n },\n xaxis: {\n showline: false,\n ticks: 'outside',\n tickwidth: 1,\n tickcolor: BASELODE_DARK.muted_1,\n ticklen: 6,\n showgrid: true,\n gridcolor: BASELODE_DARK.grid,\n gridwidth: 1,\n zeroline: false,\n title_font: { color: BASELODE_DARK.ink },\n tickfont: { color: BASELODE_DARK.ink_soft },\n },\n yaxis: {\n showline: false,\n ticks: 'outside',\n tickwidth: 1,\n tickcolor: BASELODE_DARK.muted_1,\n ticklen: 6,\n showgrid: true,\n gridcolor: BASELODE_DARK.grid,\n gridwidth: 1,\n zeroline: false,\n title_font: { color: BASELODE_DARK.ink },\n tickfont: { color: BASELODE_DARK.ink_soft },\n },\n bargap: 0.18,\n bargroupgap: 0.08,\n },\n data: {\n scatter: [{\n mode: 'lines+markers',\n line: { width: 2.5, color: BASELODE_DARK.ink },\n marker: {\n size: 7,\n color: BASELODE_DARK.ink,\n line: { width: 1.5, color: BASELODE_DARK.bg },\n },\n }],\n bar: [{\n marker: {\n color: BASELODE_DARK.ink,\n line: { color: BASELODE_DARK.bg, width: 0 },\n },\n }],\n histogram: [{\n marker: {\n color: BASELODE_DARK.ink,\n line: { color: BASELODE_DARK.bg, width: 0 },\n },\n }],\n box: [{\n fillcolor: BASELODE_DARK.accent,\n line: { color: BASELODE_DARK.ink, width: 1.5 },\n marker: { color: BASELODE_DARK.ink },\n }],\n violin: [{\n fillcolor: BASELODE_DARK.accent,\n line: { color: BASELODE_DARK.ink, width: 1.5 },\n marker: { color: BASELODE_DARK.ink },\n }],\n heatmap: [{\n colorscale: [\n [0.00, '#1b1b1f'],\n [0.20, '#2e2e28'],\n [0.40, '#5e5e50'],\n [0.60, '#c8c89a'],\n [0.80, '#f3ef9b'],\n [1.00, '#ffffbb'],\n ],\n colorbar: {\n outlinecolor: BASELODE_DARK.ink,\n tickcolor: BASELODE_DARK.ink,\n tickfont: { color: BASELODE_DARK.ink_soft },\n },\n }],\n contour: [{\n colorscale: [\n [0.00, '#1b1b1f'],\n [0.25, '#2e2e28'],\n [0.50, '#6b6b50'],\n [0.75, '#f3ef9b'],\n [1.00, '#ffffbb'],\n ],\n colorbar: {\n outlinecolor: BASELODE_DARK.ink,\n tickcolor: BASELODE_DARK.ink,\n tickfont: { color: BASELODE_DARK.ink_soft },\n },\n }],\n },\n};\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Semantic colour mapping system for Baselode graphs.\n *\n * This module provides the **semantic colour mapping** layer of Baselode graph\n * styling. Plotly templates handle overall chart appearance (fonts, layout,\n * backgrounds); this module maps *domain values* (commodities, lithologies,\n * categorical logging data) to specific colours.\n *\n * Built-in maps ship sensible defaults. Users can supply their own\n * dictionaries or override individual entries via {@link resolveColourMap}.\n *\n * @module colourMap\n */\n\n/**\n * Fallback colour used when a value is absent from any colour map.\n * @type {string}\n */\nexport const FALLBACK_COLOUR = '#7f7f7f';\n\n/**\n * Built-in semantic colours for common commodity / assay elements.\n * @type {Object.<string, string>}\n */\nexport const COMMODITY_COLOURS = {\n Au: '#FFD700', // gold\n Ag: '#C0C0C0', // silver\n Cu: '#B87333', // copper / orange-brown\n Fe: '#8B4513', // iron / rusty brown\n Ni: '#4CAF50', // nickel / green\n Zn: '#78909C', // zinc / blue-grey\n Pb: '#607D8B', // lead / slate\n Mo: '#9C27B0', // molybdenite / purple\n Co: '#2196F3', // cobalt / blue\n Li: '#FF5722', // lithium / deep orange\n Mn: '#795548', // manganese / brown\n Cr: '#009688', // chromium / teal\n V: '#673AB7', // vanadium / deep purple\n W: '#FF9800', // tungsten / amber\n Sn: '#9E9E9E', // tin / medium grey\n Ti: '#00BCD4', // titanium / cyan\n Al: '#FFEB3B', // aluminium / yellow\n U: '#8BC34A', // uranium / lime green\n};\n\n/**\n * Built-in semantic colours for common lithology categories.\n * @type {Object.<string, string>}\n */\nexport const LITHOLOGY_COLOURS = {\n // Sedimentary\n shale: '#607D8B',\n mudstone: '#78909C',\n siltstone: '#90A4AE',\n sandstone: '#F5CBA7',\n limestone: '#B0BEC5',\n dolomite: '#CFD8DC',\n conglomerate: '#D7CCC8',\n coal: '#212121',\n // Iron-formation\n BIF: '#8B4513',\n ironstone: '#A0522D',\n // Igneous – intrusive\n granite: '#EF9A9A',\n granodiorite: '#F48FB1',\n diorite: '#CE93D8',\n gabbro: '#546E7A',\n peridotite: '#33691E',\n pegmatite: '#FFF9C4',\n // Igneous – extrusive / volcanic\n basalt: '#37474F',\n andesite: '#78909C',\n rhyolite: '#FFCCBC',\n dacite: '#FFAB91',\n tuff: '#D7CCC8',\n breccia: '#BCAAA4',\n // Metamorphic\n schist: '#80CBC4',\n gneiss: '#4DB6AC',\n quartzite: '#E0F7FA',\n marble: '#F5F5F5',\n slate: '#455A64',\n phyllite: '#80DEEA',\n // Other\n quartz: '#ECEFF1',\n calcite: '#F9FBE7',\n vein: '#FFFFFF',\n unknown: '#9E9E9E',\n};\n\n/**\n * Registry of all built-in colour maps, keyed by lower-case name.\n * @type {Object.<string, Object.<string, string>>}\n */\nexport const BUILTIN_COLOUR_MAPS = {\n commodity: COMMODITY_COLOURS,\n lithology: LITHOLOGY_COLOURS,\n};\n\n/**\n * Return the colour for *value* from *colourMap*, or *fallback* if absent.\n *\n * The lookup is case-insensitive: both *value* and map keys are trimmed and\n * lower-cased before comparison.\n *\n * @param {string} value - Domain value to look up (e.g. `\"Cu\"`, `\"granite\"`).\n * @param {Object.<string, string>} colourMap - Mapping of domain values to CSS colour strings.\n * @param {string} [fallback=FALLBACK_COLOUR] - Colour to return when *value* is not found.\n * @returns {string} A CSS colour string.\n */\nexport function getColour(value, colourMap, fallback = FALLBACK_COLOUR) {\n if (!colourMap || value == null) return fallback;\n const key = String(value).trim();\n // Exact match first\n if (Object.prototype.hasOwnProperty.call(colourMap, key)) return colourMap[key];\n // Case-insensitive match\n const keyLower = key.toLowerCase();\n for (const [mapKey, mapColour] of Object.entries(colourMap)) {\n if (String(mapKey).trim().toLowerCase() === keyLower) return mapColour;\n }\n return fallback;\n}\n\n/**\n * Return a colour map object from a name or pass through a user-supplied object.\n *\n * @param {string|Object.<string, string>|null|undefined} nameOrMap\n * - `null` / `undefined`: returns an empty object.\n * - A `string`: looked up in {@link BUILTIN_COLOUR_MAPS}. Unknown names\n * throw a `RangeError`.\n * - An `object`: returned as-is (user-supplied mapping).\n * @returns {Object.<string, string>} Colour map object.\n * @throws {RangeError} If *nameOrMap* is a string that does not match any built-in map.\n * @throws {TypeError} If *nameOrMap* is not `null`, a string, or a plain object.\n */\nexport function resolveColourMap(nameOrMap) {\n if (nameOrMap == null) return {};\n if (typeof nameOrMap === 'string') {\n const key = nameOrMap.trim().toLowerCase();\n if (Object.prototype.hasOwnProperty.call(BUILTIN_COLOUR_MAPS, key)) {\n return BUILTIN_COLOUR_MAPS[key];\n }\n const available = Object.keys(BUILTIN_COLOUR_MAPS).sort().join(', ');\n throw new RangeError(\n `Unknown built-in colour map '${nameOrMap}'. Available maps: ${available}`\n );\n }\n if (\n typeof nameOrMap === 'object' &&\n Object.getPrototypeOf(nameOrMap) === Object.prototype\n ) return nameOrMap;\n throw new TypeError(\n `colourMap must be null, a string, or a plain object; got ${\n Array.isArray(nameOrMap) ? 'Array' : Object.prototype.toString.call(nameOrMap)\n }`\n );\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n// Shared drillhole 2D visualization helpers for reuse beyond the UI layer.\n// These helpers build Plotly-ready data/layout objects based on interval points.\n\nimport { getColour, resolveColourMap, COMMODITY_COLOURS } from './colourMap.js';\nimport { BASELODE_TEMPLATE } from './baselodeTemplate.js';\n\n/** Default color for numeric line traces */\nexport const NUMERIC_LINE_COLOR = '#8b1e3f';\n\n/** Default color for numeric markers */\nexport const NUMERIC_MARKER_COLOR = '#a8324f';\n\n/**\n * Auto-detect a commodity colour for a column name such as \"Au_ppm\" or \"Cu_eq\".\n * Splits on `_`, `-`, `/`, or whitespace and checks each token against\n * COMMODITY_COLOURS (exact match first, then case-insensitive).\n * Returns null when no commodity element is recognised.\n * @param {string} property\n * @returns {string|null}\n */\nfunction commodityColourForProperty(property) {\n if (!property) return null;\n const tokens = property.split(/[_\\-/\\s]+/);\n for (const token of tokens) {\n if (Object.prototype.hasOwnProperty.call(COMMODITY_COLOURS, token)) {\n return COMMODITY_COLOURS[token];\n }\n const low = token.toLowerCase();\n for (const [key, colour] of Object.entries(COMMODITY_COLOURS)) {\n if (key.toLowerCase() === low) return colour;\n }\n }\n return null;\n}\n\n/** Color for error bars */\nexport const ERROR_COLOR = '#6b7280';\n\n/** Default compact strip-log margins */\nexport const STRIPLOG_COMPACT_MARGIN = { l: 42, r: 4, t: 4, b: 30 };\n\n/** Default strip-log axis tick size */\nexport const STRIPLOG_AXIS_TICK_FONT_SIZE = 10;\n\n/** Default strip-log axis title size */\nexport const STRIPLOG_AXIS_TITLE_FONT_SIZE = 12;\n\nfunction normalizeAxisTitle(t) {\n if (!t) return {};\n return typeof t === 'string' ? { text: t } : t;\n}\n\nfunction applyStriplogLayoutDefaults(layout = {}) {\n const xTitle = normalizeAxisTitle(layout.xaxis && layout.xaxis.title);\n const yTitle = normalizeAxisTitle(layout.yaxis && layout.yaxis.title);\n return {\n ...layout,\n margin: STRIPLOG_COMPACT_MARGIN,\n autosize: true,\n width: undefined,\n xaxis: {\n ...(layout.xaxis || {}),\n tickfont: {\n ...((layout.xaxis && layout.xaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...xTitle,\n font: { ...(xTitle.font || {}), size: STRIPLOG_AXIS_TITLE_FONT_SIZE },\n },\n },\n yaxis: {\n ...(layout.yaxis || {}),\n automargin: true,\n tickfont: {\n ...((layout.yaxis && layout.yaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...yTitle,\n font: { ...(yTitle.font || {}), size: STRIPLOG_AXIS_TITLE_FONT_SIZE },\n },\n },\n };\n}\n\n/**\n * Check if a hole has data for a specific property\n * @param {Object} hole - Hole object with points array\n * @param {string} property - Property name to check\n * @returns {boolean} True if hole has at least one valid value for the property\n */\nexport function holeHasData(hole, property) {\n if (!hole || !property) return false;\n const pts = hole.points || [];\n for (let i = 0; i < pts.length; i += 1) {\n const value = pts[i]?.[property];\n if (value === undefined || value === null) continue;\n if (typeof value === 'number' && Number.isFinite(value)) return true;\n if (typeof value === 'string' && value.trim() !== '') return true;\n }\n return false;\n}\n\n/**\n * Build array of interval points for visualization from hole data\n * Extracts depth intervals and property values, deduplicates, and sorts by depth\n * @param {Object} hole - Hole object with points array\n * @param {string} property - Property name to extract\n * @param {boolean} isCategorical - Whether property is categorical (vs numeric)\n * @returns {Array<{z: number, val: *, from: number, to: number, errorPlus: number, errorMinus: number}>} Array of interval points\n */\nexport function buildIntervalPoints(hole, property, isCategorical) {\n if (!hole || !property) return [];\n const rawPoints = hole?.points || [];\n const out = [];\n const seen = new Set();\n rawPoints.forEach((p) => {\n let fromVal = Number(\n p.from ??\n p.samp_from ??\n p.sample_from ??\n p.fromdepth ??\n p.from_depth ??\n p.depth_from\n );\n let toVal = Number(\n p.to ??\n p.samp_to ??\n p.sample_to ??\n p.todepth ??\n p.to_depth ??\n p.depth_to\n );\n // Fall back to depth for point-schema data (e.g. structural measurements)\n if (!Number.isFinite(fromVal) || !Number.isFinite(toVal)) {\n const depthVal = Number(p.depth ?? p.md);\n if (Number.isFinite(depthVal)) {\n fromVal = depthVal;\n toVal = depthVal;\n }\n }\n const rawVal = p?.[property];\n if (!Number.isFinite(fromVal) || !Number.isFinite(toVal) || toVal < fromVal) return;\n if (rawVal === undefined || rawVal === null || rawVal === '') return;\n if (isCategorical && typeof rawVal === 'string' && /^(nan|null|none)$/i.test(rawVal.trim())) return;\n const key = `${property}:${fromVal}-${toVal}:${String(rawVal)}`;\n if (seen.has(key)) return;\n seen.add(key);\n const mid = (fromVal + toVal) / 2;\n const val = isCategorical ? rawVal : Number(rawVal);\n if (!isCategorical && !Number.isFinite(val)) return;\n out.push({\n z: mid,\n val,\n from: fromVal,\n to: toVal,\n errorPlus: toVal - mid,\n errorMinus: mid - fromVal\n });\n });\n return out.sort((a, b) => b.z - a.z);\n}\n\n/**\n * Build Plotly configuration for categorical property visualization\n * @private\n * @param {Array<Object>} points - Interval points array\n * @param {string} property - Property name for title\n * @param {Object|string|null} [colourMap] - Optional semantic colour map (object or built-in name)\n * @param {Object} [template] - Plotly template to include in layout\n * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildCategoricalConfig(points, property, colourMap, template) {\n if (!points.length) return { data: [], layout: {} };\n const safe = points\n .filter((point) => Number.isFinite(point?.from) && Number.isFinite(point?.to) && point.to > point.from)\n .map((point) => ({ ...point, category: `${point?.val ?? ''}`.trim() }))\n .filter((point) => point.category !== '' && !/^(nan|null|none)$/i.test(point.category))\n .sort((a, b) => a.from - b.from || a.to - b.to);\n\n if (!safe.length) return { data: [], layout: {} };\n\n const resolvedCmap = resolveColourMap(colourMap);\n\n const fallbackPalette = [\n '#1f77b4', // blue\n '#ff7f0e', // orange\n '#2ca02c', // green\n '#d62728', // red\n '#9467bd', // purple\n '#17becf', // cyan\n '#bcbd22', // olive\n '#e377c2', // pink\n '#8c564b', // brown\n '#393b79', // indigo\n '#e6550d', // deep orange\n '#31a354', // deep green\n '#756bb1', // violet\n '#636363', // dark gray\n ];\n const uniqueCategories = [...new Set(safe.map((point) => point.category))];\n\n function pickColour(cat, idx) {\n if (resolvedCmap && Object.keys(resolvedCmap).length > 0) {\n const c = getColour(cat, resolvedCmap, null);\n if (c !== null) return c;\n }\n return fallbackPalette[idx % fallbackPalette.length];\n }\n\n const colorByCategory = new Map(\n uniqueCategories.map((category, idx) => [category, pickColour(category, idx)])\n );\n\n // One bar trace per unique category. Each bar starts at `base` (from depth)\n // and has height (to - from). barmode:'overlay' lets non-overlapping intervals\n // from different traces coexist at the same x position.\n const traces = uniqueCategories.map((cat) => {\n const intervals = safe.filter((seg) => seg.category === cat);\n return {\n type: 'bar',\n x: intervals.map(() => 0.5),\n y: intervals.map((s) => s.to - s.from),\n base: intervals.map((s) => s.from),\n width: 1,\n marker: { color: colorByCategory.get(cat), line: { width: 0 } },\n name: cat,\n showlegend: false,\n customdata: intervals.map((s) => [s.from, s.to]),\n hovertemplate: `${property}: ${cat}<br>from: %{customdata[0]:.3f} to: %{customdata[1]:.3f}<extra></extra>`,\n };\n });\n\n const layout = {\n barmode: 'overlay',\n bargap: 0,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed', zeroline: false },\n showlegend: false,\n title: property || undefined,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data: traces, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Build Plotly configuration for numeric property visualization\n * @private\n * @param {Array<Object>} points - Interval points array\n * @param {string} property - Property name for axis label\n * @param {string} chartType - Chart type ('bar', 'markers', 'line', 'markers+line')\n * @param {string} [color] - Override colour for line/markers (e.g. commodity colour)\n * @param {Object} [template] - Plotly template to include in layout\n * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildNumericConfig(points, property, chartType, color, template) {\n if (!points.length) return { data: [], layout: {} };\n const isBar = chartType === 'bar';\n const isMarkersOnly = chartType === 'markers';\n const isLineOnly = chartType === 'line';\n\n const lineColor = color || NUMERIC_LINE_COLOR;\n const markerColor = color || NUMERIC_MARKER_COLOR;\n\n const baseTrace = {\n x: points.map((p) => p.val),\n y: points.map((p) => p.z),\n hovertemplate: `${property}: %{x}<br>from: %{customdata[0]:.3f} to: %{customdata[1]:.3f}<extra></extra>`,\n customdata: points.map((p) => [Math.min(p.from, p.to), Math.max(p.from, p.to)])\n };\n\n const errorConfig = {\n type: 'data',\n symmetric: false,\n array: points.map((p) => p.errorPlus),\n arrayminus: points.map((p) => p.errorMinus),\n thickness: 1.5,\n width: 2,\n color: ERROR_COLOR\n };\n\n const trace = isBar\n ? {\n ...baseTrace,\n type: 'bar',\n orientation: 'h',\n marker: { color: lineColor },\n error_y: errorConfig\n }\n : {\n ...baseTrace,\n type: 'scatter',\n mode: isMarkersOnly ? 'markers' : isLineOnly ? 'lines' : 'lines+markers',\n line: { color: lineColor, width: 2 },\n marker: { size: 7, color: markerColor },\n error_y: isLineOnly ? undefined : errorConfig\n };\n\n const layout = {\n xaxis: { title: property, zeroline: false },\n yaxis: { title: 'Depth (m)', autorange: 'reversed', zeroline: false },\n barmode: 'overlay',\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data: [trace], layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Build complete Plotly configuration for property visualization\n * @param {Object} options - Configuration options\n * @param {Array<Object>} options.points - Interval points to visualize\n * @param {boolean} options.isCategorical - Whether property is categorical\n * @param {string} options.property - Property name\n * @param {string} options.chartType - Chart type ('bar', 'markers', 'line', 'categorical', etc.)\n * @param {Object|string|null} [options.colourMap] - Optional semantic colour map (object or built-in name)\n * @param {Object} [options.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{data: Array, layout: Object}} Complete Plotly configuration\n */\nexport function buildPlotConfig({ points, isCategorical, property, chartType, colourMap, template }) {\n if (!points || !points.length || !property) return { data: [], layout: {} };\n if (isCategorical || chartType === 'categorical') {\n return buildCategoricalConfig(points, property, colourMap, template);\n }\n const colour = commodityColourForProperty(property);\n return buildNumericConfig(points, property, chartType, colour, template);\n}\n\n/**\n * Build a categorical strip-log Plotly config directly from interval rows.\n * @param {Array<Object>} rows - Interval rows (e.g. geology)\n * @param {Object} options - Field mapping options\n * @param {string} options.fromCol - From-depth column\n * @param {string} options.toCol - To-depth column\n * @param {string} options.categoryCol - Category label column\n * @param {Object|string|null} [options.colourMap] - Optional semantic colour map (object or built-in name)\n * @param {Object} [options.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{data: Array, layout: Object}} Plotly configuration for strip-log rendering\n */\nexport function buildCategoricalStripLogConfig(\n rows = [],\n {\n fromCol = 'from',\n toCol = 'to',\n categoryCol = 'geology_code',\n colourMap = null,\n template = undefined,\n } = {}\n) {\n const points = [];\n rows.forEach((row) => {\n const from = Number(row?.[fromCol]);\n const to = Number(row?.[toCol]);\n const category = row?.[categoryCol];\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) return;\n if (category === undefined || category === null || `${category}`.trim() === '') return;\n const mid = (from + to) / 2;\n points.push({\n z: mid,\n val: `${category}`,\n from,\n to,\n errorPlus: to - mid,\n errorMinus: mid - from\n });\n });\n\n points.sort((a, b) => b.z - a.z);\n return buildPlotConfig({\n points,\n isCategorical: true,\n property: categoryCol,\n chartType: 'categorical',\n colourMap,\n template,\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Plotly visualization builders for structural measurements.\n *\n * Provides:\n * - buildTadpoleConfig: 1D strip log tadpole plot (dip head + azimuth tail)\n * - buildStructuralStripConfig: categorical interval strip log\n * - buildStrikeDipSymbol: 2D map strike/dip symbol geometry\n */\n\nimport { AZIMUTH, DEPTH, DIP, FROM, TO } from '../data/datamodel.js';\nimport { BASELODE_TEMPLATE } from './baselodeTemplate.js';\n\nconst DEFAULT_PALETTE = [\n '#0f172a', '#1e3a5f', '#7c3aed', '#dc2626', '#16a34a',\n '#d97706', '#0ea5e9', '#db2777', '#65a30d', '#9333ea',\n];\n\nconst STRIPLOG_COMPACT_MARGIN = { l: 42, r: 4, t: 4, b: 30 };\nconst STRIPLOG_AXIS_TICK_FONT_SIZE = 10;\nconst STRIPLOG_AXIS_TITLE_FONT_SIZE = 12;\n\nfunction applyStriplogLayoutDefaults(layout = {}) {\n return {\n ...layout,\n margin: STRIPLOG_COMPACT_MARGIN,\n autosize: true,\n width: undefined,\n xaxis: {\n ...(layout.xaxis || {}),\n tickfont: {\n ...((layout.xaxis && layout.xaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...((layout.xaxis && layout.xaxis.title) || {}),\n font: {\n ...(((layout.xaxis && layout.xaxis.title && layout.xaxis.title.font) || {})),\n size: STRIPLOG_AXIS_TITLE_FONT_SIZE,\n },\n },\n },\n yaxis: {\n ...(layout.yaxis || {}),\n automargin: true,\n tickfont: {\n ...((layout.yaxis && layout.yaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...((layout.yaxis && layout.yaxis.title) || {}),\n font: {\n ...(((layout.yaxis && layout.yaxis.title && layout.yaxis.title.font) || {})),\n size: STRIPLOG_AXIS_TITLE_FONT_SIZE,\n },\n },\n },\n };\n}\n\n/**\n * Build a Plotly tadpole log config for structural point measurements.\n *\n * Each measurement renders as a circle (head) at its depth with a tail\n * pointing toward the dip direction. Tail length is proportional to dip magnitude.\n *\n * @param {Array<Object>} points - Structural point rows\n * @param {Object} opts\n * @param {number} [opts.tailScale=0.3] - Controls tail length relative to dip magnitude\n * @param {string|null} [opts.colorBy=null] - Column name to color heads by (e.g. 'defect')\n * @param {string[]} [opts.palette] - Color palette\n * @param {string} [opts.depthCol='depth'] - Column for measured depth\n * @param {string} [opts.dipCol='dip'] - Column for dip angle\n * @param {string} [opts.azCol='azimuth'] - Column for dip direction\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildTadpoleConfig(points, {\n tailScale = 5,\n colorBy = null,\n palette = DEFAULT_PALETTE,\n depthCol = DEPTH,\n dipCol = DIP,\n azCol = AZIMUTH,\n template = undefined,\n} = {}) {\n const valid = points.filter(p =>\n p[depthCol] != null && p[dipCol] != null && p[azCol] != null\n );\n\n if (!valid.length) {\n return { data: [], layout: {} };\n }\n\n // Build color map for categories\n const colorMap = {};\n if (colorBy) {\n const categories = [...new Set(valid.map(p => p[colorBy]).filter(v => v != null))].sort();\n categories.forEach((cat, i) => { colorMap[cat] = palette[i % palette.length]; });\n }\n\n // Group by category for legend traces\n const byCat = new Map();\n const shapes = [];\n\n for (const p of valid) {\n const depth = Number(p[depthCol]);\n const dip = Number(p[dipCol]);\n const az = Number(p[azCol]);\n const cat = colorBy ? (p[colorBy] ?? '_default') : '_default';\n const color = colorBy ? (colorMap[cat] ?? '#0f172a') : '#0f172a';\n\n if (!byCat.has(cat)) {\n byCat.set(cat, { xs: [], ys: [], dips: [], azs: [], color });\n }\n const group = byCat.get(cat);\n // Head positioned at x=dip (degrees)\n group.xs.push(dip);\n group.ys.push(depth);\n group.dips.push(dip);\n group.azs.push(az);\n\n // Tail: starts at (dip, depth), direction encodes azimuth.\n // Length scales with dip magnitude (in degree units on the x-axis).\n const azRad = (az * Math.PI) / 180;\n const length = tailScale * (Math.abs(dip) / 90);\n const dx = Math.sin(azRad) * length; // x-component (degrees)\n const dy = Math.cos(azRad) * length; // y-component (degrees, visual only)\n\n shapes.push({\n type: 'line',\n x0: dip, y0: depth,\n x1: dip + dx, y1: depth + dy,\n line: { color, width: 2 },\n });\n }\n\n const data = [];\n const showLegend = colorBy && byCat.size > 1;\n\n for (const [cat, group] of byCat.entries()) {\n data.push({\n type: 'scatter',\n x: group.xs,\n y: group.ys,\n mode: 'markers',\n name: cat !== '_default' ? String(cat) : undefined,\n marker: { size: 8, color: group.color },\n showlegend: showLegend && cat !== '_default',\n customdata: group.dips.map((d, i) => [d, group.azs[i]]),\n hovertemplate: 'Depth: %{y}<br>Dip: %{customdata[0]}<br>Az: %{customdata[1]}<extra></extra>',\n });\n }\n\n const layout = {\n shapes,\n height: 400,\n margin: { l: 40, r: 10, t: 10, b: 40 },\n xaxis: {\n title: 'Dip (°)',\n autorange: true,\n fixedrange: true,\n zeroline: true,\n tickvals: [-90, -60, -30, 0, 30, 60, 90],\n },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: !!showLegend,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout };\n}\n\n/**\n * Build a Plotly categorical strip log config for structural interval measurements.\n *\n * @param {Array<Object>} intervals - Structural interval rows\n * @param {Object} opts\n * @param {string} [opts.labelCol='defect'] - Column for interval label/color\n * @param {string[]} [opts.palette] - Color palette\n * @param {string} [opts.fromCol='from'] - From depth column\n * @param {string} [opts.toCol='to'] - To depth column\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildStructuralStripConfig(intervals, {\n labelCol = 'structure_type',\n palette = DEFAULT_PALETTE,\n fromCol = FROM,\n toCol = TO,\n template = undefined,\n} = {}) {\n const records = intervals\n .filter(iv => iv[fromCol] != null && iv[toCol] != null && Number(iv[toCol]) > Number(iv[fromCol]))\n .filter(iv => {\n const lv = iv[labelCol];\n if (lv == null) return false;\n const s = String(lv).trim();\n return s !== '' && !/^(nan|null|none)$/i.test(s);\n })\n .map(iv => ({ from: Number(iv[fromCol]), to: Number(iv[toCol]), label: String(iv[labelCol]).trim() }))\n .sort((a, b) => a.from - b.from);\n\n if (!records.length) {\n return { data: [], layout: {} };\n }\n\n const shapes = [];\n const textY = [];\n const texts = [];\n\n records.forEach((rec, idx) => {\n shapes.push({\n type: 'rect',\n xref: 'x', yref: 'y',\n x0: 0, x1: 1,\n y0: rec.from, y1: rec.to,\n fillcolor: palette[idx % palette.length],\n line: { width: 0 },\n layer: 'below',\n });\n textY.push(0.5 * (rec.from + rec.to));\n texts.push(rec.label);\n });\n\n const data = [{\n type: 'scatter',\n x: Array(texts.length).fill(0.5),\n y: textY,\n mode: 'text',\n text: texts,\n textposition: 'middle center',\n showlegend: false,\n hoverinfo: 'text',\n }];\n\n const layout = {\n shapes,\n height: 400,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Word-wrap text at word boundaries, inserting Plotly HTML line breaks.\n * @private\n * @param {string} text\n * @param {number} charsPerLine\n * @returns {string}\n */\nfunction wrapComment(text, charsPerLine) {\n if (!text) return '';\n const words = String(text).trim().split(/\\s+/);\n const lines = [];\n let current = '';\n for (const word of words) {\n if (current && current.length + 1 + word.length > charsPerLine) {\n lines.push(current);\n current = word;\n } else {\n current = current ? `${current} ${word}` : word;\n }\n }\n if (current) lines.push(current);\n return lines.join('<br>');\n}\n\n/**\n * Build a Plotly comments log config — depth intervals with text annotations overlaid.\n *\n * Each interval is drawn as a lightly shaded rectangle spanning its from/to depth.\n * Non-empty comments are word-wrapped and centered inside the rectangle.\n * Intervals with no comment show a thin border only.\n *\n * @param {Array<Object>} intervals - Interval rows (must have from, to, and a comment column)\n * @param {Object} opts\n * @param {string} [opts.commentCol='comments'] - Column containing comment text\n * @param {string} [opts.fromCol='from'] - From depth column\n * @param {string} [opts.toCol='to'] - To depth column\n * @param {string} [opts.bgColor='#f1f5f9'] - Fill color for intervals with a comment\n * @param {string} [opts.borderColor='#cbd5e1'] - Rectangle border color\n * @param {string} [opts.textColor='#1e293b'] - Comment text color\n * @param {number} [opts.charsPerLine=18] - Characters before word-wrapping\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildCommentsConfig(intervals, {\n commentCol = 'comments',\n fromCol = FROM,\n toCol = TO,\n bgColor = '#f1f5f9',\n borderColor = '#cbd5e1',\n textColor = '#1e293b',\n charsPerLine = 18,\n template = undefined,\n} = {}) {\n const records = intervals\n .filter(iv => iv[fromCol] != null && iv[toCol] != null && Number(iv[toCol]) > Number(iv[fromCol]))\n .map(iv => {\n const raw = iv[commentCol];\n const comment = (raw != null && String(raw).trim() !== '' && String(raw) !== 'null')\n ? String(raw).trim()\n : '';\n return { from: Number(iv[fromCol]), to: Number(iv[toCol]), comment };\n })\n .sort((a, b) => a.from - b.from);\n\n if (!records.length) {\n return { data: [], layout: {} };\n }\n\n const shapes = [];\n const textXs = [];\n const textYs = [];\n const texts = [];\n const hovers = [];\n\n for (const rec of records) {\n const mid = 0.5 * (rec.from + rec.to);\n const hasComment = !!rec.comment;\n\n shapes.push({\n type: 'rect',\n xref: 'x', yref: 'y',\n x0: 0, x1: 1,\n y0: rec.from, y1: rec.to,\n fillcolor: hasComment ? bgColor : 'rgba(0,0,0,0)',\n line: { color: borderColor, width: 1 },\n layer: 'below',\n });\n\n if (hasComment) {\n textXs.push(0.5);\n textYs.push(mid);\n texts.push(wrapComment(rec.comment, charsPerLine));\n hovers.push(`${rec.from.toFixed(3)}–${rec.to.toFixed(3)} m<br>${wrapComment(rec.comment, 40)}`);\n }\n }\n\n const data = textXs.length ? [{\n type: 'scatter',\n x: textXs,\n y: textYs,\n mode: 'text',\n text: texts,\n textposition: 'middle center',\n textfont: { color: textColor, size: 10 },\n hovertext: hovers,\n hoverinfo: 'text',\n showlegend: false,\n }] : [];\n\n const layout = {\n shapes,\n height: 400,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Compute 2D map strike/dip symbol geometry for a single structural measurement.\n *\n * Returns the geometry needed to draw a strike line and dip tick on a map.\n *\n * @param {Object} point - Structural measurement with x, y, dip, azimuth\n * @param {Object} opts\n * @param {number} [opts.symbolSize=10] - Strike line half-length in map units\n * @param {string} [opts.xCol='easting'] - X coordinate column\n * @param {string} [opts.yCol='northing'] - Y coordinate column\n * @returns {{ strike: number, dipValue: number, x: number, y: number,\n * strikeX0: number, strikeY0: number, strikeX1: number, strikeY1: number,\n * tickX1: number, tickY1: number } | null}\n */\nexport function buildStrikeDipSymbol(point, {\n symbolSize = 10,\n xCol = 'easting',\n yCol = 'northing',\n} = {}) {\n const x = point[xCol] != null ? Number(point[xCol]) : null;\n const y = point[yCol] != null ? Number(point[yCol]) : null;\n const dip = point[DIP] != null ? Number(point[DIP]) : null;\n const az = point[AZIMUTH] != null ? Number(point[AZIMUTH]) : null;\n\n if (x === null || y === null || dip === null || az === null) return null;\n\n const strike = ((az - 90) + 360) % 360;\n const strikeRad = (strike * Math.PI) / 180;\n const azRad = (az * Math.PI) / 180;\n\n // Strike line half-endpoints\n const dxS = symbolSize * Math.sin(strikeRad);\n const dyS = symbolSize * Math.cos(strikeRad);\n\n // Dip tick from center (length scaled by dip magnitude)\n const tickLen = symbolSize * 0.4 * (dip / 90);\n const dxD = tickLen * Math.sin(azRad);\n const dyD = tickLen * Math.cos(azRad);\n\n return {\n strike,\n dipValue: dip,\n x,\n y,\n strikeX0: x - dxS,\n strikeY0: y - dyS,\n strikeX1: x + dxS,\n strikeY1: y + dyS,\n tickX1: x + dxD,\n tickY1: y + dyD,\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useRef, useState } from 'react';\nimport Plotly from 'plotly.js-dist-min';\nimport { buildPlotConfig } from './drillholeViz.js';\nimport { buildCommentsConfig, buildTadpoleConfig } from './structuralViz.js';\nimport { getChartOptions, DISPLAY_COMMENT, DISPLAY_CATEGORICAL, DISPLAY_NUMERIC, DISPLAY_TADPOLE } from '../data/columnMeta.js';\n\nconst DEFAULT_NUMERIC_CHART_TYPE = 'markers+line';\n\n/**\n * Resolve chart type from available options.\n * @private\n */\nfunction resolveChartType(displayType, requestedChartType) {\n const chartOptions = getChartOptions(displayType);\n if (chartOptions.some((opt) => opt.value === requestedChartType)) return requestedChartType;\n return chartOptions[0]?.value || DEFAULT_NUMERIC_CHART_TYPE;\n}\n\n/**\n * Plotly-based trace plot component for drillhole data.\n * Renders 1D strip logs with chart type options driven by column display type.\n *\n * @param {Object} props\n * @param {Object} props.config - Plot configuration {holeId, property, chartType}\n * @param {Object} props.graph - Graph data {hole, points, displayType, isCategorical, isComment, loading}\n * @param {Array} props.holeOptions - Available holes for dropdown\n * @param {Array} props.propertyOptions - Available properties for dropdown\n * @param {Function} props.onConfigChange - Handler for configuration changes\n * @param {Object} [props.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {JSX.Element}\n */\nfunction TracePlot({ config, graph, holeOptions = [], propertyOptions = [], onConfigChange, template }) {\n const containerRef = useRef(null);\n const hole = graph?.hole;\n const points = graph?.points || [];\n const property = config?.property || '';\n const chartType = config?.chartType || DEFAULT_NUMERIC_CHART_TYPE;\n const selectedHoleId = config?.holeId || '';\n\n // Derive display type from graph metadata (set by useDrillholeTraceGrid)\n const displayType = graph?.displayType\n || (graph?.isComment ? DISPLAY_COMMENT : (graph?.isCategorical ? DISPLAY_CATEGORICAL : DISPLAY_NUMERIC));\n\n const chartOptions = getChartOptions(displayType);\n const effectiveChartType = resolveChartType(displayType, chartType);\n\n const [renderError, setRenderError] = useState('');\n\n useEffect(() => {\n const isComment = displayType === DISPLAY_COMMENT;\n const isTadpole = displayType === DISPLAY_TADPOLE;\n if (!hole || !property) return;\n // For comment type, allow empty points (empty intervals still draw border boxes)\n // For tadpole, points are raw hole points — allow empty array through so buildTadpoleConfig can return empty gracefully\n if (!isComment && !isTadpole && points.length === 0) return;\n const target = containerRef.current;\n if (!target) return;\n\n let plotData;\n try {\n if (isComment) {\n plotData = buildCommentsConfig(points, { commentCol: property, fromCol: 'from', toCol: 'to' });\n } else if (isTadpole) {\n plotData = buildTadpoleConfig(points);\n } else {\n plotData = buildPlotConfig({\n points,\n isCategorical: displayType === DISPLAY_CATEGORICAL,\n property,\n chartType: effectiveChartType,\n template,\n });\n }\n } catch (err) {\n console.error('Plot build error', err);\n setRenderError(err?.message || 'Plot build error');\n return;\n }\n\n if (!plotData?.data || plotData.data.length === 0) {\n if (!isComment) return;\n }\n\n const plotConfig = {\n displayModeBar: true,\n responsive: true,\n useResizeHandler: true,\n modeBarButtonsToRemove: ['select2d', 'lasso2d', 'autoScale2d']\n };\n\n try {\n setRenderError('');\n Plotly.react(target, plotData.data, plotData.layout, plotConfig);\n requestAnimationFrame(() => {\n if (target && target.parentElement) {\n Plotly.Plots.resize(target);\n }\n });\n } catch (err) {\n console.error('Plot render error', err);\n setRenderError(err?.message || 'Plot render error');\n }\n\n return () => {\n if (target) {\n try {\n Plotly.purge(target);\n } catch (err) {\n console.warn('Plot purge error', err);\n }\n }\n };\n }, [hole, property, effectiveChartType, displayType, points, template]);\n\n useEffect(() => {\n const target = containerRef.current;\n if (!target || typeof ResizeObserver === 'undefined') return undefined;\n const resizeObserver = new ResizeObserver(() => {\n try {\n if (target && target.data) {\n Plotly.Plots.resize(target);\n }\n } catch (err) {\n console.warn('Plot resize error', err);\n }\n });\n resizeObserver.observe(target);\n return () => resizeObserver.disconnect();\n }, []);\n\n if (!hole || !property) {\n return (\n <div className=\"plot-card empty\">\n <div className=\"placeholder\">{config?.holeId ? (graph?.loading ? `Loading ${config.holeId}...` : 'Select a property') : 'Loading demo data...'}</div>\n </div>\n );\n }\n\n if (displayType !== DISPLAY_COMMENT && displayType !== DISPLAY_TADPOLE && points.length === 0) {\n return (\n <div className=\"plot-card empty\">\n <div className=\"placeholder\">No data</div>\n </div>\n );\n }\n\n if (renderError) {\n return (\n <div className=\"plot-card empty\">\n <div className=\"placeholder\">Plot error: {renderError}</div>\n </div>\n );\n }\n\n return (\n <div className=\"plot-card\">\n <div className=\"plot-title\">\n <select\n className=\"plot-select\"\n value={selectedHoleId}\n onChange={(e) => onConfigChange && onConfigChange({ holeId: e.target.value })}\n >\n {holeOptions.map((h) => {\n const holeId = typeof h === 'string' ? h : h.holeId;\n const label = typeof h === 'string' ? h : h.label || h.holeId;\n return (\n <option key={holeId} value={holeId}>{label}</option>\n );\n })}\n </select>\n </div>\n <div className=\"plot-controls column\">\n {propertyOptions.length > 0 && (\n <select\n className=\"plot-select\"\n value={property}\n onChange={(e) => onConfigChange && onConfigChange({ property: e.target.value })}\n >\n {propertyOptions.map((p) => (\n <option key={p} value={p}>{p}</option>\n ))}\n </select>\n )}\n {chartOptions.length > 1 && (\n <select\n className=\"plot-select\"\n value={effectiveChartType}\n onChange={(e) => onConfigChange && onConfigChange({ chartType: e.target.value })}\n >\n {chartOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n )}\n </div>\n <div className=\"plotly-chart\" ref={containerRef} />\n </div>\n );\n}\n\nexport default TracePlot;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n deriveAssayProps,\n loadAssayHole,\n loadAssayMetadata\n} from '../data/assayDataLoader.js';\nimport {\n buildTraceConfigsForHoleIds,\n coerceChartTypeForProperty,\n reorderHoleIds\n} from '../data/traceGridConfig.js';\nimport { buildIntervalPoints, holeHasData } from './drillholeViz.js';\n\n/**\n * Merge two arrays of hole objects by holeId.\n * For holes that appear in both arrays, their points are concatenated.\n * @private\n */\nfunction mergeHoleSets(primary, extra) {\n if (!extra?.length) return primary;\n const byId = new Map(primary.map((h) => [h.id || h.holeId, { ...h }]));\n for (const eh of extra) {\n const id = eh.id || eh.holeId;\n if (!id) continue;\n if (byId.has(id)) {\n const existing = byId.get(id);\n byId.set(id, { ...existing, points: [...(existing.points || []), ...(eh.points || [])] });\n } else {\n byId.set(id, eh);\n }\n }\n return Array.from(byId.values());\n}\n\n/**\n * Build comment-type interval points from a hole.\n * Unlike buildIntervalPoints, this keeps intervals with empty/null comment values\n * so that buildCommentsConfig can draw the interval border rectangles.\n * @private\n */\nfunction buildCommentPoints(hole, property) {\n if (!hole || !property) return [];\n const seen = new Set();\n const out = [];\n for (const p of hole.points || []) {\n const from = Number(p.from ?? p.samp_from ?? p.depth_from ?? p.from_depth);\n const to = Number(p.to ?? p.samp_to ?? p.depth_to ?? p.to_depth);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const key = `${from}-${to}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ from, to, [property]: p[property] ?? '' });\n }\n return out;\n}\n\n/**\n * React hook for managing a grid of drillhole trace plots.\n * Handles loading assay data, optional extra hole data (e.g. structural intervals),\n * column metadata classification, and trace config coordination.\n *\n * @param {Object} options\n * @param {string} options.initialFocusedHoleId - Initial focused hole ID\n * @param {File|Blob|null} options.sourceFile - Assay data CSV file\n * @param {Array<Object>} options.extraHoles - Pre-parsed extra hole data (e.g. structural)\n * @param {number} options.plotCount - Number of plots in grid (default: 4)\n * @returns {Object} Hook state and actions\n */\nexport default function useDrillholeTraceGrid({\n initialFocusedHoleId = '',\n sourceFile = null,\n extraHoles = [],\n plotCount = 4\n} = {}) {\n const [holes, setHoles] = useState([]);\n const [holeIds, setHoleIds] = useState([]);\n const [numericProps, setNumericProps] = useState([]);\n const [categoricalProps, setCategoricalProps] = useState([]);\n const [commentProps, setCommentProps] = useState([]);\n const [columnMeta, setColumnMeta] = useState({});\n const [defaultProp, setDefaultProp] = useState('');\n const [traceConfigs, setTraceConfigs] = useState([]);\n const [error, setError] = useState('');\n const [focusedHoleId, setFocusedHoleId] = useState(initialFocusedHoleId || '');\n const [loadingHoles, setLoadingHoles] = useState([]);\n const loadedSourceFileRef = useRef(null);\n\n const applyAssayState = (state) => {\n if (!state) return;\n setError('');\n setHoles(state.holes || []);\n setHoleIds((state.holes || []).map((h) => ({\n holeId: h.id || h.holeId\n })).filter((h) => h.holeId));\n setNumericProps(state.numericProps || []);\n setCategoricalProps(state.categoricalProps || []);\n setCommentProps(state.commentProps || []);\n setColumnMeta(state.columnMeta || {});\n setDefaultProp(state.defaultProp || '');\n setTraceConfigs(state.traceConfigs || []);\n };\n\n // Load metadata (hole IDs) from assay CSV on first mount\n useEffect(() => {\n if (!sourceFile || loadedSourceFileRef.current === sourceFile) return;\n loadedSourceFileRef.current = sourceFile;\n loadAssayMetadata(sourceFile)\n .then((ids) => {\n if (!ids) return;\n const uniqueIds = Array.from(new Map(ids.map((h) => [h.holeId, h])).values());\n setHoleIds(uniqueIds);\n setTraceConfigs(buildTraceConfigsForHoleIds({\n holeIds: uniqueIds.map((h) => h.holeId),\n focusedHoleId,\n plotCount,\n defaultProp: '',\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n }));\n })\n .catch((err) => {\n console.info('Assay metadata load skipped:', err.message);\n });\n }, [sourceFile, focusedHoleId, plotCount, categoricalProps, commentProps]);\n\n // Inject extra holes (structural etc.) into holeIds — always, regardless of whether\n // an assay sourceFile is also present. This ensures structural-only holes appear in\n // the dropdown even when assay data is loaded from a separate file.\n useEffect(() => {\n if (!extraHoles?.length) return;\n const ids = extraHoles\n .map((h) => ({ holeId: h.id || h.holeId }))\n .filter((h) => h.holeId);\n setHoleIds((prev) => {\n const existing = new Set(prev.map((h) => h.holeId));\n const newIds = ids.filter((h) => !existing.has(h.holeId));\n return newIds.length ? [...prev, ...newIds] : prev;\n });\n }, [extraHoles]);\n\n useEffect(() => {\n setError((prev) => (prev && prev.startsWith('Loading ') && prev.includes(' for hole ') ? prev : ''));\n }, [traceConfigs]);\n\n useEffect(() => {\n if (!holeIds.length) {\n setTraceConfigs([]);\n return;\n }\n const orderedHoleIds = reorderHoleIds(holeIds.map((h) => h.holeId), focusedHoleId);\n setTraceConfigs((prev) => {\n const next = Array.from({ length: plotCount }).map((_, idx) => {\n const existing = prev[idx] || {};\n const holeId = holeIds.some((h) => h.holeId === existing.holeId) ? existing.holeId : orderedHoleIds[idx] || holeIds[idx]?.holeId || '';\n const property = existing.property || defaultProp;\n const chartType = coerceChartTypeForProperty({\n property,\n chartType: existing.chartType,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n });\n return { holeId, property, chartType };\n });\n return next;\n });\n }, [holeIds, focusedHoleId, defaultProp, categoricalProps, commentProps, plotCount]);\n\n // Load assay hole data on demand as trace configs change\n useEffect(() => {\n if (!sourceFile) return;\n const needed = traceConfigs.map((cfg) => cfg.holeId).filter(Boolean);\n needed.forEach((holeId) => {\n const already = holes.some((h) => (h.id || h.holeId) === holeId);\n const loading = loadingHoles.includes(holeId);\n if (already || loading) return;\n setLoadingHoles((prev) => [...prev, holeId]);\n loadAssayHole(sourceFile, holeId)\n .then((hole) => {\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n if (!hole) return;\n setHoles((prev) => {\n const merged = mergeHoleSets(\n [...prev.filter((h) => (h.id || h.holeId) !== holeId), hole],\n extraHoles\n );\n const props = deriveAssayProps(merged);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) {\n setDefaultProp(props.defaultProp);\n setTraceConfigs((configs) => configs.map((cfg) => ({\n ...cfg,\n property: cfg.property || props.defaultProp,\n chartType: coerceChartTypeForProperty({\n property: cfg.property || props.defaultProp,\n chartType: cfg.chartType,\n categoricalProps: props.categoricalProps,\n commentProps: props.commentProps,\n numericDefaultChartType: 'markers+line'\n })\n })));\n }\n return merged;\n });\n })\n .catch((err) => {\n console.error(err);\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n setError(err.message || `Error loading hole ${holeId}`);\n });\n });\n }, [traceConfigs, sourceFile, holes, loadingHoles, defaultProp, extraHoles]);\n\n // Merge extra holes whenever they change (and assay holes are present)\n useEffect(() => {\n if (!extraHoles?.length) return;\n setHoles((prev) => {\n if (!prev.length) {\n // No assay data yet — seed from extra holes only\n const props = deriveAssayProps(extraHoles);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) setDefaultProp(props.defaultProp);\n return extraHoles;\n }\n const merged = mergeHoleSets(prev, extraHoles);\n const props = deriveAssayProps(merged);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) setDefaultProp(props.defaultProp);\n return merged;\n });\n }, [extraHoles]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const propertyOptions = useMemo(\n () => [...numericProps, ...categoricalProps, ...commentProps],\n [numericProps, categoricalProps, commentProps]\n );\n\n const labeledHoleOptions = useMemo(\n () => holeIds\n .map((h) => ({ holeId: h.holeId, label: h.holeId }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n [holeIds]\n );\n\n const traceGraphs = useMemo(() => {\n const allProps = [...numericProps, ...categoricalProps, ...commentProps];\n return Array.from({ length: plotCount }).map((_, idx) => {\n const cfg = traceConfigs[idx] || {};\n const hole = holes.find((h) => (h.id || h.holeId) === cfg.holeId) || null;\n\n // Per-hole property list: only columns this hole actually has data for\n const holePropertyOptions = hole\n ? allProps.filter((p) => holeHasData(hole, p))\n : allProps;\n\n let property = cfg.property || defaultProp;\n // Auto-select first available property if the configured one has no data for this hole\n if (hole && !holePropertyOptions.includes(property)) {\n property = holePropertyOptions[0] || property;\n }\n\n const isComment = commentProps.includes(property);\n const isCategorical = !isComment && categoricalProps.includes(property);\n const isTadpole = !isComment && !isCategorical && property === 'dip';\n const displayType = isComment ? 'comment' : isTadpole ? 'tadpole' : (isCategorical ? 'categorical' : 'numeric');\n\n const chartType = isTadpole ? 'tadpole' : cfg.chartType || (isComment ? 'comment' : (isCategorical ? 'categorical' : 'markers+line'));\n const holeId = cfg.holeId || hole?.id || hole?.holeId || '';\n\n const points = isTadpole\n ? (hole?.points || [])\n : isComment\n ? buildCommentPoints(hole, property)\n : buildIntervalPoints(hole, property, isCategorical);\n\n return {\n config: { holeId, property, chartType },\n hole,\n loading: loadingHoles.includes(cfg.holeId),\n isCategorical,\n isComment,\n isTadpole,\n displayType,\n points,\n propertyOptions: holePropertyOptions,\n label: holeId\n };\n });\n }, [traceConfigs, holes, defaultProp, categoricalProps, commentProps, loadingHoles, plotCount, numericProps]);\n\n const handleConfigChange = (index, patch) => {\n setTraceConfigs((prev) => {\n const next = [...prev];\n const base = next[index] || {};\n const merged = { ...base, ...patch };\n if (patch.property) {\n merged.chartType = coerceChartTypeForProperty({\n property: patch.property,\n chartType: merged.chartType,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n });\n }\n next[index] = merged;\n return next;\n });\n };\n\n return {\n error,\n focusedHoleId,\n setFocusedHoleId,\n setError,\n holeCount: holeIds.length,\n numericProps,\n categoricalProps,\n commentProps,\n columnMeta,\n propertyOptions,\n labeledHoleOptions,\n traceGraphs,\n handleConfigChange\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/** Default 10-color palette for assay visualization (blue to red gradient) */\nexport const ASSAY_COLOR_PALETTE_10 = [\n '#313695',\n '#4575b4',\n '#74add1',\n '#abd9e9',\n '#e0f3f8',\n '#fee090',\n '#fdae61',\n '#f46d43',\n '#d73027',\n '#a50026'\n];\n\n/**\n * Build an equal-range color scale from numeric values\n * Uses percentile-based binning for better distribution with outliers\n * @param {Array<number>} values - Array of numeric values to analyze\n * @param {Array<string>} colors - Array of color hex strings to use for bins\n * @returns {{min: number|null, max: number|null, step: number|null, bins: Array<{index: number, min: number, max: number, label: string}>, colors: Array<string>}} Color scale object\n */\nexport function buildEqualRangeColorScale(values = [], colors = ASSAY_COLOR_PALETTE_10) {\n // Filter to finite values and sort\n const finiteValues = values.filter((v) => Number.isFinite(v));\n \n if (!finiteValues.length) {\n return {\n min: null,\n max: null,\n step: null,\n bins: [],\n colors\n };\n }\n\n const sorted = finiteValues.slice().sort((a, b) => a - b);\n const min = sorted[0];\n const max = sorted[sorted.length - 1];\n const binCount = colors.length;\n\n if (max === min) {\n const bins = colors.map((_, index) => ({\n index,\n min,\n max,\n label: `${min}`\n }));\n return {\n min,\n max,\n step: 0,\n bins,\n colors\n };\n }\n\n // Use percentile-based binning for better distribution\n const bins = colors.map((_, index) => {\n const percentileLow = index / binCount;\n const percentileHigh = (index + 1) / binCount;\n const idxLow = Math.floor(percentileLow * sorted.length);\n const idxHigh = Math.min(sorted.length - 1, Math.floor(percentileHigh * sorted.length));\n const lower = sorted[idxLow];\n const upper = index === binCount - 1 ? max : sorted[idxHigh];\n \n return {\n index,\n min: lower,\n max: upper,\n label: formatBinLabel(lower, upper)\n };\n });\n\n const step = (max - min) / binCount;\n\n return {\n min,\n max,\n step,\n bins,\n colors\n };\n}\n\n/**\n * Format bin label with appropriate precision\n * @private\n */\nfunction formatBinLabel(min, max) {\n const formatVal = (v) => {\n if (!Number.isFinite(v)) return 'n/a';\n if (Math.abs(v) >= 1000) return v.toFixed(0);\n if (Math.abs(v) >= 10) return v.toFixed(1);\n if (Math.abs(v) >= 0.1) return v.toFixed(2);\n return v.toFixed(3);\n };\n return `${formatVal(min)} – ${formatVal(max)}`;\n}\n\n/**\n * Get the bin index for a value in a color scale\n * @param {number} value - Value to find bin for\n * @param {Object} scale - Color scale object from buildEqualRangeColorScale\n * @returns {number} Bin index (0 to bins.length-1) or -1 if invalid\n */\nexport function getEqualRangeBinIndex(value, scale) {\n if (!Number.isFinite(value) || !scale || !Array.isArray(scale.bins) || !scale.bins.length) {\n return -1;\n }\n\n if (scale.max === scale.min) {\n return value === scale.min ? 0 : -1;\n }\n\n // Find the bin that contains this value\n for (let i = 0; i < scale.bins.length; i += 1) {\n const bin = scale.bins[i];\n if (value >= bin.min && (value <= bin.max || i === scale.bins.length - 1)) {\n return i;\n }\n }\n\n // Value is out of range\n return -1;\n}\n\n/**\n * Get the color for a value using an equal-range color scale\n * @param {number} value - Value to get color for\n * @param {Object} scale - Color scale object from buildEqualRangeColorScale\n * @param {string} fallbackColor - Color to use if value is invalid or out of range\n * @returns {string} Color hex string\n */\nexport function getEqualRangeColor(value, scale, fallbackColor = '#8b1e3f') {\n const index = getEqualRangeBinIndex(value, scale);\n if (index < 0) return fallbackColor;\n return scale.colors[index] || fallbackColor;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n * @param {*} rows - Value to convert to array\n * @returns {Array} Array\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n * @param {*} value - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Normalize a point to ensure x, y, z are finite numbers\n * @private\n * @param {Object} point - Point object\n * @returns {Object} Normalized point\n */\nfunction normalizePoint(point = {}) {\n return {\n ...point,\n x: toNumber(point.x),\n y: toNumber(point.y),\n z: toNumber(point.z)\n };\n}\n\n/**\n * Project 3D trace points onto a 2D cross-section plane\n * @param {Array<Object>} traces - Array of trace points with x, y, z coordinates\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @returns {Array<Object>} Trace points with added 'along' and 'across' section coordinates\n */\nexport function projectTraceToSection(traces = [], origin = [0, 0], azimuth = 0) {\n const [ox, oy] = origin;\n const azRad = (Number(azimuth) * Math.PI) / 180;\n const cosA = Math.cos(azRad);\n const sinA = Math.sin(azRad);\n\n return toArray(traces)\n .map(normalizePoint)\n .map((row) => {\n if (!Number.isFinite(row.x) || !Number.isFinite(row.y)) return { ...row };\n const dx = row.x - ox;\n const dy = row.y - oy;\n return {\n ...row,\n along: (dx * sinA) + (dy * cosA),\n across: (dx * cosA) - (dy * sinA)\n };\n });\n}\n\n/**\n * Filter trace points to those within a width window around a section line\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width (points within ±width/2 are included)\n * @returns {Array<Object>} Filtered trace points within section window\n */\nexport function sectionWindow(traces = [], origin = [0, 0], azimuth = 0, width = 50) {\n const projected = projectTraceToSection(traces, origin, azimuth);\n const half = 0.5 * Number(width || 0);\n if (!Number.isFinite(half) || half <= 0) return projected;\n return projected.filter((row) => Number.isFinite(row.across) && Math.abs(row.across) <= half);\n}\n\n/**\n * Generate plan view of traces, optionally filtered by depth slice\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>|null} depthSlice - Optional [top, bottom] depth range to filter\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Trace points for plan view\n */\nexport function planView(traces = [], depthSlice = null, colorBy = null) {\n let rows = toArray(traces).map(normalizePoint);\n if (Array.isArray(depthSlice) && depthSlice.length === 2) {\n const [top, bottom] = depthSlice;\n rows = rows.filter((row) => Number.isFinite(row.z) && row.z <= Number(top) && row.z >= Number(bottom));\n }\n if (colorBy) {\n rows = rows.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return rows;\n}\n\n/**\n * Generate cross-section view of traces within a window\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Section trace points with along/across coordinates\n */\nexport function sectionView(traces = [], origin = [0, 0], azimuth = 0, width = 50, colorBy = null) {\n let section = sectionWindow(traces, origin, azimuth, width);\n if (colorBy) {\n section = section.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return section;\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Extract hole ID from a row using common field name variations\n * @private\n */\nfunction getHoleId(row = {}) {\n return row.hole_id ?? row.holeId ?? row.id;\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Convert trace points to line segments grouped by hole ID\n * @param {Array<Object>} traces - Array of trace points with x, y, z, md coordinates\n * @param {string|null} colorBy - Optional property name for per-point color values\n * @returns {Array<{hole_id: string, x: Array<number>, y: Array<number>, z: Array<number>, color: Array|null}>} Array of line segments\n */\nexport function tracesAsSegments(traces = [], colorBy = null) {\n const grouped = new Map();\n\n toArray(traces).forEach((row) => {\n const holeId = getHoleId(row);\n if (holeId === undefined || holeId === null || `${holeId}`.trim() === '') return;\n const key = `${holeId}`;\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(row);\n });\n\n const segments = [];\n grouped.forEach((rows, holeId) => {\n const sorted = [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0));\n const payload = {\n hole_id: holeId,\n x: sorted.map((row) => toNumber(row.x, 0)),\n y: sorted.map((row) => toNumber(row.y, 0)),\n z: sorted.map((row) => toNumber(row.z, 0)),\n color: null\n };\n if (colorBy) {\n payload.color = sorted.map((row) => row?.[colorBy]);\n }\n segments.push(payload);\n });\n\n return segments;\n}\n\n/**\n * Convert interval data to tube/cylinder representations for 3D rendering\n * @param {Array<Object>} intervals - Array of interval objects with from/to depths and x, y, z coordinates\n * @param {number} radius - Tube radius\n * @param {string|null} colorBy - Optional property name for color value\n * @returns {Array<{hole_id: string, x: number, y: number, z: number, from: number, to: number, radius: number, color_value: *}>} Array of tube specifications\n */\nexport function intervalsAsTubes(intervals = [], radius = 1, colorBy = null) {\n return toArray(intervals).map((row) => ({\n hole_id: getHoleId(row),\n from: row?.from,\n to: row?.to,\n radius,\n color: colorBy ? row?.[colorBy] : null,\n value: colorBy ? row?.[colorBy] : null\n }));\n}\n\n/**\n * Extract annotation labels from interval data for 3D text display\n * @param {Array<Object>} intervals - Array of interval objects\n * @param {string|null} labelCol - Property name to use for label text\n * @returns {Array<{hole_id: string, label: string, depth: number}>} Array of annotations with computed mid-depth\n */\nexport function annotationsFromIntervals(intervals = [], labelCol = null) {\n if (!labelCol) return [];\n return toArray(intervals)\n .filter((row) => Object.prototype.hasOwnProperty.call(row || {}, labelCol))\n .map((row) => ({\n hole_id: getHoleId(row),\n label: row?.[labelCol],\n depth: 0.5 * ((toNumber(row?.from, 0)) + (toNumber(row?.to, 0)))\n }));\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Rebuild `sceneCtx.selectables` from all renderable mesh lists.\n * Called after any data-type module adds or removes objects from the scene.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function syncSelectables(sceneCtx) {\n sceneCtx.selectables = [\n ...sceneCtx.blocks,\n ...sceneCtx.drillMeshes,\n ...sceneCtx.structuralMeshes,\n ];\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { AZIMUTH, DIP } from '../data/datamodel.js';\nimport { computeStructuralPositions } from '../data/structuralPositions.js';\nimport { syncSelectables } from './sceneSelectables.js';\n\nconst DEFAULT_COLOR_MAP = {\n bedding: '#2563eb',\n foliation: '#16a34a',\n joint: '#9333ea',\n fault: '#dc2626',\n vein: '#f59e0b',\n 'shear zone': '#0ea5e9',\n 'fault zone': '#ef4444',\n};\n\n/**\n * Resolve a color string for a structure type from a color map.\n * @private\n * @param {string|null} structureType\n * @param {Object|null} colorMap\n * @returns {number} THREE.js hex color integer\n */\nfunction resolveColor(structureType, colorMap) {\n const map = colorMap || DEFAULT_COLOR_MAP;\n const key = (structureType || '').toLowerCase().trim();\n const hex = map[key] || '#888888';\n return new THREE.Color(hex).getHex();\n}\n\n/**\n * Compute a disc plane normal vector from dip and azimuth in ENU coordinates.\n *\n * Convention: azimuth is clockwise from North, dip is measured from horizontal.\n * The normal points upward (positive Z in elevation-positive convention).\n *\n * @param {number} dip - Dip angle in degrees [0, 90]\n * @param {number} azimuth - Dip direction azimuth in degrees [0, 360)\n * @returns {THREE.Vector3} Unit normal vector in ENU coordinates\n */\nexport function dipAzimuthToNormal(dip, azimuth) {\n const dipRad = (dip * Math.PI) / 180;\n const azRad = (azimuth * Math.PI) / 180;\n return new THREE.Vector3(\n Math.sin(azRad) * Math.sin(dipRad), // East component\n Math.cos(azRad) * Math.sin(dipRad), // North component\n Math.cos(dipRad) // Up component\n ).normalize();\n}\n\n/**\n * Build Three.js disc meshes for structural measurements.\n *\n * Each structural measurement with valid 3D coordinates is rendered as a\n * thin cylinder (disc) oriented perpendicular to the plane normal derived\n * from the dip/azimuth values.\n *\n * userData is set on each mesh for hover/picking support.\n *\n * @param {Array<Object>} structures - Each row must have x/easting, y/northing, z/elevation,\n * dip, and azimuth fields.\n * @param {Object} opts\n * @param {number} [opts.radius=5] - Disc radius in scene units\n * @param {number} [opts.discThickness=0.2] - Disc thickness in scene units\n * @param {number} [opts.opacity=0.7] - Material opacity [0, 1]\n * @param {number} [opts.segments=32] - Cylinder radial segments (higher = smoother)\n * @param {Object|null} [opts.colorMap] - Map from defect string to hex color string\n * @returns {THREE.Group} Group containing one Mesh per valid measurement\n */\nexport function buildStructuralDiscs(structures, opts = {}) {\n const {\n radius = 5,\n discThickness = 0.2,\n opacity = 0.7,\n segments = 32,\n colorMap = null,\n } = opts;\n\n const group = new THREE.Group();\n const yAxis = new THREE.Vector3(0, 1, 0);\n\n for (const s of structures) {\n const xVal = s.x != null ? s.x : (s.easting != null ? s.easting : null);\n const yVal = s.y != null ? s.y : (s.northing != null ? s.northing : null);\n const zVal = s.z != null ? s.z : (s.elevation != null ? s.elevation : null);\n\n if (xVal == null || yVal == null || zVal == null) continue;\n if (!Number.isFinite(xVal) || !Number.isFinite(yVal) || !Number.isFinite(zVal)) continue;\n\n const dipVal = s[DIP] != null ? Number(s[DIP]) : null;\n const azVal = s[AZIMUTH] != null ? Number(s[AZIMUTH]) : null;\n\n let normal;\n if (s.nx != null && Number.isFinite(s.nx) && s.ny != null && Number.isFinite(s.ny) && s.nz != null && Number.isFinite(s.nz)) {\n normal = new THREE.Vector3(s.nx, s.ny, s.nz).normalize();\n } else {\n if (dipVal == null || azVal == null || !Number.isFinite(dipVal) || !Number.isFinite(azVal)) continue;\n normal = dipAzimuthToNormal(dipVal, azVal);\n }\n\n const geom = new THREE.CylinderGeometry(radius, radius, discThickness, segments, 1, false);\n const mat = new THREE.MeshStandardMaterial({\n color: resolveColor(s['structure_type'], colorMap),\n transparent: true,\n opacity,\n side: THREE.DoubleSide,\n });\n\n const mesh = new THREE.Mesh(geom, mat);\n mesh.position.set(xVal, yVal, zVal);\n\n // CylinderGeometry default axis is Y; rotate so Y aligns with normal\n mesh.quaternion.setFromUnitVectors(yAxis, normal);\n\n mesh.userData = {\n type: 'structure',\n hole_id: s.hole_id,\n depth: s.depth ?? s.mid,\n structure_type: s['structure_type'],\n dip: dipVal,\n azimuth: azVal,\n comments: s.comments,\n };\n\n group.add(mesh);\n }\n\n return group;\n}\n\n// ---------------------------------------------------------------------------\n// Scene-level functions (operate on a Baselode3DScene instance)\n// ---------------------------------------------------------------------------\n\n/**\n * Compute positions for structural measurements and add disc meshes to the scene.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {Array} structures - Structural measurement rows\n * @param {Array} holes - Desurveyed hole objects with `points`\n * @param {object} [opts]\n * @param {number} [opts.maxDiscs=3000] - Cap on rendered discs for performance\n */\nexport function setStructuralDiscs(sceneCtx, structures, holes, opts = {}) {\n if (!sceneCtx.scene) return;\n clearStructuralDiscs(sceneCtx);\n if (!structures?.length || !holes?.length) return;\n\n const { maxDiscs = 3000 } = opts;\n let input = structures;\n if (input.length > maxDiscs) {\n const step = input.length / maxDiscs;\n const sampled = [];\n for (let i = 0; i < maxDiscs; i++) {\n sampled.push(input[Math.floor(i * step)]);\n }\n input = sampled;\n }\n\n const traceRows = holes.flatMap(h => (h.points || []).map(p => ({ ...p, hole_id: h.id })));\n const enriched = computeStructuralPositions(input, traceRows, opts);\n if (!enriched.length) return;\n\n sceneCtx.structuralGroup = buildStructuralDiscs(enriched, opts);\n sceneCtx.scene.add(sceneCtx.structuralGroup);\n sceneCtx.structuralGroup.traverse(child => {\n if (child.isMesh) sceneCtx.structuralMeshes.push(child);\n });\n syncSelectables(sceneCtx);\n}\n\n/**\n * Remove all structural disc meshes from the scene and dispose GPU resources.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function clearStructuralDiscs(sceneCtx) {\n if (sceneCtx.structuralGroup) {\n sceneCtx.scene.remove(sceneCtx.structuralGroup);\n sceneCtx.structuralGroup.traverse(child => {\n if (child.isMesh) {\n child.geometry.dispose();\n child.material.dispose();\n }\n });\n sceneCtx.structuralGroup = null;\n }\n sceneCtx.structuralMeshes = [];\n syncSelectables(sceneCtx);\n}\n\n/**\n * Show or hide the structural discs group.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {boolean} visible\n */\nexport function setStructuralDiscsVisible(sceneCtx, visible) {\n if (sceneCtx.structuralGroup) {\n sceneCtx.structuralGroup.visible = Boolean(visible);\n }\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\n\nlet _overlayIdCounter = 0;\n\n/**\n * Normalize caller-supplied bounds to the canonical { minX, minY, maxX, maxY } form.\n * Accepts either { minX, minY, maxX, maxY } or { x, y, width, height }.\n *\n * @param {object} bounds\n * @returns {{ minX: number, minY: number, maxX: number, maxY: number }}\n * @throws {Error} if the resulting width or height is zero or negative\n */\nexport function normalizeBounds(bounds) {\n let minX, minY, maxX, maxY;\n\n if ('width' in bounds || 'height' in bounds || ('x' in bounds && !('maxX' in bounds))) {\n const x = Number(bounds.x ?? 0);\n const y = Number(bounds.y ?? 0);\n const width = Number(bounds.width ?? 0);\n const height = Number(bounds.height ?? 0);\n minX = x;\n minY = y;\n maxX = x + width;\n maxY = y + height;\n } else {\n minX = Number(bounds.minX);\n minY = Number(bounds.minY);\n maxX = Number(bounds.maxX);\n maxY = Number(bounds.maxY);\n }\n\n if (maxX - minX <= 0) {\n throw new Error(\n `Invalid raster bounds: width must be positive (got minX=${minX}, maxX=${maxX})`\n );\n }\n if (maxY - minY <= 0) {\n throw new Error(\n `Invalid raster bounds: height must be positive (got minY=${minY}, maxY=${maxY})`\n );\n }\n\n return { minX, minY, maxX, maxY };\n}\n\n/**\n * Load a THREE.Texture from a source descriptor.\n *\n * Supported source types:\n * - `{ type: 'url', url: string }` – load from a URL or data URI\n * - `{ type: 'file', file: File }` – load from a browser File object\n * - `{ type: 'texture', texture: THREE.Texture }` – use a pre-built texture\n *\n * @param {{ type: string, url?: string, file?: File, texture?: THREE.Texture }} source\n * @returns {Promise<THREE.Texture>}\n */\nfunction loadTexture(source) {\n if (source.type === 'texture') {\n return Promise.resolve(source.texture);\n }\n\n let url;\n let createdObjectUrl = false;\n\n if (source.type === 'url') {\n url = source.url;\n } else if (source.type === 'file') {\n url = URL.createObjectURL(source.file);\n createdObjectUrl = true;\n } else {\n return Promise.reject(\n new Error(`Unsupported raster source type: \"${source.type}\"`)\n );\n }\n\n return new Promise((resolve, reject) => {\n const loader = new THREE.TextureLoader();\n loader.load(\n url,\n (texture) => {\n if (createdObjectUrl) URL.revokeObjectURL(url);\n resolve(texture);\n },\n undefined,\n (err) => {\n if (createdObjectUrl) URL.revokeObjectURL(url);\n reject(\n new Error(\n `Failed to load raster texture from \"${url}\": ${err?.message ?? err}`\n )\n );\n }\n );\n });\n}\n\n/**\n * Create a raster overlay layer from the supplied options.\n *\n * The overlay is returned as a self-contained layer descriptor object. Add it\n * to a scene with addRasterOverlay().\n *\n * Placement: the image is rendered as a flat plane on the X/Y axis at the\n * given elevation. The left edge aligns with minX, the right with maxX, the\n * bottom with minY, and the top with maxY. Image orientation follows\n * THREE.js default texture behaviour (flipY=true), so north-up map images\n * are placed correctly without any additional rotation.\n *\n * @param {object} options\n * @param {string} [options.id] - Unique identifier; auto-generated if omitted\n * @param {string} [options.name] - Human-readable display name\n * @param {{ type: string }} options.source - Image source descriptor\n * @param {object} options.bounds - Placement bounds\n * @param {number} [options.elevation=0] - Z position in scene units\n * @param {number} [options.opacity=1] - Initial opacity [0, 1]; clamped if out of range\n * @param {boolean} [options.visible=true] - Initial visibility\n * @param {number} [options.renderOrder=0] - THREE.js renderOrder for draw-order control\n * @returns {Promise<object>} Raster overlay layer descriptor\n */\nexport async function createRasterOverlay(options) {\n const { source, bounds, elevation = 0, visible = true, renderOrder = 0 } = options;\n\n const id = options.id ?? `raster-overlay-${++_overlayIdCounter}`;\n const name = options.name ?? id;\n\n let opacity = options.opacity ?? 1;\n if (opacity < 0 || opacity > 1) {\n console.warn(\n `[baselode] raster overlay \"${id}\": opacity ${opacity} is outside [0, 1] — clamped`\n );\n opacity = Math.max(0, Math.min(1, opacity));\n }\n\n if (!source) throw new Error('raster overlay: options.source is required');\n if (!bounds) throw new Error('raster overlay: options.bounds is required');\n\n const normalizedBounds = normalizeBounds(bounds);\n const { minX, minY, maxX, maxY } = normalizedBounds;\n\n const width = maxX - minX;\n const height = maxY - minY;\n const centerX = (minX + maxX) / 2;\n const centerY = (minY + maxY) / 2;\n\n const texture = await loadTexture(source);\n\n // PlaneGeometry lies in the XY plane by default. THREE.js TextureLoader sets\n // flipY=true, which means the image top-row maps to world maxY and the\n // image bottom-row maps to world minY — correct for north-up map images.\n const geometry = new THREE.PlaneGeometry(width, height);\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n opacity,\n side: THREE.DoubleSide,\n depthWrite: false,\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.position.set(centerX, centerY, elevation);\n mesh.renderOrder = renderOrder;\n mesh.visible = visible;\n\n return { id, name, mesh, texture, bounds: normalizedBounds, elevation, opacity, visible };\n}\n\n// ---------------------------------------------------------------------------\n// Scene-level helpers — all operate on a Baselode3DScene instance\n// ---------------------------------------------------------------------------\n\n/**\n * Add a raster overlay layer to the scene.\n *\n * The layer must have been created with createRasterOverlay(). Duplicate ids\n * replace the existing entry (the old mesh is removed and disposed first).\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {object} layer - Layer returned by createRasterOverlay()\n */\nexport function addRasterOverlay(sceneCtx, layer) {\n if (!sceneCtx.scene) return;\n if (sceneCtx.rasterOverlays.has(layer.id)) {\n removeRasterOverlay(sceneCtx, layer.id);\n }\n sceneCtx.rasterOverlays.set(layer.id, layer);\n sceneCtx.scene.add(layer.mesh);\n}\n\n/**\n * Remove a raster overlay from the scene and dispose its GPU resources.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id - Overlay id\n */\nexport function removeRasterOverlay(sceneCtx, id) {\n const layer = sceneCtx.rasterOverlays.get(id);\n if (!layer) return;\n sceneCtx.scene?.remove(layer.mesh);\n layer.mesh.geometry.dispose();\n layer.mesh.material.dispose();\n if (layer.texture) layer.texture.dispose();\n sceneCtx.rasterOverlays.delete(id);\n}\n\n/**\n * Set the opacity of a raster overlay at runtime without recreating geometry.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id - Overlay id\n * @param {number} opacity - New opacity [0, 1]; clamped if out of range\n */\nexport function setRasterOverlayOpacity(sceneCtx, id, opacity) {\n const layer = sceneCtx.rasterOverlays.get(id);\n if (!layer) return;\n const clamped = Math.max(0, Math.min(1, Number(opacity)));\n layer.opacity = clamped;\n layer.mesh.material.opacity = clamped;\n layer.mesh.material.needsUpdate = true;\n}\n\n/**\n * Show or hide a raster overlay without destroying it.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id - Overlay id\n * @param {boolean} visible\n */\nexport function setRasterOverlayVisibility(sceneCtx, id, visible) {\n const layer = sceneCtx.rasterOverlays.get(id);\n if (!layer) return;\n layer.visible = Boolean(visible);\n layer.mesh.visible = layer.visible;\n}\n\n/**\n * Update the elevation (Z position) of a raster overlay at runtime.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id - Overlay id\n * @param {number} elevation\n */\nexport function setRasterOverlayElevation(sceneCtx, id, elevation) {\n const layer = sceneCtx.rasterOverlays.get(id);\n if (!layer) return;\n layer.elevation = Number(elevation);\n layer.mesh.position.setZ(layer.elevation);\n}\n\n/**\n * Get a raster overlay by id, or undefined if not found.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id\n * @returns {object|undefined}\n */\nexport function getRasterOverlay(sceneCtx, id) {\n return sceneCtx.rasterOverlays.get(id);\n}\n\n/**\n * Return all raster overlay layers in insertion order.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @returns {object[]}\n */\nexport function listRasterOverlays(sceneCtx) {\n return Array.from(sceneCtx.rasterOverlays.values());\n}\n\n/**\n * Remove all raster overlays from the scene and dispose all GPU resources.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function clearRasterOverlays(sceneCtx) {\n for (const id of [...sceneCtx.rasterOverlays.keys()]) {\n removeRasterOverlay(sceneCtx, id);\n }\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Build a string signature from view state for comparison (to detect changes)\n * @param {Object} viewState - View state object with camera, target, up vectors\n * @returns {string} String signature representing the view state\n */\nexport function buildViewSignature(viewState) {\n if (!viewState) return '';\n const toNum = (v) => Number.isFinite(v) ? v.toFixed(3) : 'nan';\n return [\n toNum(viewState.camera?.x),\n toNum(viewState.camera?.y),\n toNum(viewState.camera?.z),\n toNum(viewState.target?.x),\n toNum(viewState.target?.y),\n toNum(viewState.target?.z),\n toNum(viewState.up?.x),\n toNum(viewState.up?.y),\n toNum(viewState.up?.z)\n ].join('|');\n}\n\n/**\n * Extract current view state from 3D scene state\n * @param {Object} state - Baselode3D scene state with camera and controls\n * @returns {Object|null} View state object or null if state invalid\n */\nexport function getViewState(state) {\n if (!state.camera || !state.controls) return null;\n return {\n camera: {\n x: state.camera.position.x,\n y: state.camera.position.y,\n z: state.camera.position.z\n },\n target: {\n x: state.controls.target.x,\n y: state.controls.target.y,\n z: state.controls.target.z\n },\n up: {\n x: state.camera.up.x,\n y: state.camera.up.y,\n z: state.camera.up.z\n }\n };\n}\n\n/**\n * Apply a view state to the 3D scene camera and controls\n * @param {Object} state - Baselode3D scene state\n * @param {Object} viewState - View state to apply\n * @returns {boolean} True if successfully applied\n */\nexport function setViewState(state, viewState) {\n if (!state.camera || !state.controls || !viewState) return false;\n const camera = viewState.camera || {};\n const target = viewState.target || {};\n const up = viewState.up || {};\n\n const values = [camera.x, camera.y, camera.z, target.x, target.y, target.z, up.x, up.y, up.z];\n if (!values.every(Number.isFinite)) return false;\n\n state.camera.position.set(camera.x, camera.y, camera.z);\n state.controls.target.set(target.x, target.y, target.z);\n state.camera.up.set(up.x, up.y, up.z);\n state.camera.lookAt(target.x, target.y, target.z);\n state.controls.update();\n state._lastViewSignature = buildViewSignature(viewState);\n return true;\n}\n\n/**\n * Emit view change event if view has changed (throttled to 250ms)\n * @param {Object} state - Baselode3D scene state with viewChangeHandler\n */\nexport function emitViewChangeIfNeeded(state) {\n if (!state.viewChangeHandler) return;\n const now = Date.now();\n if (now - state._lastViewEmitMs < 250) return;\n const viewState = getViewState(state);\n if (!viewState) return;\n const signature = buildViewSignature(viewState);\n if (signature === state._lastViewSignature) return;\n state._lastViewSignature = signature;\n state._lastViewEmitMs = now;\n state.viewChangeHandler(viewState);\n}\n\n/**\n * Fit camera to view all content within specified bounds\n * @param {Object} state - Baselode3D scene state\n * @param {Object} bounds - Bounding box {minX, maxX, minY, maxY, minZ, maxZ}\n */\nexport function fitCameraToBounds(state, { minX, maxX, minY, maxY, minZ, maxZ }) {\n const centerX = (minX + maxX) / 2;\n const centerY = (minY + maxY) / 2;\n const centerZ = (minZ + maxZ) / 2;\n const sizeX = maxX - minX;\n const sizeY = maxY - minY;\n const sizeZ = maxZ - minZ;\n const maxDim = Math.max(sizeX, sizeY, sizeZ, 1);\n const distance = maxDim * 2;\n\n state.controls.target.set(centerX, centerY, centerZ);\n state.camera.position.set(centerX + distance, centerY + distance, centerZ + distance);\n state.camera.lookAt(centerX, centerY, centerZ);\n state.controls.update();\n}\n\n/**\n * Recenter camera to origin at specified distance\n * @param {Object} state - Baselode3D scene state\n * @param {number} distance - Distance from origin\n */\nexport function recenterCameraToOrigin(state, distance = 1000) {\n if (!state.camera || !state.controls) return;\n state.controls.target.set(0, 0, 0);\n state.camera.position.set(distance, distance, distance);\n state.camera.lookAt(0, 0, 0);\n state.controls.update();\n}\n\n/**\n * Position camera looking straight down from above\n * @param {Object} state - Baselode3D scene state\n * @param {number} distance - Distance above origin\n */\nexport function lookDown(state, distance = 2000) {\n if (!state.camera || !state.controls) return;\n state.controls.target.set(0, 0, 0);\n state.camera.position.set(0, 0, distance);\n state.camera.up.set(0, 1, 0);\n state.camera.lookAt(0, 0, 0);\n state.controls.update();\n}\n\n/**\n * Pan the camera view by screen-space delta\n * @param {Object} state - Baselode3D scene state\n * @param {number} dx - Horizontal pan delta\n * @param {number} dy - Vertical pan delta\n */\nexport function pan(state, dx = 0, dy = 0) {\n if (!state.controls) return;\n if (typeof state.controls.pan === 'function') {\n state.controls.pan(dx, dy);\n state.controls.update();\n }\n}\n\n/**\n * Zoom camera in or out by scale factor\n * @param {Object} state - Baselode3D scene state\n * @param {number} scale - Scale factor (>1 zooms out, <1 zooms in)\n */\nexport function dolly(state, scale = 1.1) {\n if (!state.controls || typeof state.controls.dollyIn !== 'function' || typeof state.controls.dollyOut !== 'function') return;\n if (scale > 1) {\n state.controls.dollyOut(scale);\n } else {\n state.controls.dollyIn(1 / scale);\n }\n state.controls.update();\n}\n\n/**\n * Focus camera on last computed bounds with optional padding\n * @param {Object} state - Baselode3D scene state with lastBounds property\n * @param {number} padding - Padding multiplier for bounds (1.2 = 20% larger view)\n */\nexport function focusOnLastBounds(state, padding = 1.2) {\n if (!state.lastBounds) return;\n const {\n minX, maxX, minY, maxY, minZ, maxZ\n } = state.lastBounds;\n const sizeX = (maxX - minX) * padding;\n const sizeY = (maxY - minY) * padding;\n const sizeZ = (maxZ - minZ) * padding;\n const centerX = (minX + maxX) / 2;\n const centerY = (minY + maxY) / 2;\n const centerZ = (minZ + maxZ) / 2;\n const maxDim = Math.max(sizeX, sizeY, sizeZ, 1);\n const distance = maxDim * 2;\n state.controls.target.set(centerX, centerY, centerZ);\n state.camera.position.set(centerX + distance, centerY + distance, centerZ + distance);\n state.camera.lookAt(centerX, centerY, centerZ);\n state.controls.update();\n}\n\n/** Minimum and maximum permitted camera FOV in degrees. */\nexport const FOV_MIN_DEG = 1;\nexport const FOV_MAX_DEG = 120;\n\n/**\n * Change the camera field-of-view while keeping the visible scene the same apparent size.\n * Adjusts camera distance so the frustum height at the orbit target is preserved.\n * FOV is clamped to [FOV_MIN_DEG, FOV_MAX_DEG] to avoid numerical issues near 0° or 180°.\n * @param {Object} state - Baselode3D scene state with camera and controls\n * @param {number} fovDeg - Desired FOV in degrees\n * @returns {boolean} True if the FOV was applied, false if state is invalid\n */\nexport function setFov(state, fovDeg) {\n if (!state.camera || !state.controls) return false;\n if (!Number.isFinite(fovDeg)) return false;\n const clampedFov = Math.min(FOV_MAX_DEG, Math.max(FOV_MIN_DEG, fovDeg));\n\n const target = state.controls.target;\n const currentDist = state.camera.position.distanceTo(target);\n const currentFovRad = (state.camera.fov * Math.PI) / 180;\n const frustumHeight = 2 * currentDist * Math.tan(currentFovRad / 2);\n\n const newFovRad = (clampedFov * Math.PI) / 180;\n const newDist = frustumHeight / (2 * Math.tan(newFovRad / 2));\n\n const dir = state.camera.position.clone().sub(target).normalize();\n state.camera.position.copy(target).addScaledVector(dir, newDist);\n state.camera.fov = clampedFov;\n state.camera.updateProjectionMatrix();\n state.controls.update();\n return true;\n}\n\n/**\n * Switch between orbit and fly camera control modes\n * @param {Object} state - Baselode3D scene state with orbit and fly controls\n * @param {string} mode - Control mode ('orbit' or 'fly')\n */\nexport function setControlMode(state, mode = 'orbit') {\n state.controlMode = mode === 'fly' ? 'fly' : 'orbit';\n if (state.controlMode === 'fly') {\n if (state.controls) state.controls.enabled = false;\n if (state.flyControls) state.flyControls.enabled = true;\n } else {\n if (state.flyControls) state.flyControls.enabled = false;\n if (state.controls) {\n state.controls.enabled = true;\n state.camera.getWorldDirection(state._tmpDir);\n const target = state.camera.position.clone().addScaledVector(state._tmpDir, 10);\n state.controls.target.copy(target);\n state.controls.update();\n }\n }\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { buildEqualRangeColorScale, getEqualRangeBinIndex, getEqualRangeColor } from './assayColorScale.js';\nimport { fitCameraToBounds } from './baselode3dCameraControls.js';\nimport { syncSelectables } from './sceneSelectables.js';\n\n/** Default color for low or zero assay values */\nconst LOW_ASSAY_GREY = '#9ca3af';\n\n/**\n * Get measured depth range for a segment between two points\n */\nexport function getMeasuredDepthRange(p1, p2) {\n const md1 = Number(p1?.md);\n const md2 = Number(p2?.md);\n if (!Number.isFinite(md1) || !Number.isFinite(md2)) return null;\n const segStart = Math.min(md1, md2);\n const segEnd = Math.max(md1, md2);\n if (segEnd <= segStart) return null;\n return { segStart, segEnd };\n}\n\n/**\n * Calculate weighted average assay value for a segment overlapping with assay intervals\n */\nexport function getWeightedIntervalValue(assayIntervals, segStart, segEnd) {\n let weightedSum = 0;\n let weightTotal = 0;\n\n for (let i = 0; i < assayIntervals.length; i += 1) {\n const candidate = assayIntervals[i];\n const from = Number(candidate?.from);\n const to = Number(candidate?.to);\n const value = Number(candidate?.value);\n if (!Number.isFinite(from) || !Number.isFinite(to) || !Number.isFinite(value) || to <= from) continue;\n const overlapStart = Math.max(segStart, from);\n const overlapEnd = Math.min(segEnd, to);\n const overlap = overlapEnd - overlapStart;\n if (overlap <= 0) continue;\n weightedSum += value * overlap;\n weightTotal += overlap;\n }\n\n if (weightTotal <= 0) return null;\n const value = weightedSum / weightTotal;\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * Get THREE.Color for an assay value based on color scale\n */\nexport function getAssaySegmentColor(value, assayScale) {\n if (!Number.isFinite(value)) return new THREE.Color(LOW_ASSAY_GREY);\n const binIndex = getEqualRangeBinIndex(value, assayScale);\n if (binIndex < 0) return new THREE.Color(LOW_ASSAY_GREY);\n const colorHex = getEqualRangeColor(value, assayScale, LOW_ASSAY_GREY);\n return new THREE.Color(colorHex);\n}\n\n/**\n * Get a deterministic hex color for a categorical value using FNV-1a hash → HSL\n */\nexport function getCategoryHexColor(category) {\n if (!category || !String(category).trim()) return LOW_ASSAY_GREY;\n const h = seededUnit(String(category).toLowerCase().trim());\n return '#' + new THREE.Color().setHSL(h, 0.70, 0.50).getHexString();\n}\n\n/**\n * Normalize drillhole rendering options with defaults\n */\nexport function normalizeDrillholeRenderOptions(options = {}) {\n return {\n preserveView: Boolean(options.preserveView),\n assayIntervalsByHole: options.assayIntervalsByHole || null,\n selectedAssayVariable: options.selectedAssayVariable || '',\n isCategoricalVariable: Boolean(options.isCategoricalVariable),\n };\n}\n\n/**\n * Collect all numeric assay values from interval data\n */\nexport function collectAssayValues(assayIntervalsByHole, selectedAssayVariable) {\n if (!assayIntervalsByHole || !selectedAssayVariable) return [];\n const allAssayValues = [];\n Object.values(assayIntervalsByHole).forEach((intervals) => {\n (intervals || []).forEach((interval) => {\n const value = Number(interval?.value);\n if (Number.isFinite(value)) allAssayValues.push(value);\n });\n });\n return allAssayValues;\n}\n\n/**\n * Build user data object for drillhole mesh\n */\nexport function buildHoleUserData(hole) {\n return {\n holeId: hole.id,\n project: hole.project\n };\n}\n\n/**\n * Normalize hole key to lowercase trimmed string for case-insensitive matching\n */\nexport function normalizeHoleKey(value) {\n return `${value ?? ''}`.trim().toLowerCase();\n}\n\n/**\n * Generate a deterministic per-segment color based on hole ID and segment index\n */\nexport function randomSegmentColor(holeId, segmentIndex) {\n const seed = `${holeId ?? ''}:${segmentIndex ?? 0}`;\n const base = seededUnit(seed);\n const band = ((segmentIndex ?? 0) % 14) / 14;\n const hue = (base * 0.15 + band * 0.85) % 1;\n const color = new THREE.Color();\n color.setHSL(hue, 1.0, 0.5);\n return color;\n}\n\n/**\n * Produce a deterministic float in [0, 1) from an input string (FNV-1a hash)\n */\nexport function seededUnit(input) {\n const text = `${input ?? ''}`;\n let hash = 2166136261;\n for (let i = 0; i < text.length; i += 1) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0) / 4294967295;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers (not exported)\n// ---------------------------------------------------------------------------\n\nfunction getDominantCategory(intervals, segStart, segEnd) {\n let best = null;\n let bestOverlap = 0;\n for (const iv of intervals) {\n const from = Number(iv?.from);\n const to = Number(iv?.to);\n if (!Number.isFinite(from) || !Number.isFinite(to)) continue;\n const overlap = Math.min(segEnd, to) - Math.max(segStart, from);\n if (overlap > bestOverlap) { bestOverlap = overlap; best = iv?.value; }\n }\n return best;\n}\n\nfunction resolveAssayIntervalsForHole(hole, assayIntervalsByHole) {\n if (!assayIntervalsByHole || !hole) return [];\n const holeId = hole.id || hole.holeId;\n if (!holeId) return [];\n\n const exact = assayIntervalsByHole[holeId];\n if (Array.isArray(exact) && exact.length) return exact;\n\n const normalized = normalizeHoleKey(holeId);\n if (normalized) {\n const byNormalized = assayIntervalsByHole[normalized];\n if (Array.isArray(byNormalized) && byNormalized.length) return byNormalized;\n }\n\n return [];\n}\n\nfunction getSegmentColor({ selectedAssayVariable, assayIntervals, assayScale, holeId, segmentIndex, p1, p2, isCategorical }) {\n if (!selectedAssayVariable) {\n return randomSegmentColor(holeId, segmentIndex);\n }\n if (selectedAssayVariable === '__HAS_ASSAY__') {\n if (!assayIntervals?.length) return new THREE.Color(LOW_ASSAY_GREY);\n const depthRange = getMeasuredDepthRange(p1, p2);\n if (!depthRange) return new THREE.Color(LOW_ASSAY_GREY);\n const hasData = assayIntervals.some((interval) => {\n const from = Number(interval?.from);\n const to = Number(interval?.to);\n if (!Number.isFinite(from) || !Number.isFinite(to)) return false;\n const overlapStart = Math.max(depthRange.segStart, from);\n const overlapEnd = Math.min(depthRange.segEnd, to);\n return overlapEnd > overlapStart;\n });\n return hasData ? new THREE.Color('#ff8c42') : new THREE.Color(LOW_ASSAY_GREY);\n }\n if (!assayIntervals?.length) return new THREE.Color(LOW_ASSAY_GREY);\n const depthRange = getMeasuredDepthRange(p1, p2);\n if (!depthRange) return new THREE.Color(LOW_ASSAY_GREY);\n if (isCategorical) {\n const cat = getDominantCategory(assayIntervals, depthRange.segStart, depthRange.segEnd);\n return new THREE.Color(getCategoryHexColor(cat));\n }\n const value = getWeightedIntervalValue(assayIntervals, depthRange.segStart, depthRange.segEnd);\n return getAssaySegmentColor(value, assayScale);\n}\n\n// ---------------------------------------------------------------------------\n// Public scene functions\n// ---------------------------------------------------------------------------\n\n/**\n * Build cylinder meshes for all drillholes and add them to the scene.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {Array} holes - Array of desurveyed hole objects with `points`\n * @param {object} [options]\n */\nexport function setDrillholes(sceneCtx, holes, options = {}) {\n if (!sceneCtx.scene) return;\n\n clearDrillholes(sceneCtx);\n if (!holes || holes.length === 0) return;\n\n const { preserveView, assayIntervalsByHole, selectedAssayVariable, isCategoricalVariable } = normalizeDrillholeRenderOptions(options);\n const allAssayValues = isCategoricalVariable ? [] : collectAssayValues(assayIntervalsByHole, selectedAssayVariable);\n const assayScale = buildEqualRangeColorScale(allAssayValues);\n\n let minX = Infinity, maxX = -Infinity;\n let minY = Infinity, maxY = -Infinity;\n let minZ = Infinity, maxZ = -Infinity;\n\n const tmpVec = new THREE.Vector3();\n const up = new THREE.Vector3(0, 1, 0);\n\n holes.forEach((hole, idx) => {\n const goldenAngle = 137.5;\n const hue = ((idx * goldenAngle) % 360) / 360;\n const defaultColor = new THREE.Color().setHSL(hue, 0.75, 0.55);\n const points = (hole.points || []).map((p) => {\n minX = Math.min(minX, p.x);\n maxX = Math.max(maxX, p.x);\n minY = Math.min(minY, p.y);\n maxY = Math.max(maxY, p.y);\n minZ = Math.min(minZ, p.z);\n maxZ = Math.max(maxZ, p.z);\n const point = new THREE.Vector3(p.x, p.y, p.z);\n point.md = p.md;\n return point;\n });\n\n if (points.length < 2) {\n if (points.length === 1) {\n const sphereGeom = new THREE.SphereGeometry(5, 12, 12);\n const sphereMat = new THREE.MeshLambertMaterial({\n color: defaultColor,\n emissive: defaultColor,\n emissiveIntensity: 0.2\n });\n const sphere = new THREE.Mesh(sphereGeom, sphereMat);\n sphere.position.copy(points[0]);\n sphere.userData = buildHoleUserData(hole);\n sceneCtx.scene.add(sphere);\n sceneCtx.drillLines.push(sphere);\n sceneCtx.drillMeshes.push(sphere);\n }\n return;\n }\n\n const group = new THREE.Group();\n group.userData = buildHoleUserData(hole);\n const assayIntervals = selectedAssayVariable\n ? resolveAssayIntervalsForHole(hole, assayIntervalsByHole)\n : [];\n\n for (let i = 0; i < points.length - 1; i += 1) {\n const p1 = points[i];\n const p2 = points[i + 1];\n const dir = tmpVec.subVectors(p2, p1);\n const len = dir.length();\n if (len <= 0.001) continue;\n const radius = 2.2;\n const cylinderGeom = new THREE.CylinderGeometry(radius, radius, len, 6, 1, true);\n const segmentColor = getSegmentColor({\n selectedAssayVariable,\n assayIntervals,\n assayScale,\n holeId: hole.id,\n segmentIndex: i,\n p1,\n p2,\n isCategorical: isCategoricalVariable,\n });\n const cylinderMat = new THREE.MeshLambertMaterial({\n color: segmentColor,\n flatShading: true,\n emissive: segmentColor,\n emissiveIntensity: 0.15\n });\n const mesh = new THREE.Mesh(cylinderGeom, cylinderMat);\n mesh.position.copy(p1.clone().addScaledVector(dir, 0.5));\n mesh.quaternion.setFromUnitVectors(up, dir.clone().normalize());\n mesh.userData = buildHoleUserData(hole);\n group.add(mesh);\n sceneCtx.drillMeshes.push(mesh);\n }\n\n sceneCtx.scene.add(group);\n sceneCtx.drillLines.push(group);\n });\n\n if (sceneCtx.camera && sceneCtx.controls) {\n sceneCtx.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n if (!preserveView) {\n fitCameraToBounds(sceneCtx, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n }\n syncSelectables(sceneCtx);\n}\n\n/**\n * Remove all drillhole meshes from the scene and dispose GPU resources.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function clearDrillholes(sceneCtx) {\n sceneCtx.drillLines.forEach((line) => {\n sceneCtx.scene.remove(line);\n if (line.isGroup) {\n line.traverse((child) => {\n if (child.isMesh) {\n child.geometry.dispose();\n child.material.dispose();\n }\n });\n } else if (line.isMesh) {\n line.geometry.dispose();\n line.material.dispose();\n }\n });\n sceneCtx.drillLines = [];\n sceneCtx.drillMeshes = [];\n syncSelectables(sceneCtx);\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';\nimport { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';\nimport { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';\n\n/** Glow colour for the selection outline (#ffffbb) */\nconst GLOW_COLOR = '#ffffbb';\n\n/** OutlinePass tuning: produces a soft ~3 px diffuse halo */\nconst EDGE_STRENGTH = 2.0;\nconst EDGE_THICKNESS = 1.5;\nconst EDGE_GLOW = 1.0;\n\n/**\n * Initialise an EffectComposer with a RenderPass and OutlinePass for\n * click-select glow. Attaches `_composer` and `_outlinePass` to sceneCtx.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function initSelectionGlow(sceneCtx) {\n const { renderer, scene, camera, container } = sceneCtx;\n if (!renderer || !scene || !camera) return;\n\n const width = container?.clientWidth || renderer.domElement.clientWidth || 1;\n const height = container?.clientHeight || renderer.domElement.clientHeight || 1;\n\n const composer = new EffectComposer(renderer);\n\n const renderPass = new RenderPass(scene, camera);\n composer.addPass(renderPass);\n\n const resolution = new THREE.Vector2(width, height);\n const outlinePass = new OutlinePass(resolution, scene, camera);\n outlinePass.visibleEdgeColor.set(GLOW_COLOR);\n outlinePass.hiddenEdgeColor.set(GLOW_COLOR);\n outlinePass.edgeStrength = EDGE_STRENGTH;\n outlinePass.edgeThickness = EDGE_THICKNESS;\n outlinePass.edgeGlow = EDGE_GLOW;\n outlinePass.pulsePeriod = 0;\n outlinePass.selectedObjects = [];\n composer.addPass(outlinePass);\n\n composer.setSize(width, height);\n\n sceneCtx._composer = composer;\n sceneCtx._outlinePass = outlinePass;\n}\n\n/**\n * Resize the effect composer and outline pass to match the new viewport\n * dimensions. Call this whenever the renderer is resized.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {number} width - New viewport pixel width\n * @param {number} height - New viewport pixel height\n */\nexport function resizeGlow(sceneCtx, width, height) {\n if (!sceneCtx._composer || !sceneCtx._outlinePass) return;\n sceneCtx._composer.setSize(width, height);\n sceneCtx._outlinePass.resolution.set(width, height);\n}\n\n/**\n * Apply or clear the glow selection highlight.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {THREE.Object3D|null} object - Object to highlight, or null to clear\n */\nexport function applySelection(sceneCtx, object) {\n if (!sceneCtx._outlinePass) return;\n sceneCtx._outlinePass.selectedObjects = object ? [object] : [];\n sceneCtx._selectedObject = object || null;\n}\n\n/**\n * Dispose the effect composer and free all GPU resources associated with the\n * selection glow.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function disposeSelectionGlow(sceneCtx) {\n if (sceneCtx._composer) {\n sceneCtx._composer.dispose();\n sceneCtx._composer = null;\n }\n sceneCtx._outlinePass = null;\n sceneCtx._selectedObject = null;\n sceneCtx.selectables = [];\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { getColorForValue } from '../data/blockModelLoader.js';\nimport { fitCameraToBounds } from './baselode3dCameraControls.js';\nimport { syncSelectables } from './sceneSelectables.js';\n\n// Six face definitions. neibDir locates the neighbour in that direction.\n// verts are ±1 scale factors of the half-extents (dx/2 etc.).\nconst FACE_DEFS = [\n { normal: [ 1, 0, 0], neibDir: [ 1, 0, 0], verts: [[ 1,-1,-1],[ 1, 1,-1],[ 1, 1, 1],[ 1,-1, 1]] },\n { normal: [-1, 0, 0], neibDir: [-1, 0, 0], verts: [[-1,-1, 1],[-1, 1, 1],[-1, 1,-1],[-1,-1,-1]] },\n { normal: [ 0, 1, 0], neibDir: [ 0, 1, 0], verts: [[-1, 1, 1],[ 1, 1, 1],[ 1, 1,-1],[-1, 1,-1]] },\n { normal: [ 0,-1, 0], neibDir: [ 0,-1, 0], verts: [[ 1,-1, 1],[-1,-1, 1],[-1,-1,-1],[ 1,-1,-1]] },\n { normal: [ 0, 0, 1], neibDir: [ 0, 0, 1], verts: [[-1,-1, 1],[ 1,-1, 1],[ 1, 1, 1],[-1, 1, 1]] },\n { normal: [ 0, 0,-1], neibDir: [ 0, 0,-1], verts: [[ 1,-1,-1],[-1,-1,-1],[-1, 1,-1],[ 1, 1,-1]] },\n];\n\n/**\n * Render block model data as a single merged mesh of exterior faces only.\n *\n * Adjacent blocks' shared faces are skipped so there are no coincident\n * polygons and therefore no z-fighting. Vertex colours are used so the\n * entire model is a single draw call.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {Array<Object>} data - Block rows (canonical column names)\n * @param {string} selectedProperty - Attribute column used for colouring\n * @param {Object} stats - Property statistics\n * @param {Object} [options]\n * @param {Object} [options.offset] - Optional {x, y, z} translation\n * @param {number} [options.opacity=1.0] - Initial material opacity (0–1)\n * @param {boolean} [options.autoCenter=true] - Auto-centre blocks at scene origin\n */\nexport function setBlocks(sceneCtx, data, selectedProperty, stats, options = {}) {\n if (!sceneCtx.scene) return;\n\n clearBlocks(sceneCtx);\n\n if (!data || !selectedProperty || !stats) return;\n\n const { autoCenter = true, opacity = 1.0 } = options;\n\n let rawMinX = Infinity, rawMaxX = -Infinity;\n let rawMinY = Infinity, rawMaxY = -Infinity;\n let rawMinZ = Infinity, rawMaxZ = -Infinity;\n\n data.forEach((row) => {\n const x = Number(row.x ?? row.center_x ?? 0);\n const y = Number(row.y ?? row.center_y ?? 0);\n const z = Number(row.z ?? row.center_z ?? 0);\n const dx = Number(row.dx ?? row.size_x ?? 1);\n const dy = Number(row.dy ?? row.size_y ?? 1);\n const dz = Number(row.dz ?? row.size_z ?? 1);\n rawMinX = Math.min(rawMinX, x - dx / 2);\n rawMaxX = Math.max(rawMaxX, x + dx / 2);\n rawMinY = Math.min(rawMinY, y - dy / 2);\n rawMaxY = Math.max(rawMaxY, y + dy / 2);\n rawMinZ = Math.min(rawMinZ, z - dz / 2);\n rawMaxZ = Math.max(rawMaxZ, z + dz / 2);\n });\n\n let offX = 0, offY = 0, offZ = 0;\n if (options.offset) {\n offX = Number(options.offset.x ?? 0);\n offY = Number(options.offset.y ?? 0);\n offZ = Number(options.offset.z ?? 0);\n } else if (autoCenter) {\n offX = -((rawMinX + rawMaxX) / 2);\n offY = -((rawMinY + rawMaxY) / 2);\n offZ = -((rawMinZ + rawMaxZ) / 2);\n }\n\n const minX = rawMinX + offX, maxX = rawMaxX + offX;\n const minY = rawMinY + offY, maxY = rawMaxY + offY;\n const minZ = rawMinZ + offZ, maxZ = rawMaxZ + offZ;\n\n const bkey = (x, y, z) => `${Math.round(x)},${Math.round(y)},${Math.round(z)}`;\n const blockSet = new Set(\n data.map(row => bkey(Number(row.x ?? 0), Number(row.y ?? 0), Number(row.z ?? 0)))\n );\n\n const positions = [];\n const normals = [];\n const colors = [];\n const indices = [];\n const quadToBlock = [];\n let vi = 0;\n\n data.forEach((row) => {\n const bx = Number(row.x ?? row.center_x ?? 0);\n const by = Number(row.y ?? row.center_y ?? 0);\n const bz = Number(row.z ?? row.center_z ?? 0);\n const dx = Number(row.dx ?? row.size_x ?? 1);\n const dy = Number(row.dy ?? row.size_y ?? 1);\n const dz = Number(row.dz ?? row.size_z ?? 1);\n const cx = bx + offX, cy = by + offY, cz = bz + offZ;\n\n const color = getColorForValue(row[selectedProperty], stats, THREE);\n const { r, g, b } = color;\n\n FACE_DEFS.forEach((face) => {\n const nbx = bx + face.neibDir[0] * dx;\n const nby = by + face.neibDir[1] * dy;\n const nbz = bz + face.neibDir[2] * dz;\n if (blockSet.has(bkey(nbx, nby, nbz))) return;\n\n const vBase = vi;\n face.verts.forEach(([sx, sy, sz]) => {\n positions.push(cx + sx * dx / 2, cy + sy * dy / 2, cz + sz * dz / 2);\n normals.push(face.normal[0], face.normal[1], face.normal[2]);\n colors.push(r, g, b);\n vi++;\n });\n indices.push(vBase, vBase + 1, vBase + 2, vBase, vBase + 2, vBase + 3);\n quadToBlock.push(row);\n });\n });\n\n if (positions.length === 0) return;\n\n const geometry = new THREE.BufferGeometry();\n geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));\n geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));\n geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));\n geometry.setIndex(indices);\n\n const material = new THREE.MeshLambertMaterial({\n vertexColors: true,\n transparent: opacity < 1,\n opacity,\n side: THREE.DoubleSide,\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.userData._isMergedBlocks = true;\n mesh.userData._quadToBlock = quadToBlock;\n mesh.userData._offset = { x: offX, y: offY, z: offZ };\n sceneCtx.scene.add(mesh);\n sceneCtx.blocks.push(mesh);\n syncSelectables(sceneCtx);\n\n if (sceneCtx.camera && sceneCtx.controls) {\n sceneCtx.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n fitCameraToBounds(sceneCtx, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n}\n\n/**\n * Remove all block meshes (and the highlight ghost mesh) from the scene.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function clearBlocks(sceneCtx) {\n sceneCtx.blocks.forEach((block) => {\n sceneCtx.scene.remove(block);\n block.geometry.dispose();\n block.material.dispose();\n });\n sceneCtx.blocks = [];\n if (sceneCtx._blockHighlightMesh) {\n sceneCtx.scene?.remove(sceneCtx._blockHighlightMesh);\n sceneCtx._blockHighlightMesh.geometry.dispose();\n sceneCtx._blockHighlightMesh.material.dispose();\n sceneCtx._blockHighlightMesh = null;\n }\n syncSelectables(sceneCtx);\n}\n\n/**\n * Update the opacity of all currently rendered blocks.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {number} opacity - New opacity value between 0 and 1\n */\nexport function setBlockOpacity(sceneCtx, opacity) {\n const clamped = Math.max(0, Math.min(1, Number(opacity)));\n sceneCtx.blocks.forEach((block) => {\n if (block.material) {\n block.material.opacity = clamped;\n block.material.transparent = clamped < 1;\n block.material.needsUpdate = true;\n }\n });\n}\n\n/**\n * Return (creating on first use) an invisible ghost box mesh positioned and\n * scaled to exactly cover one block. The OutlinePass uses its geometry\n * shape to draw the per-block glow; the box itself is invisible to the\n * normal render.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {object} blockRow - Block data row\n * @param {object} offset - Scene offset {x, y, z}\n * @returns {THREE.Mesh}\n */\nexport function getBlockHighlightMesh(sceneCtx, blockRow, offset) {\n const offX = offset?.x ?? 0;\n const offY = offset?.y ?? 0;\n const offZ = offset?.z ?? 0;\n const cx = Number(blockRow.x ?? blockRow.center_x ?? 0) + offX;\n const cy = Number(blockRow.y ?? blockRow.center_y ?? 0) + offY;\n const cz = Number(blockRow.z ?? blockRow.center_z ?? 0) + offZ;\n const dx = Number(blockRow.dx ?? blockRow.size_x ?? 1);\n const dy = Number(blockRow.dy ?? blockRow.size_y ?? 1);\n const dz = Number(blockRow.dz ?? blockRow.size_z ?? 1);\n if (!sceneCtx._blockHighlightMesh) {\n const geom = new THREE.BoxGeometry(1, 1, 1);\n const mat = new THREE.MeshBasicMaterial({ transparent: true, opacity: 0, depthWrite: false });\n sceneCtx._blockHighlightMesh = new THREE.Mesh(geom, mat);\n sceneCtx.scene.add(sceneCtx._blockHighlightMesh);\n }\n sceneCtx._blockHighlightMesh.position.set(cx, cy, cz);\n sceneCtx._blockHighlightMesh.scale.set(dx, dy, dz);\n return sceneCtx._blockHighlightMesh;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { applySelection } from './selectionGlow.js';\nimport { getBlockHighlightMesh } from './blockModelScene.js';\n\n/**\n * Raycast against `selectables` using the current pointer position and apply\n * the glow to the nearest hit object (or clear if nothing is hit).\n * For merged block meshes, redirects the glow to an invisible ghost box\n * sized and positioned to match only the hovered block.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function updateSelectionFromPointer(sceneCtx) {\n if (!sceneCtx._outlinePass || sceneCtx.selectables.length === 0) {\n if (sceneCtx._outlinePass) applySelection(sceneCtx, null);\n return;\n }\n const hits = sceneCtx.raycaster.intersectObjects(sceneCtx.selectables, true);\n if (hits.length === 0) {\n applySelection(sceneCtx, null);\n return;\n }\n const hit = hits[0];\n const obj = hit.object;\n if (obj?.userData?._isMergedBlocks) {\n const quadIndex = Math.floor(hit.faceIndex / 2);\n const blockRow = obj.userData._quadToBlock?.[quadIndex];\n if (blockRow) {\n applySelection(sceneCtx, getBlockHighlightMesh(sceneCtx, blockRow, obj.userData._offset));\n return;\n }\n }\n applySelection(sceneCtx, obj);\n}\n\n/**\n * Register a click listener on the renderer canvas. Handles block →\n * structural → drillhole priority. Stores the listener reference in\n * `sceneCtx.handleCanvasClick` for later removal in `dispose()`.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function attachCanvasClickHandler(sceneCtx) {\n const renderer = sceneCtx.renderer;\n if (!renderer) return;\n\n sceneCtx.handleCanvasClick = (event) => {\n if (event.button !== 0) return; // left click only\n\n // Ignore clicks inside the gizmo area\n if (sceneCtx.gizmo?.domElement) {\n const gizmoRect = sceneCtx.gizmo.domElement.getBoundingClientRect();\n if (\n event.clientX >= gizmoRect.left &&\n event.clientX <= gizmoRect.right &&\n event.clientY >= gizmoRect.top &&\n event.clientY <= gizmoRect.bottom\n ) {\n return;\n }\n }\n\n const rect = renderer.domElement.getBoundingClientRect();\n const localX = event.clientX - rect.left;\n const localY = event.clientY - rect.top;\n\n sceneCtx.pointer.x = ((localX / rect.width) * 2) - 1;\n sceneCtx.pointer.y = -((localY / rect.height) * 2) + 1;\n\n sceneCtx.raycaster.setFromCamera(sceneCtx.pointer, sceneCtx.camera);\n\n // Selection glow: raycast against registered selectables\n updateSelectionFromPointer(sceneCtx);\n\n // Check block clicks first (blocks take priority over drillholes)\n if (sceneCtx.blocks.length > 0) {\n const blockIntersects = sceneCtx.raycaster.intersectObjects(sceneCtx.blocks, false);\n if (blockIntersects.length > 0) {\n const hit = blockIntersects[0];\n const blockObj = hit.object;\n if (blockObj?.userData?._isMergedBlocks && sceneCtx.blockClickHandler) {\n const quadIndex = Math.floor(hit.faceIndex / 2);\n const blockData = blockObj.userData._quadToBlock[quadIndex];\n if (blockData) sceneCtx.blockClickHandler(blockData);\n }\n return;\n }\n }\n\n // Fall through to drillhole / structural click detection\n const drillHits = sceneCtx.raycaster.intersectObjects(sceneCtx.drillMeshes, true);\n const structHits = sceneCtx.raycaster.intersectObjects(sceneCtx.structuralMeshes, true);\n\n const drillDist = drillHits[0]?.distance ?? Infinity;\n const structDist = structHits[0]?.distance ?? Infinity;\n\n if (structDist < drillDist && structHits.length > 0) {\n const mesh = structHits[0].object;\n if (sceneCtx.drillholeClickHandler) {\n sceneCtx.drillholeClickHandler({ type: 'structure', ...mesh.userData });\n }\n return;\n }\n\n if (drillHits.length === 0) return;\n let obj = drillHits[0].object;\n while (obj && obj.parent && !obj.userData?.holeId) {\n obj = obj.parent;\n }\n const holeId = obj?.userData?.holeId;\n const project = obj?.userData?.project;\n if (holeId && sceneCtx.drillholeClickHandler) {\n sceneCtx.drillholeClickHandler({ holeId, project });\n }\n };\n\n renderer.domElement.addEventListener('click', sceneCtx.handleCanvasClick);\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';\nimport { FlyControls } from 'three/examples/jsm/controls/FlyControls';\nimport { ViewportGizmo } from 'three-viewport-gizmo';\nimport {\n buildViewSignature,\n emitViewChangeIfNeeded,\n fitCameraToBounds,\n focusOnLastBounds,\n getViewState,\n lookDown,\n pan,\n dolly,\n recenterCameraToOrigin,\n setControlMode,\n setFov,\n setViewState\n} from './baselode3dCameraControls.js';\nimport {\n initSelectionGlow,\n resizeGlow,\n applySelection,\n disposeSelectionGlow\n} from './selectionGlow.js';\nimport { setDrillholes as _setDrillholes, clearDrillholes as _clearDrillholes } from './drillholeScene.js';\nimport { setBlocks as _setBlocks, clearBlocks as _clearBlocks, setBlockOpacity as _setBlockOpacity } from './blockModelScene.js';\nimport {\n setStructuralDiscs as _setStructuralDiscs,\n clearStructuralDiscs as _clearStructuralDiscs,\n setStructuralDiscsVisible as _setStructuralDiscsVisible\n} from './structuralScene.js';\nimport { attachCanvasClickHandler as _attachCanvasClickHandler, updateSelectionFromPointer as _updateSelectionFromPointer } from './sceneClickHandler.js';\nimport { syncSelectables } from './sceneSelectables.js';\nimport {\n addRasterOverlay as _addRasterOverlay,\n removeRasterOverlay as _removeRasterOverlay,\n setRasterOverlayOpacity as _setRasterOverlayOpacity,\n setRasterOverlayVisibility as _setRasterOverlayVisibility,\n setRasterOverlayElevation as _setRasterOverlayElevation,\n getRasterOverlay as _getRasterOverlay,\n listRasterOverlays as _listRasterOverlays,\n clearRasterOverlays as _clearRasterOverlays,\n} from './rasterOverlayScene.js';\n\n/**\n * Baselode 3D Scene Manager\n * Manages THREE.js scene for rendering drillholes and block models in 3D.\n * Supports orbit and fly camera controls, assay coloring, and interactive selection.\n *\n * Rendering logic lives in the domain-specific modules; this class is a thin\n * orchestrator that owns the WebGL context and delegates to those modules.\n */\nclass Baselode3DScene {\n constructor() {\n this.container = null;\n this.scene = null;\n this.camera = null;\n this.renderer = null;\n this.controls = null;\n this.flyControls = null;\n this.gizmo = null;\n this.blocks = [];\n this.drillLines = [];\n this.drillMeshes = [];\n this.structuralGroup = null;\n this.structuralMeshes = [];\n this.frameId = null;\n this.clock = new THREE.Clock();\n this.handleCanvasClick = null;\n this.raycaster = new THREE.Raycaster();\n this.pointer = new THREE.Vector2();\n this.drillholeClickHandler = null;\n this.blockClickHandler = null;\n this.controlMode = 'orbit';\n this._tmpDir = new THREE.Vector3();\n this.viewChangeHandler = null;\n this._lastViewSignature = '';\n this._lastViewEmitMs = 0;\n this.selectables = [];\n this._selectedObject = null;\n this._composer = null;\n this._blockHighlightMesh = null;\n this._outlinePass = null;\n this.rasterOverlays = new Map();\n }\n\n init(container) {\n if (!container) return;\n this.container = container;\n\n const width = container.clientWidth;\n const height = container.clientHeight;\n\n // Scene\n this.scene = new THREE.Scene();\n this.scene.background = new THREE.Color(0xffffff);\n\n // Camera — lower near plane allows ultra-close zoom without clipping;\n // far plane set large enough for ultra-low-FOV (near-ortho) modes where\n // the camera must retreat several hundred kilometres to show a km-scale scene.\n this.camera = new THREE.PerspectiveCamera(28, width / height, 0.001, 10_000_000);\n this.camera.up.set(0, 0, 1);\n this.camera.position.set(50, 50, 50);\n this.camera.lookAt(0, 0, 0);\n\n // Renderer\n this.renderer = new THREE.WebGLRenderer({ antialias: true });\n this.renderer.setSize(width, height);\n this.renderer.setPixelRatio(window.devicePixelRatio);\n this.renderer.autoClear = false;\n container.appendChild(this.renderer.domElement);\n\n // Lighting\n const ambientLight = new THREE.AmbientLight(0xffffff, 1.2);\n this.scene.add(ambientLight);\n const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);\n directionalLight.position.set(10, 10, 5);\n this.scene.add(directionalLight);\n\n // Axes helper\n const axesHelper = new THREE.AxesHelper(20);\n this.scene.add(axesHelper);\n\n // Orbit controls\n this.controls = new OrbitControls(this.camera, this.renderer.domElement);\n this.controls.enableDamping = false;\n this.controls.screenSpacePanning = true;\n this.controls.enableZoom = true;\n this.controls.zoomSpeed = 1.2;\n this.controls.minDistance = 0.003;\n this.controls.maxDistance = 5_000_000;\n this.controls.mouseButtons = {\n LEFT: THREE.MOUSE.PAN,\n MIDDLE: THREE.MOUSE.DOLLY,\n RIGHT: THREE.MOUSE.ROTATE\n };\n this.controls.touches = {\n ONE: THREE.TOUCH.ROTATE,\n TWO: THREE.TOUCH.PAN\n };\n this.controls.maxPolarAngle = Math.PI;\n\n // Fly controls (disabled by default)\n this.flyControls = new FlyControls(this.camera, this.renderer.domElement);\n this.flyControls.movementSpeed = 2000;\n this.flyControls.rollSpeed = Math.PI / 12;\n this.flyControls.dragToLook = true;\n this.flyControls.enabled = false;\n\n // Viewport gizmo\n this.gizmo = new ViewportGizmo(this.camera, this.renderer, {\n container: this.container,\n placement: 'top-right',\n size: 110,\n offset: { top: 12, right: 12 },\n animated: true,\n speed: 1.5\n });\n this.gizmo.attachControls(this.controls);\n\n _attachCanvasClickHandler(this);\n\n // Selection glow post-processing\n initSelectionGlow(this);\n\n // Animation loop\n const animate = () => {\n this.frameId = requestAnimationFrame(animate);\n const delta = this.clock.getDelta();\n this.renderer.clear();\n if (this.controlMode === 'fly' && this.flyControls?.enabled) {\n this.flyControls.update(delta);\n } else if (this.controls) {\n this.controls.update();\n }\n this._emitViewChangeIfNeeded();\n if (this._composer) {\n this._composer.render(delta);\n } else {\n this.renderer.render(this.scene, this.camera);\n }\n if (this.gizmo) this.gizmo.render();\n };\n animate();\n }\n\n resize() {\n if (!this.container || !this.camera || !this.renderer) return;\n const width = this.container.clientWidth;\n const height = this.container.clientHeight;\n this.camera.aspect = width / height;\n this.camera.updateProjectionMatrix();\n this.renderer.setSize(width, height);\n if (this.gizmo) this.gizmo.update();\n resizeGlow(this, width, height);\n }\n\n dispose() {\n if (this.frameId) cancelAnimationFrame(this.frameId);\n if (this.renderer && this.handleCanvasClick) {\n this.renderer.domElement.removeEventListener('click', this.handleCanvasClick);\n }\n if (this.gizmo) {\n this.gizmo.dispose();\n this.gizmo = null;\n }\n this.viewChangeHandler = null;\n _clearBlocks(this);\n _clearDrillholes(this);\n _clearStructuralDiscs(this);\n _clearRasterOverlays(this);\n disposeSelectionGlow(this);\n if (this.controls) this.controls.dispose();\n if (this.flyControls) this.flyControls.dispose();\n if (this.renderer) {\n this.renderer.dispose();\n if (this.container && this.renderer.domElement) {\n this.container.removeChild(this.renderer.domElement);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Data renderers — delegate to domain modules\n // ---------------------------------------------------------------------------\n\n setDrillholes(holes, options = {}) { _setDrillholes(this, holes, options); }\n\n /**\n * Render block model data as a single merged mesh of exterior faces only.\n * @param {Array<Object>} data - Block rows (canonical column names)\n * @param {string} selectedProperty - Attribute column used for colouring\n * @param {Object} stats - Property statistics\n * @param {Object} [options]\n */\n setBlocks(data, selectedProperty, stats, options = {}) { _setBlocks(this, data, selectedProperty, stats, options); }\n\n /**\n * Update the opacity of all currently rendered blocks.\n * @param {number} opacity - New opacity value between 0 and 1\n */\n setBlockOpacity(opacity) { _setBlockOpacity(this, opacity); }\n\n setStructuralDiscs(structures, holes, opts = {}) { _setStructuralDiscs(this, structures, holes, opts); }\n\n setStructuralDiscsVisible(visible) { _setStructuralDiscsVisible(this, visible); }\n\n // ---------------------------------------------------------------------------\n // Click handlers\n // ---------------------------------------------------------------------------\n\n setDrillholeClickHandler(handler) {\n this.drillholeClickHandler = handler;\n }\n\n /**\n * Register a click handler for block selection.\n * @param {Function|null} handler - Callback ``(blockData) => void``, or null to clear\n */\n setBlockClickHandler(handler) {\n this.blockClickHandler = typeof handler === 'function' ? handler : null;\n }\n\n // ---------------------------------------------------------------------------\n // Camera controls — delegate to baselode3dCameraControls\n // ---------------------------------------------------------------------------\n\n setViewChangeHandler(handler) {\n this.viewChangeHandler = typeof handler === 'function' ? handler : null;\n }\n\n getViewState() { return getViewState(this); }\n setViewState(viewState) { return setViewState(this, viewState); }\n\n _buildViewSignature(viewState) { return buildViewSignature(viewState); }\n _emitViewChangeIfNeeded() { emitViewChangeIfNeeded(this); }\n\n _fitCameraToBounds({ minX, maxX, minY, maxY, minZ, maxZ }) {\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n\n recenterCameraToOrigin(distance = 1000) { recenterCameraToOrigin(this, distance); }\n lookDown(distance = 2000) { lookDown(this, distance); }\n pan(dx = 0, dy = 0) { pan(this, dx, dy); }\n dolly(scale = 1.1) { dolly(this, scale); }\n focusOnLastBounds(padding = 1.2) { focusOnLastBounds(this, padding); }\n\n /**\n * Change the camera field-of-view while keeping the visible scene the same apparent size.\n * @param {number} fovDeg - Desired FOV in degrees\n */\n setCameraFov(fovDeg) { setFov(this, fovDeg); }\n\n setControlMode(mode = 'orbit') { setControlMode(this, mode); }\n\n // ---------------------------------------------------------------------------\n // Selection glow public API\n // ---------------------------------------------------------------------------\n\n _syncSelectables() { syncSelectables(this); }\n\n /**\n * Register the objects that are candidates for click-select glow.\n * @param {THREE.Object3D[]} objects\n */\n setSelectableObjects(objects) {\n this.selectables = Array.isArray(objects) ? objects.slice() : [];\n }\n\n /**\n * Programmatically select an object (or pass null to clear).\n * @param {THREE.Object3D|null} object\n */\n selectObject(object) { applySelection(this, object || null); }\n\n /**\n * Return the currently selected object, or null if nothing is selected.\n * @returns {THREE.Object3D|null}\n */\n getSelectedObject() { return this._selectedObject || null; }\n\n /**\n * Dispose the effect composer and all GPU resources used by the selection glow.\n */\n disposeGlow() { disposeSelectionGlow(this); }\n\n /** @private */\n _updateSelectionFromPointer() { _updateSelectionFromPointer(this); }\n\n // ---------------------------------------------------------------------------\n // Raster overlay API — delegate to rasterOverlayScene\n // ---------------------------------------------------------------------------\n\n /**\n * Add a raster overlay layer (created with createRasterOverlay) to the scene.\n * @param {object} layer - Layer descriptor returned by createRasterOverlay()\n */\n addRasterOverlay(layer) { _addRasterOverlay(this, layer); }\n\n /**\n * Remove a raster overlay from the scene and dispose its GPU resources.\n * @param {string} id - Overlay id\n */\n removeRasterOverlay(id) { _removeRasterOverlay(this, id); }\n\n /**\n * Set the opacity of a raster overlay at runtime.\n * @param {string} id - Overlay id\n * @param {number} opacity - New opacity [0, 1]\n */\n setRasterOverlayOpacity(id, opacity) { _setRasterOverlayOpacity(this, id, opacity); }\n\n /**\n * Show or hide a raster overlay.\n * @param {string} id - Overlay id\n * @param {boolean} visible\n */\n setRasterOverlayVisibility(id, visible) { _setRasterOverlayVisibility(this, id, visible); }\n\n /**\n * Update the elevation (Z position) of a raster overlay.\n * @param {string} id - Overlay id\n * @param {number} elevation\n */\n setRasterOverlayElevation(id, elevation) { _setRasterOverlayElevation(this, id, elevation); }\n\n /**\n * Return a raster overlay by id, or undefined if not found.\n * @param {string} id\n * @returns {object|undefined}\n */\n getRasterOverlay(id) { return _getRasterOverlay(this, id); }\n\n /**\n * Return all raster overlay layers in insertion order.\n * @returns {object[]}\n */\n listRasterOverlays() { return _listRasterOverlays(this); }\n}\n\nexport default Baselode3DScene;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport './Baselode3DControls.css';\n\n/**\n * 3D scene control buttons component\n * Provides UI controls for camera manipulation in the 3D drillhole viewer\n * @param {Object} props - Component props\n * @param {string} props.controlMode - Current control mode ('orbit' or 'fly')\n * @param {Function} props.onToggleFly - Handler for toggling fly mode\n * @param {Function} props.onRecenter - Handler for recentering camera\n * @param {Function} props.onLookDown - Handler for top-down view\n * @param {Function} props.onFit - Handler for fitting camera to scene\n * @returns {JSX.Element} Control buttons component\n */\nfunction Baselode3DControls({\n controlMode = 'orbit',\n onToggleFly = () => {},\n onRecenter = () => {},\n onLookDown = () => {},\n onFit = () => {}\n}) {\n return (\n <div className=\"baselode-3d-controls\">\n <button type=\"button\" className=\"ghost-button\" onClick={onRecenter}>\n Recenter to (0,0,0)\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onLookDown}>\n Look down\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onFit}>\n Fit to scene\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onToggleFly}>\n {controlMode === 'orbit' ? 'Enable fly controls' : 'Disable fly controls'}\n </button>\n </div>\n );\n}\n\nexport default Baselode3DControls;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport './BlockModelWidget.css';\n\n/**\n * A panel of controls for exploring a block model in the 3-D scene.\n *\n * Exports widget state (attribute selection, opacity) so that a wrapping\n * application can pass the values on to the 3-D scene. Renders:\n * - An attribute (color-by) drop-down selector\n * - A translucency / opacity slider\n * - A simple color-scale legend (numeric attributes)\n * - A popup card showing all attributes of the last clicked block\n *\n * @param {Object} props\n * @param {Array<string>} props.properties - List of attribute column names\n * @param {string} props.selectedProperty - Currently selected attribute\n * @param {Function} props.onPropertyChange - Called with new property name string\n * @param {number} props.opacity - Current opacity value (0–1, default 0.85)\n * @param {Function} props.onOpacityChange - Called with new opacity number\n * @param {Object|null} props.propertyStats - Stats object for the selected\n * property (``{type, min?, max?, categories?}`` from calculatePropertyStats)\n * @param {Object|null} props.clickedBlock - Block row data to display in the\n * popup, or null when no block is selected\n * @param {Function} props.onPopupClose - Called when the user dismisses the popup\n */\nfunction BlockModelWidget({\n properties = [],\n selectedProperty = '',\n onPropertyChange = () => {},\n opacity = 0.85,\n onOpacityChange = () => {},\n propertyStats = null,\n clickedBlock = null,\n onPopupClose = () => {},\n}) {\n return (\n <div className=\"bm-widget\">\n {/* Attribute selector */}\n <label className=\"bm-widget__label\" htmlFor=\"bm-property-select\">\n Color by\n </label>\n <select\n id=\"bm-property-select\"\n className=\"bm-widget__select\"\n value={selectedProperty}\n onChange={(e) => onPropertyChange(e.target.value)}\n >\n {properties.length === 0 && (\n <option value=\"\">— no attributes —</option>\n )}\n {properties.map((p) => (\n <option key={p} value={p}>{p}</option>\n ))}\n </select>\n\n {/* Color scale legend */}\n {propertyStats && propertyStats.type === 'numeric' && (\n <div className=\"bm-widget__scale\">\n <span className=\"bm-widget__scale-label bm-widget__scale-label--min\">\n {propertyStats.min?.toFixed(2) ?? '—'}\n </span>\n <div className=\"bm-widget__scale-bar\" />\n <span className=\"bm-widget__scale-label bm-widget__scale-label--max\">\n {propertyStats.max?.toFixed(2) ?? '—'}\n </span>\n </div>\n )}\n {propertyStats && propertyStats.type === 'categorical' && (\n <div className=\"bm-widget__categories\">\n {(propertyStats.categories || []).map((cat, i) => {\n const hue = Math.round((i / Math.max(propertyStats.categories.length, 1)) * 360);\n return (\n <span key={cat} className=\"bm-widget__category-chip\"\n style={{ background: `hsl(${hue},70%,50%)` }}>\n {cat}\n </span>\n );\n })}\n </div>\n )}\n\n {/* Opacity slider */}\n <label className=\"bm-widget__label\" htmlFor=\"bm-opacity-slider\">\n Opacity ({Math.round(opacity * 100)}%)\n </label>\n <input\n id=\"bm-opacity-slider\"\n type=\"range\"\n className=\"bm-widget__slider\"\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n value={opacity}\n onChange={(e) => onOpacityChange(parseFloat(e.target.value))}\n />\n\n {/* Block attribute popup */}\n {clickedBlock && (\n <div className=\"bm-widget__popup\">\n <div className=\"bm-widget__popup-header\">\n <span>Block attributes</span>\n <button\n type=\"button\"\n className=\"bm-widget__popup-close\"\n onClick={onPopupClose}\n aria-label=\"Close\"\n >\n ×\n </button>\n </div>\n <table className=\"bm-widget__popup-table\">\n <tbody>\n {Object.entries(clickedBlock).map(([key, value]) => (\n <tr key={key}>\n <th>{key}</th>\n <td>{value === null || value === undefined ? '—' : String(value)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n );\n}\n\nexport default BlockModelWidget;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Core photo table visualization helpers.\n *\n * Provides depth-ordered layout utilities, LOD image selection, and depth\n * marker generation for the CorePhotoTable component.\n */\n\n/**\n * Default LOD (Level of Detail) breakpoints.\n *\n * At low zoom values small/thumbnail images are loaded; at high zoom values\n * full-resolution images are used. Each entry specifies the minimum zoom level\n * that activates the corresponding LOD key.\n *\n * Photo objects may supply a ``lod_urls`` map keyed by these values. If a\n * photo has no ``lod_urls``, ``image_url`` is used regardless of zoom.\n */\nexport const DEFAULT_LOD_BREAKPOINTS = [\n { minZoom: 0, lodKey: 'thumb' },\n { minZoom: 4, lodKey: 'medium' },\n { minZoom: 7, lodKey: 'full' },\n];\n\n/**\n * Base pixels-per-metre at zoom level 5. At other zoom levels this value is\n * scaled linearly so that ``pixelsPerMetre = BASE_PIXELS_PER_METRE * zoom / 5``.\n */\nexport const BASE_PIXELS_PER_METRE = 50;\n\n/**\n * Select the image URL appropriate for the given zoom level.\n *\n * If the photo object contains a ``lod_urls`` map the function returns the\n * URL for the highest LOD whose ``minZoom`` is still ≤ the current zoom\n * level. If no ``lod_urls`` are present (or the resolved key is missing)\n * the function falls back to ``photo.image_url``.\n *\n * @param {Object} photo - Photo entry. Expected fields: ``image_url`` (string),\n * ``lod_urls`` (optional object mapping lodKey → URL string).\n * @param {number} zoom - Current zoom level (1–10).\n * @param {Array<{minZoom: number, lodKey: string}>} [lodBreakpoints] - LOD\n * breakpoints to use. Defaults to {@link DEFAULT_LOD_BREAKPOINTS}.\n * @returns {string} The selected image URL, or an empty string if none found.\n */\nexport function selectPhotoLodUrl(photo, zoom, lodBreakpoints = DEFAULT_LOD_BREAKPOINTS) {\n const lodUrls = photo.lod_urls;\n if (!lodUrls || typeof lodUrls !== 'object' || Array.isArray(lodUrls)) {\n return photo.image_url || '';\n }\n\n // Walk breakpoints and keep the last one whose minZoom ≤ zoom.\n let selectedKey = lodBreakpoints[0]?.lodKey ?? 'thumb';\n for (const bp of lodBreakpoints) {\n if (zoom >= bp.minZoom) {\n selectedKey = bp.lodKey;\n }\n }\n\n return lodUrls[selectedKey] || photo.image_url || '';\n}\n\n/**\n * Return a copy of ``photos`` sorted ascending by ``from_depth``.\n *\n * @param {Array<Object>} photos - Array of photo objects, each with a\n * numeric ``from_depth`` field.\n * @returns {Array<Object>} New sorted array.\n */\nexport function sortPhotosByDepth(photos) {\n return [...photos].sort((a, b) => (a.from_depth ?? 0) - (b.from_depth ?? 0));\n}\n\n/**\n * Group photos by their ``photo_set`` value.\n *\n * Photos without a ``photo_set`` (null, undefined, or empty string) are\n * placed in the ``'default'`` group. The returned object's keys preserve\n * insertion order (first-seen order within ``photos``).\n *\n * @param {Array<Object>} photos - Array of photo objects.\n * @returns {Object.<string, Array<Object>>} Map of set name → photo array.\n */\nexport function groupPhotosBySet(photos) {\n /** @type {Object.<string, Array<Object>>} */\n const sets = {};\n for (const photo of photos) {\n const key =\n photo.photo_set != null && photo.photo_set !== ''\n ? String(photo.photo_set)\n : 'default';\n if (!sets[key]) sets[key] = [];\n sets[key].push(photo);\n }\n return sets;\n}\n\n/**\n * Build an array of depth marker descriptors at regular intervals.\n *\n * Markers start at the first multiple of ``intervalMetres`` that is ≥\n * ``minDepth`` and continue up to and including ``maxDepth``.\n *\n * @param {number} minDepth - Shallowest depth of the view (m).\n * @param {number} maxDepth - Deepest depth of the view (m).\n * @param {number} [intervalMetres=10] - Spacing between markers in metres.\n * @returns {Array<{depth: number, label: string}>} Ordered marker descriptors.\n */\nexport function buildDepthMarkers(minDepth, maxDepth, intervalMetres = 10) {\n const markers = [];\n if (minDepth >= maxDepth || intervalMetres <= 0) return markers;\n\n const first = Math.ceil(minDepth / intervalMetres) * intervalMetres;\n // Guard against floating-point drift with a small epsilon.\n const epsilon = intervalMetres * 1e-9;\n for (let d = first; d <= maxDepth + epsilon; d += intervalMetres) {\n const rounded = Math.round(d * 1e6) / 1e6;\n markers.push({ depth: rounded, label: `${rounded} m` });\n }\n return markers;\n}\n\n/**\n * Choose an appropriate depth-marker interval (in metres) for the given zoom\n * level so that labels are legible without overcrowding.\n *\n * @param {number} zoom - Current zoom level (1–10).\n * @returns {number} Interval in metres.\n */\nexport function depthMarkerInterval(zoom) {\n if (zoom >= 9) return 1;\n if (zoom >= 7) return 2;\n if (zoom >= 5) return 5;\n if (zoom >= 3) return 10;\n return 20;\n}\n\n/**\n * Default filename generator used by {@link buildTrayPhotos}.\n *\n * @param {number} index - Zero-based tray index.\n * @returns {string}\n */\nexport function defaultTrayFilename(index) {\n return `tray_${String(index).padStart(3, '0')}.jpg`;\n}\n\n/**\n * Build a CorePhotoTable-compatible photos array from tray depth intervals\n * and a pair of image base URLs.\n *\n * This is the same transform performed internally by CorePhotoViewer; it is\n * exported so callers that need to combine multiple datasets (e.g. two\n * concurrent scan runs for the same hole) can build the array themselves and\n * pass it directly to CorePhotoTable.\n *\n * @param {string} holeId - Drillhole identifier stored on every photo entry.\n * @param {Array<{\n * fromDepth: number,\n * toDepth: number,\n * filename?: string,\n * photoSet?: string\n * }>} trays - One entry per image, in any depth order.\n * @param {string} thumbBaseUrl - URL prefix for thumbnail images.\n * @param {string} fullBaseUrl - URL prefix for full-resolution images.\n * @param {string} [photoSet='Tray Images'] - Default column label; overridden\n * per-tray via ``tray.photoSet``.\n * @param {function} [getFilename] - ``(index: number) => string`` filename\n * generator. ``index`` is the zero-based position in ``trays``. Defaults to\n * {@link defaultTrayFilename} (``tray_000.jpg``, ``tray_001.jpg``, …).\n * @returns {Array<Object>} Photos array suitable for ``CorePhotoTable.photos``.\n */\nexport function buildTrayPhotos(\n holeId,\n trays,\n thumbBaseUrl,\n fullBaseUrl,\n photoSet = 'Tray Images',\n getFilename = defaultTrayFilename,\n) {\n const thumb = (thumbBaseUrl ?? '').replace(/\\/$/, '');\n const full = (fullBaseUrl ?? '').replace(/\\/$/, '');\n return trays.map((tray, index) => {\n const filename = tray.filename ?? getFilename(index);\n const set = tray.photoSet ?? photoSet;\n return {\n hole_id: holeId,\n from_depth: tray.fromDepth,\n to_depth: tray.toDepth,\n photo_set: set,\n image_url: `${thumb}/${filename}`,\n lod_urls: {\n thumb: `${thumb}/${filename}`,\n full: `${full}/${filename}`,\n },\n };\n });\n}\n\n/**\n * Convert a depth interval in metres to a pixel height for the given zoom.\n *\n * Uses a linear scale so that the displayed height is always proportional to\n * the physical depth span, enabling correct alignment across photo columns.\n *\n * @param {number} depthInterval - Depth span in metres (``to_depth - from_depth``).\n * @param {number} zoom - Current zoom level (1–10).\n * @param {number} [basePixelsPerMetre] - Pixels per metre at zoom 5.\n * Defaults to {@link BASE_PIXELS_PER_METRE}.\n * @returns {number} Pixel height (always ≥ 1).\n */\nexport function depthIntervalToPixels(depthInterval, zoom, basePixelsPerMetre = BASE_PIXELS_PER_METRE) {\n const scale = zoom / 5;\n return Math.max(1, Math.round(depthInterval * basePixelsPerMetre * scale));\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport {\n BASE_PIXELS_PER_METRE,\n buildDepthMarkers,\n depthMarkerInterval,\n groupPhotosBySet,\n selectPhotoLodUrl,\n sortPhotosByDepth,\n} from './corePhotoViz.js';\nimport './CorePhotoTable.css';\n\nconst BASE_ZOOM = 5; // fixed layout zoom (no reflow on pan/zoom)\nconst ZOOM_FACTOR = 1.12; // scale multiplier per wheel tick\nconst SCALE_MIN = 0.05;\nconst SCALE_MAX = 40;\n\n/**\n * CorePhotoTable\n *\n * Renders a pannable/zoomable core photograph table with:\n *\n * - Depth-ordered images stacked from shallow → deep\n * - Multiple photo sets displayed side-by-side (one column per set)\n * - A depth ruler for spatial orientation\n * - Mouse-wheel zoom centred on cursor (Leaflet-style)\n * - Left-click drag to pan\n *\n * @param {Object} props\n * @param {Array<CorePhoto>} [props.photos=[]] - Photo entries to display.\n * Each entry should have: ``hole_id`` (string), ``from_depth`` (number),\n * ``to_depth`` (number), ``image_url`` (string), ``photo_set`` (string,\n * optional), ``lod_urls`` (object, optional).\n * @param {string} [props.holeId=''] - Hole identifier shown in the control bar.\n * @param {number} [props.initialZoom=5] - Starting zoom level (1–10) for LOD.\n */\nexport function CorePhotoTable({\n photos = [],\n holeId = '',\n initialZoom = 5,\n transform: controlledTransform,\n onTransformChange,\n}) {\n // ── Pan / zoom transform ────────────────────────────────────────────────\n // Supports both uncontrolled (internal state) and controlled modes.\n // Pass transform + onTransformChange to share state across multiple tables.\n const [internalTransform, setInternalTransform] = useState({ scale: 1, tx: 0, ty: 0 });\n const transform = controlledTransform ?? internalTransform;\n const transformRef = useRef(transform);\n transformRef.current = transform;\n\n // Always resolves functional updaters to a plain object before dispatching,\n // so onTransformChange is always called with a transform object, never a function.\n const setTransform = useCallback(\n (updater) => {\n const next = typeof updater === 'function' ? updater(transformRef.current) : updater;\n if (onTransformChange) {\n onTransformChange(next);\n } else {\n setInternalTransform(next);\n }\n },\n [onTransformChange],\n );\n\n const [dragging, setDragging] = useState(false);\n const dragOrigin = useRef(null);\n\n const viewportRef = useRef(null);\n\n // ── Derived layout values (fixed at BASE_ZOOM, no reflow on pan/zoom) ──\n\n const sorted = useMemo(() => sortPhotosByDepth(photos), [photos]);\n const grouped = useMemo(() => groupPhotosBySet(sorted), [sorted]);\n\n // Derive column order from the original (unsorted) photos so that callers\n // control left-to-right ordering by the sequence they pass photos in.\n const setNames = useMemo(() => {\n const seen = new Set();\n for (const p of photos) {\n const key = p.photo_set != null && p.photo_set !== '' ? String(p.photo_set) : 'default';\n seen.add(key);\n }\n return [...seen];\n }, [photos]);\n\n const { minDepth, maxDepth } = useMemo(() => {\n if (!sorted.length) return { minDepth: 0, maxDepth: 0 };\n const allTo = sorted.map((p) => p.to_depth ?? p.from_depth ?? 0);\n return {\n minDepth: sorted[0].from_depth ?? 0,\n maxDepth: Math.max(...allTo),\n };\n }, [sorted]);\n\n const pixelsPerMetre = (BASE_PIXELS_PER_METRE * BASE_ZOOM) / 5;\n\n const totalHeight = useMemo(\n () => Math.max(1, Math.round((maxDepth - minDepth) * pixelsPerMetre)),\n [minDepth, maxDepth, pixelsPerMetre],\n );\n\n const markers = useMemo(() => {\n const interval = depthMarkerInterval(BASE_ZOOM);\n return buildDepthMarkers(minDepth, maxDepth, interval);\n }, [minDepth, maxDepth]);\n\n const imageWidth = (540 * BASE_ZOOM) / 5; // fixed 540 px at base zoom\n\n // Effective LOD zoom derived from visual scale so thumbnails swap to full\n // resolution when the user zooms in close enough.\n const lodZoom = useMemo(\n () => Math.max(1, Math.min(10, Math.round(initialZoom * transform.scale))),\n [initialZoom, transform.scale],\n );\n\n // ── Wheel zoom (non-passive, centred on cursor) ─────────────────────────\n\n const handleWheel = useCallback((e) => {\n e.preventDefault();\n const factor = e.deltaY < 0 ? ZOOM_FACTOR : 1 / ZOOM_FACTOR;\n const rect = viewportRef.current.getBoundingClientRect();\n const cx = e.clientX - rect.left;\n const cy = e.clientY - rect.top;\n\n setTransform((prev) => {\n const newScale = Math.max(SCALE_MIN, Math.min(SCALE_MAX, prev.scale * factor));\n const ratio = newScale / prev.scale;\n return {\n scale: newScale,\n tx: cx - (cx - prev.tx) * ratio,\n ty: cy - (cy - prev.ty) * ratio,\n };\n });\n }, [setTransform]);\n\n useEffect(() => {\n const el = viewportRef.current;\n if (!el) return;\n el.addEventListener('wheel', handleWheel, { passive: false });\n return () => el.removeEventListener('wheel', handleWheel);\n }, [handleWheel]);\n\n // ── Left-drag pan ───────────────────────────────────────────────────────\n\n const handleMouseDown = useCallback((e) => {\n if (e.button !== 0) return;\n e.preventDefault();\n dragOrigin.current = {\n x: e.clientX,\n y: e.clientY,\n tx: transformRef.current.tx,\n ty: transformRef.current.ty,\n };\n setDragging(true);\n }, []);\n\n useEffect(() => {\n const handleMouseMove = (e) => {\n if (!dragOrigin.current) return;\n // Snapshot ref values before entering the async updater — the ref may\n // be nulled by handleMouseUp before React executes the updater.\n const { tx, ty, x, y } = dragOrigin.current;\n setTransform((prev) => ({\n ...prev,\n tx: tx + (e.clientX - x),\n ty: ty + (e.clientY - y),\n }));\n };\n const handleMouseUp = () => {\n dragOrigin.current = null;\n setDragging(false);\n };\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n return () => {\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n };\n }, [setTransform]);\n\n // ── Render ──────────────────────────────────────────────────────────────\n\n return (\n <div className=\"core-photo-table\">\n {/* Controls */}\n <div className=\"core-photo-controls\">\n {holeId && <span className=\"core-photo-hole-id\">{holeId}</span>}\n <span className=\"core-photo-zoom-label\">\n {Math.round(transform.scale * 100)}%\n </span>\n <button\n className=\"core-photo-zoom-btn\"\n onClick={() => setTransform({ scale: 1, tx: 0, ty: 0 })}\n aria-label=\"Reset view\"\n title=\"Reset view\"\n >\n ⌂\n </button>\n </div>\n\n {/* Column label strip */}\n {photos.length > 0 && (\n <div className=\"core-photo-col-headers\">\n <div className=\"core-photo-ruler-spacer\" />\n {setNames.map((name) => (\n <div\n key={name}\n className=\"core-photo-set-header\"\n style={{ width: imageWidth }}\n >\n {name}\n </div>\n ))}\n </div>\n )}\n\n {/* Body */}\n {photos.length === 0 ? (\n <div className=\"core-photo-empty\">No photos to display.</div>\n ) : (\n <div\n className={`core-photo-scroll${dragging ? ' is-dragging' : ''}`}\n ref={viewportRef}\n onMouseDown={handleMouseDown}\n >\n <div\n className=\"core-photo-inner\"\n style={{\n height: totalHeight,\n transform: `translate(${transform.tx}px, ${transform.ty}px) scale(${transform.scale})`,\n transformOrigin: '0 0',\n }}\n >\n {/* Depth ruler */}\n <div\n className=\"core-photo-depth-ruler\"\n style={{ height: totalHeight }}\n >\n {markers.map(({ depth, label }) => (\n <div\n key={depth}\n className=\"core-photo-depth-marker\"\n style={{\n top: Math.round((depth - minDepth) * pixelsPerMetre),\n }}\n >\n {label}\n </div>\n ))}\n </div>\n\n {/* Photo set columns */}\n {setNames.map((setName) => (\n <div\n key={setName}\n className=\"core-photo-col-body\"\n style={{ height: totalHeight, width: imageWidth }}\n >\n {grouped[setName].map((photo) => {\n const fromDepth = photo.from_depth ?? 0;\n const toDepth = photo.to_depth ?? fromDepth;\n const top = Math.round(\n (fromDepth - minDepth) * pixelsPerMetre,\n );\n const height = Math.max(\n 2,\n Math.round((toDepth - fromDepth) * pixelsPerMetre),\n );\n const src = selectPhotoLodUrl(photo, lodZoom);\n\n return (\n <div\n key={`${photo.hole_id ?? ''}-${fromDepth}-${toDepth}-${setName}`}\n className=\"core-photo-item\"\n style={{ top, height, width: imageWidth }}\n title={`${fromDepth}–${toDepth} m`}\n >\n {src ? (\n <img\n src={src}\n alt={`Core ${fromDepth}–${toDepth} m`}\n loading=\"lazy\"\n />\n ) : (\n <div className=\"core-photo-no-image\" />\n )}\n {height >= 18 && (\n <span className=\"core-photo-item-label\">\n {fromDepth}–{toDepth} m\n </span>\n )}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport default CorePhotoTable;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useMemo } from 'react';\nimport CorePhotoTable from './CorePhotoTable.jsx';\nimport { buildTrayPhotos, defaultTrayFilename } from './corePhotoViz.js';\n\n/**\n * CorePhotoViewer\n * ===============\n *\n * A self-contained, pannable/zoomable core box photograph viewer for use in\n * any React application. Point it at a sequence of tray images (e.g. from an\n * NVCL HyLogger dataset) and it renders them depth-aligned with a ruler,\n * mouse-wheel zoom, and left-drag panning.\n *\n *\n * ## Minimal usage\n *\n * ```jsx\n * import { CorePhotoViewer } from 'baselode';\n * import 'baselode/style.css'; // required\n *\n * <div style={{ width: '100%', height: '80vh' }}>\n * <CorePhotoViewer\n * holeId=\"09RTD001\"\n * trays={[\n * { fromDepth: 57.103, toDepth: 60.603 },\n * { fromDepth: 60.603, toDepth: 64.003 },\n * // … one entry per image, in ascending depth order\n * ]}\n * thumbBaseUrl=\"/data/09RTD001/thumb\"\n * fullBaseUrl=\"/data/09RTD001/full\"\n * />\n * </div>\n * ```\n *\n *\n * ## What you must supply\n *\n * ### 1 · Tray depth intervals (`trays`)\n *\n * An array of objects, one per image file, each containing the downhole depth\n * range (metres) that the tray covers:\n *\n * ```js\n * [\n * { fromDepth: 57.103, toDepth: 60.603 },\n * { fromDepth: 60.603, toDepth: 64.003 },\n * ]\n * ```\n *\n * **Where to get these values:**\n * - Core scanning system export (HyLogger, Corescan, etc.) — usually a CSV\n * with from/to depths per tray.\n * - LIMS or core database — query from/to depths for the hole ordered by depth.\n * - Manual entry or calculation from known tray length and start depth.\n *\n *\n * ### 2 · Image directories (`thumbBaseUrl` / `fullBaseUrl`)\n *\n * Two URL prefixes pointing to directories that contain the image files:\n *\n * | Prop | Purpose | Typical size |\n * |----------------|-------------------------------------------|----------------|\n * | `thumbBaseUrl` | Low-resolution thumbnails — always loaded | ~10–50 KB each |\n * | `fullBaseUrl` | High-resolution originals — loaded on zoom| 1–10 MB each |\n *\n * If you only have one resolution pass the same URL for both props.\n *\n * The directories must be **browser-accessible** — e.g.:\n * - Served from your web server's `public/` folder\n * - An S3 bucket / CDN with CORS enabled (`Access-Control-Allow-Origin: *`)\n * - A Vercel / Netlify static asset path\n *\n * The viewer never loads all full-res images at once; it swaps to full\n * resolution only when the user zooms in past a threshold.\n *\n *\n * ### 3 · Container size\n *\n * The component fills 100 % of its parent's width and height. Wrap it in a\n * `<div>` with an explicit height (e.g. `height: '80vh'`).\n *\n *\n * ## Filename conventions\n *\n * By default filenames are generated as `tray_000.jpg`, `tray_001.jpg`, …\n * (zero-padded three-digit index, 0-based).\n *\n * Override per-tray via `tray.filename`:\n * ```js\n * { fromDepth: 3.4, toDepth: 6.8, filename: 'DDH001_box002.jpg' }\n * ```\n *\n * Or supply a custom generator for the whole dataset:\n * ```jsx\n * <CorePhotoViewer\n * getFilename={(index) => `box_${String(index + 1).padStart(4, '0')}.jpg`}\n * // …\n * />\n * ```\n *\n *\n * ## Multiple photo sets (columns)\n *\n * If you have parallel image sets (e.g. \"Wet\" and \"Dry\") for the same hole,\n * interleave the trays and tag each with `photoSet`:\n *\n * ```js\n * trays={[\n * { fromDepth: 0, toDepth: 3.4, photoSet: 'Wet', filename: 'wet_000.jpg' },\n * { fromDepth: 0, toDepth: 3.4, photoSet: 'Dry', filename: 'dry_000.jpg' },\n * { fromDepth: 3.4, toDepth: 6.8, photoSet: 'Wet', filename: 'wet_001.jpg' },\n * { fromDepth: 3.4, toDepth: 6.8, photoSet: 'Dry', filename: 'dry_001.jpg' },\n * ]}\n * ```\n *\n *\n * ## Interaction\n *\n * | Input | Action |\n * |------------------------|-------------------------------------|\n * | Mouse wheel | Zoom in / out (centred on cursor) |\n * | Left-click drag | Pan |\n * | ⌂ button in header | Reset to default view |\n *\n *\n * @param {Object} props\n * @param {string} [props.holeId='']\n * Drillhole identifier — shown in the header bar.\n *\n * @param {Array<{\n * fromDepth: number,\n * toDepth: number,\n * filename?: string,\n * photoSet?: string\n * }>} [props.trays=[]]\n * Tray depth intervals. `fromDepth` / `toDepth` in metres downhole.\n * Optional `filename` overrides the auto-generated name for that tray.\n * Optional `photoSet` overrides the column label for that tray.\n *\n * @param {string} [props.thumbBaseUrl='']\n * URL prefix for thumbnail images (no trailing slash needed).\n * Example: `'https://cdn.example.com/holes/09RTD001/thumb'`\n *\n * @param {string} [props.fullBaseUrl='']\n * URL prefix for full-resolution images.\n * Example: `'https://cdn.example.com/holes/09RTD001/full'`\n *\n * @param {string} [props.photoSet='Tray Images']\n * Default column label used when `tray.photoSet` is absent.\n *\n * @param {function} [props.getFilename]\n * `(index: number) => string` — custom filename generator.\n * `index` is 0-based. Defaults to `tray_NNN.jpg`.\n *\n * @param {number} [props.initialZoom=5]\n * Starting LOD level (1–10). Controls when the viewer switches from\n * thumbnails to full-res as the user zooms in. 5 is a sensible default.\n */\nexport function CorePhotoViewer({\n holeId = '',\n trays = [],\n thumbBaseUrl = '',\n fullBaseUrl = '',\n photoSet: defaultPhotoSet = 'Tray Images',\n getFilename = defaultTrayFilename,\n initialZoom = 5,\n transform,\n onTransformChange,\n}) {\n const photos = useMemo(\n () => buildTrayPhotos(holeId, trays, thumbBaseUrl, fullBaseUrl, defaultPhotoSet, getFilename),\n [holeId, trays, thumbBaseUrl, fullBaseUrl, defaultPhotoSet, getFilename],\n );\n\n return (\n <CorePhotoTable\n photos={photos}\n holeId={holeId}\n initialZoom={initialZoom}\n transform={transform}\n onTransformChange={onTransformChange}\n />\n );\n}\n\nexport default CorePhotoViewer;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * @module grade_blocks\n *\n * Loading and Three.js rendering of 3D polygonal grade block meshes.\n *\n * A grade block is a closed polyhedral mesh defined by:\n * - An array of 3-D vertices [[x,y,z], ...]\n * - An array of triangle indices [[i,j,k], ...]\n * - Optional attributes and material hints\n *\n * Schema version \"1.0\" is the only version supported.\n */\n\nimport * as THREE from 'three';\n\n// ---------------------------------------------------------------------------\n// Data model helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse and validate a raw JSON object (or JSON string) and return a\n * GradeBlockSet plain object.\n *\n * @param {object|string} input - Parsed JSON object or a JSON string.\n * @returns {{ schema_version: string, units: string, blocks: GradeBlock[] }}\n * @throws {Error} If schema_version is not \"1.0\" or required fields are missing.\n */\nexport function loadGradeBlocksFromJson(input) {\n const data = typeof input === 'string' ? JSON.parse(input) : input;\n\n if (data.schema_version !== '1.0') {\n throw new Error(\n `Unsupported schema_version: ${JSON.stringify(data.schema_version)}. Expected \"1.0\".`\n );\n }\n\n if (!Array.isArray(data.blocks)) {\n throw new Error('\"blocks\" must be a JSON array.');\n }\n\n const blocks = data.blocks.map((raw, i) => {\n if (raw.id == null) throw new Error(`Block at index ${i} is missing required field \"id\".`);\n if (raw.name == null) throw new Error(`Block \"${raw.id}\" is missing required field \"name\".`);\n if (!Array.isArray(raw.vertices)) throw new Error(`Block \"${raw.id}\" is missing required field \"vertices\".`);\n if (!Array.isArray(raw.triangles)) throw new Error(`Block \"${raw.id}\" is missing required field \"triangles\".`);\n\n return {\n id: raw.id,\n name: raw.name,\n vertices: raw.vertices,\n triangles: raw.triangles,\n attributes: raw.attributes ?? {},\n material: raw.material ?? {},\n };\n });\n\n return {\n schema_version: data.schema_version,\n units: data.units ?? '',\n blocks,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Three.js geometry\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a grade block to a Three.js BufferGeometry.\n *\n * @param {object} block - A grade block object (from loadGradeBlocksFromJson).\n * @returns {THREE.BufferGeometry}\n */\nexport function gradeBlockToThreeGeometry(block) {\n const geometry = new THREE.BufferGeometry();\n\n // Flatten vertices: [[x,y,z], ...] -> Float32Array\n const positionData = new Float32Array(block.vertices.length * 3);\n block.vertices.forEach(([x, y, z], i) => {\n positionData[i * 3] = x;\n positionData[i * 3 + 1] = y;\n positionData[i * 3 + 2] = z;\n });\n geometry.setAttribute('position', new THREE.BufferAttribute(positionData, 3));\n\n // Flatten triangles: [[i,j,k], ...] -> Uint32Array\n const indexData = new Uint32Array(block.triangles.length * 3);\n block.triangles.forEach(([a, b, c], i) => {\n indexData[i * 3] = a;\n indexData[i * 3 + 1] = b;\n indexData[i * 3 + 2] = c;\n });\n geometry.setIndex(new THREE.BufferAttribute(indexData, 1));\n\n return geometry;\n}\n\n// ---------------------------------------------------------------------------\n// Scene helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Create Three.js meshes for all blocks and add them to the scene.\n *\n * @param {THREE.Scene} scene\n * @param {{ blocks: object[] }} blockSet - Result of loadGradeBlocksFromJson.\n * @param {object} [options]\n * @param {number} [options.defaultOpacity=1.0] - Fallback opacity when not set per block.\n * @returns {THREE.Group}\n */\nexport function addGradeBlocksToScene(scene, blockSet, options = {}) {\n const { defaultOpacity = 1.0 } = options;\n const group = new THREE.Group();\n\n blockSet.blocks.forEach((block) => {\n const geometry = gradeBlockToThreeGeometry(block);\n\n const color = block.material?.color ?? '#888888';\n const opacity = block.material?.opacity ?? defaultOpacity;\n const transparent = opacity < 1.0;\n\n const material = new THREE.MeshStandardMaterial({\n color: new THREE.Color(color),\n opacity,\n transparent,\n side: THREE.DoubleSide,\n flatShading: true,\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.userData = {\n id: block.id,\n attributes: block.attributes,\n };\n\n // Edge highlight — hidden by default, shown on selection\n const edgeGeo = new THREE.EdgesGeometry(geometry, 15);\n const edgeMat = new THREE.LineBasicMaterial({ color: '#ffffbb', linewidth: 1 });\n const edgeLines = new THREE.LineSegments(edgeGeo, edgeMat);\n edgeLines.visible = false;\n mesh.add(edgeLines);\n\n group.add(mesh);\n });\n\n scene.add(group);\n return group;\n}\n"],"names":["HOLE_ID","LATITUDE","LONGITUDE","ELEVATION","AZIMUTH","DIP","FROM","TO","MID","PROJECT_ID","EASTING","NORTHING","CRS","DEPTH","ALPHA","BETA","STRIKE","GEOLOGY_CODE","GEOLOGY_DESCRIPTION","BASELODE_DATA_MODEL_DRILL_COLLAR","BASELODE_DATA_MODEL_DRILL_SURVEY","BASELODE_DATA_MODEL_DRILL_ASSAY","BASELODE_DATA_MODEL_DRILL_GEOLOGY","BASELODE_DATA_MODEL_STRUCTURAL_POINT","DEFAULT_COLUMN_MAP","_COLUMN_LOOKUP","standardCol","variations","variation","normalized","normalizeFieldName","name","standardizeColumns","row","columnMap","sourceColumnMap","lookup","rawName","expectedName","normalizedKey","normalizedValue","renamed","col","value","key","mapped","standardizeRowArray","rows","ASSAY_NON_VALUE_FIELDS","DATA_LOG_PREFIX","toError","error","fallbackMessage","message","withDataErrorContext","context","baseError","wrapped","logDataWarning","logDataInfo","normalizeRow","rawRow","extractIdFields","extractInterval","holeIdRaw","holeId","project","from","to","intervalsToHole","intervals","sorted","a","b","points","iv","rest","pointData","_a","parseAssayHoleIds","file","resolve","reject","holeIds","Papa","results","hid","hasAssayValue","k","v","parseAssayHoleIdsWithAssays","byHole","parseAssayHole","config","wanted","interval","hole","parseAssaysCSV","holes","normalizeCsvRow","pickFirstPresent","keys","fallback","DEFAULT_TRACE_PLOT_COUNT","reorderHoleIds","ids","focusId","matchIdx","id","selected","_","idx","coerceChartTypeForProperty","property","chartType","categoricalProps","commentProps","numericDefaultChartType","buildTraceConfigsForHoleIds","focusedHoleId","plotCount","defaultProp","ordered","DISPLAY_NUMERIC","DISPLAY_CATEGORICAL","DISPLAY_COMMENT","DISPLAY_HIDDEN","DISPLAY_TADPOLE","CHART_OPTIONS","HIDDEN_COLUMNS","COMMENT_COLUMN_NAMES","classifyColumns","allCols","byType","hasValue","r","hasNumeric","t","getChartOptions","displayType","defaultChartType","opts","deriveAssayProps","h","numericCols","categoricalCols","commentCols","loadAssayMetadata","loadAssayHole","buildAssayState","numericProps","columnMeta","traceConfigs","loadAssayFile","state","parseSurveyCSV","err","norm","lat","toNumber","lng","surveyDepth","dip","azimuth","maxDepth","n","desurveyTraces","collars","surveys","collarByKey","c","refLat","_b","refLng","_c","_d","refMetersPerDegLat","refMetersPerDegLon","grouped","s","stations","collar","lat0","lng0","metersPerDegLat","metersPerDegLon","baseX","baseY","accX","accY","accZ","i","curr","prev","currDepth","currAzimuth","currDip","prevDepth","prevAzimuth","prevDip","deltaMD","inc1","toInclination","inc2","az1","degToRad","az2","beta","rf","dx","dy","dzDown","pointsWithGeo","p","d","dipDeg","val","incDeg","clamped","normalizeHoleIdValue","canonicalizeHoleIdRows","holeIdCol","preferred","resolved","angle","directionCosines","azRad","dipRad","ca","cb","cc","segmentDisplacement","deltaMd","az0","dip0","dip1","method","dc0","dc1","azAvg","dipAvg","dcAvg","dot","dogleg","desurvey","rowsCollars","rowsSurveys","options","step","safeStep","collarsCanonical","surveysCanonical","collarsByHole","surveysByHole","out","x","y","z","mdCursor","azPrev","dipPrev","firstRecord","s0","s1","md0","segmentSteps","mdIncrement","stepIdx","weight","azInterp","dipInterp","disp","record","minimumCurvatureDesurvey","tangentialDesurvey","balancedTangentialDesurvey","buildTraces","nearestByMeasuredDepth","traceRows","midMd","best","bestDist","md","dist","attachAssayPositions","assays","traces","assaysCanonical","tracesCanonical","tracesByHole","assay","nearest","merged","parseDrillholesCSV","order","pts","toArray","source","sortByColumns","columns","av","bv","validateNoOverlappingIntervals","label","prevToByHole","fromValue","toValue","prevTo","parseCsv","papaParseConfig","standardizeRow","loadTable","kind","loadCollars","crs","keepAll","tableOptions","standardized","hasXY","hasLatLon","result","loadSurveys","required","loadAssays","loadGeology","hasCode","hasDescription","code","description","keep","joinAssaysToTraces","onCols","keyOf","tracesByKey","trace","filterByProject","projectId","coerceNumeric","next","column","assembleDataset","geology","structures","metadata","GEOMETRY_COLS","BLOCK_COL_MAP","_blockColLookup","canon","variants","normalizeBlockRow","parseBlockModelCSV","data","propertyColumns","loadBlockModelMetadata","calculatePropertyStats","values","min","max","getBlockStats","propertyKeys","filterBlocks","criteria","condition","calculateBlockVolume","sum","dz","getColorForValue","stats","THREEInstance","range","hue","detectSchema","first","hasInterval","hasPoint","extractStructuralPoint","depth","extractStructuralInterval","mid","validateStructuralPoints","valid","errors","messages","az","parseStructuralPointsCSV","point","parseStructuralIntervalsCSV","groupRowsByHole","byId","parseStructuralCSV","schema","parsed","parseAssayCsvTextToHoles","csvText","_dip","_az","rowWithoutStructural","parseGeologyCsvText","parseUnifiedDataset","assayCsv","structuralCsv","geologyCsv","assayHoles","structuralHoles","geologyHoles","sh","existing","significantIntercepts","assayField","minGrade","minLength","fromCol","toCol","holeCol","holeIntervals","qualifying","grade","runs","currentRun","f","run","totalFrom","totalTo","totalLength","weightedSum","totalWeight","len","avgGrade","nSamples","interpolateTrace","sortedTraceRows","targetMd","segIdx","md1","p0","p1","segLen","ddx","ddy","ddz","dlen","alphaBetaToNormal","alphaDeg","betaDeg","drillDir","betaZeroAxis","betaHandedness","D","U","dotDU","crossUD","crossUDLen","R","crossDR","crossDRLen","B","N_perp0","betaRad","cosB","sinB","dotND","crossDN","N_perp","theta","cosT","sinT","Nx","Ny","Nz","NLen","computeStructuralPositions","holeTrace","pos","nx","ny","nz","alpha","betaVal","BASELODE_TEMPLATE_NAME","BASELODE_LIGHT_TEMPLATE_NAME","BASELODE_COLORWAY","BASELODE_LIGHT","BASELODE_TEMPLATE","BASELODE_LIGHT_TEMPLATE","BASELODE_DARK","BASELODE_DARK_TEMPLATE_NAME","BASELODE_DARK_TEMPLATE","FALLBACK_COLOUR","COMMODITY_COLOURS","LITHOLOGY_COLOURS","BUILTIN_COLOUR_MAPS","getColour","colourMap","keyLower","mapKey","mapColour","resolveColourMap","nameOrMap","available","NUMERIC_LINE_COLOR","NUMERIC_MARKER_COLOR","commodityColourForProperty","tokens","token","low","colour","ERROR_COLOR","STRIPLOG_COMPACT_MARGIN","STRIPLOG_AXIS_TICK_FONT_SIZE","STRIPLOG_AXIS_TITLE_FONT_SIZE","normalizeAxisTitle","applyStriplogLayoutDefaults","layout","xTitle","yTitle","holeHasData","buildIntervalPoints","isCategorical","rawPoints","seen","fromVal","toVal","depthVal","rawVal","buildCategoricalConfig","template","safe","resolvedCmap","fallbackPalette","uniqueCategories","pickColour","cat","colorByCategory","category","seg","buildNumericConfig","color","isBar","isMarkersOnly","isLineOnly","lineColor","markerColor","baseTrace","errorConfig","buildPlotConfig","buildCategoricalStripLogConfig","categoryCol","DEFAULT_PALETTE","buildTadpoleConfig","tailScale","colorBy","palette","depthCol","dipCol","azCol","colorMap","byCat","shapes","group","length","showLegend","buildStructuralStripConfig","labelCol","records","lv","textY","texts","rec","wrapComment","text","charsPerLine","words","lines","current","word","buildCommentsConfig","commentCol","bgColor","borderColor","textColor","raw","comment","textXs","textYs","hovers","hasComment","buildStrikeDipSymbol","symbolSize","xCol","yCol","strike","strikeRad","dxS","dyS","tickLen","dxD","dyD","DEFAULT_NUMERIC_CHART_TYPE","resolveChartType","requestedChartType","chartOptions","opt","TracePlot","graph","holeOptions","propertyOptions","onConfigChange","containerRef","useRef","selectedHoleId","effectiveChartType","renderError","setRenderError","useState","useEffect","isComment","isTadpole","target","plotData","plotConfig","Plotly","resizeObserver","jsx","jsxs","e","mergeHoleSets","primary","extra","eh","buildCommentPoints","useDrillholeTraceGrid","initialFocusedHoleId","sourceFile","extraHoles","setHoles","setHoleIds","setNumericProps","setCategoricalProps","setCommentProps","setColumnMeta","setDefaultProp","setTraceConfigs","setError","setFocusedHoleId","loadingHoles","setLoadingHoles","loadedSourceFileRef","uniqueIds","newIds","orderedHoleIds","cfg","already","loading","props","configs","useMemo","labeledHoleOptions","traceGraphs","allProps","holePropertyOptions","handleConfigChange","index","patch","ASSAY_COLOR_PALETTE_10","buildEqualRangeColorScale","colors","finiteValues","binCount","bins","percentileLow","percentileHigh","idxLow","idxHigh","lower","upper","formatBinLabel","formatVal","getEqualRangeBinIndex","scale","bin","getEqualRangeColor","fallbackColor","normalizePoint","projectTraceToSection","origin","ox","oy","cosA","sinA","sectionWindow","width","projected","half","planView","depthSlice","top","bottom","sectionView","section","getHoleId","tracesAsSegments","segments","payload","intervalsAsTubes","radius","annotationsFromIntervals","syncSelectables","sceneCtx","DEFAULT_COLOR_MAP","resolveColor","structureType","map","hex","THREE","dipAzimuthToNormal","buildStructuralDiscs","discThickness","opacity","yAxis","xVal","yVal","zVal","dipVal","azVal","normal","geom","mat","mesh","setStructuralDiscs","clearStructuralDiscs","maxDiscs","input","sampled","enriched","child","setStructuralDiscsVisible","visible","_overlayIdCounter","normalizeBounds","bounds","minX","minY","maxX","maxY","height","loadTexture","url","createdObjectUrl","texture","createRasterOverlay","elevation","renderOrder","normalizedBounds","centerX","centerY","geometry","material","addRasterOverlay","layer","removeRasterOverlay","setRasterOverlayOpacity","setRasterOverlayVisibility","setRasterOverlayElevation","getRasterOverlay","listRasterOverlays","clearRasterOverlays","buildViewSignature","viewState","toNum","_e","_f","_g","_h","_i","getViewState","setViewState","camera","up","emitViewChangeIfNeeded","now","signature","fitCameraToBounds","minZ","maxZ","centerZ","sizeX","sizeY","sizeZ","distance","recenterCameraToOrigin","lookDown","pan","dolly","focusOnLastBounds","padding","FOV_MIN_DEG","FOV_MAX_DEG","setFov","fovDeg","clampedFov","currentDist","currentFovRad","frustumHeight","newFovRad","newDist","dir","setControlMode","mode","LOW_ASSAY_GREY","getMeasuredDepthRange","p2","md2","segStart","segEnd","getWeightedIntervalValue","assayIntervals","weightTotal","candidate","overlapStart","overlap","getAssaySegmentColor","assayScale","colorHex","getCategoryHexColor","seededUnit","normalizeDrillholeRenderOptions","collectAssayValues","assayIntervalsByHole","selectedAssayVariable","allAssayValues","buildHoleUserData","normalizeHoleKey","randomSegmentColor","segmentIndex","seed","base","band","hash","getDominantCategory","bestOverlap","resolveAssayIntervalsForHole","exact","byNormalized","getSegmentColor","depthRange","setDrillholes","clearDrillholes","preserveView","isCategoricalVariable","tmpVec","defaultColor","sphereGeom","sphereMat","sphere","cylinderGeom","segmentColor","cylinderMat","line","GLOW_COLOR","EDGE_STRENGTH","EDGE_THICKNESS","EDGE_GLOW","initSelectionGlow","renderer","scene","container","composer","EffectComposer","renderPass","RenderPass","resolution","outlinePass","OutlinePass","resizeGlow","applySelection","object","disposeSelectionGlow","FACE_DEFS","setBlocks","selectedProperty","clearBlocks","autoCenter","rawMinX","rawMaxX","rawMinY","rawMaxY","rawMinZ","rawMaxZ","offX","offY","offZ","bkey","blockSet","positions","normals","indices","quadToBlock","vi","bx","by","bz","cx","cy","cz","g","face","nbx","nby","nbz","vBase","sx","sy","sz","block","setBlockOpacity","getBlockHighlightMesh","blockRow","offset","updateSelectionFromPointer","hits","hit","obj","quadIndex","attachCanvasClickHandler","event","gizmoRect","rect","localX","localY","blockIntersects","blockObj","blockData","drillHits","structHits","drillDist","Baselode3DScene","ambientLight","directionalLight","axesHelper","OrbitControls","FlyControls","ViewportGizmo","_attachCanvasClickHandler","animate","delta","_clearBlocks","_clearDrillholes","_clearStructuralDiscs","_clearRasterOverlays","_setDrillholes","_setBlocks","_setBlockOpacity","_setStructuralDiscs","_setStructuralDiscsVisible","handler","objects","_updateSelectionFromPointer","_addRasterOverlay","_removeRasterOverlay","_setRasterOverlayOpacity","_setRasterOverlayVisibility","_setRasterOverlayElevation","_getRasterOverlay","_listRasterOverlays","Baselode3DControls","controlMode","onToggleFly","onRecenter","onLookDown","onFit","BlockModelWidget","properties","onPropertyChange","onOpacityChange","propertyStats","clickedBlock","onPopupClose","DEFAULT_LOD_BREAKPOINTS","BASE_PIXELS_PER_METRE","selectPhotoLodUrl","photo","zoom","lodBreakpoints","lodUrls","selectedKey","bp","sortPhotosByDepth","photos","groupPhotosBySet","sets","buildDepthMarkers","minDepth","intervalMetres","markers","epsilon","rounded","depthMarkerInterval","defaultTrayFilename","buildTrayPhotos","trays","thumbBaseUrl","fullBaseUrl","photoSet","getFilename","thumb","full","tray","filename","set","depthIntervalToPixels","depthInterval","basePixelsPerMetre","BASE_ZOOM","ZOOM_FACTOR","SCALE_MIN","SCALE_MAX","CorePhotoTable","initialZoom","controlledTransform","onTransformChange","internalTransform","setInternalTransform","transform","transformRef","setTransform","useCallback","updater","dragging","setDragging","dragOrigin","viewportRef","setNames","allTo","pixelsPerMetre","totalHeight","imageWidth","lodZoom","handleWheel","factor","newScale","ratio","el","handleMouseDown","handleMouseMove","tx","ty","handleMouseUp","setName","fromDepth","toDepth","src","CorePhotoViewer","defaultPhotoSet","loadGradeBlocksFromJson","blocks","gradeBlockToThreeGeometry","positionData","indexData","addGradeBlocksToScene","defaultOpacity","transparent","edgeGeo","edgeMat","edgeLines"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"baselode.js","sources":["../src/data/datamodel.js","../src/data/keying.js","../src/data/assayFieldSets.js","../src/data/dataErrorUtils.js","../src/data/assayLoader.js","../src/data/csvRowUtils.js","../src/data/traceGridConfig.js","../src/data/columnMeta.js","../src/data/assayDataLoader.js","../src/data/desurvey.js","../src/data/desurveyMethods.js","../src/data/drillholeLoader.js","../src/data/datasetLoader.js","../src/data/blockModelLoader.js","../src/data/structuralLoader.js","../src/data/unifiedLoader.js","../src/data/intercepts.js","../src/data/geophysicsLoader.js","../src/data/lasLoader.js","../src/data/structuralPositions.js","../src/viz/baselodeTemplate.js","../src/viz/baselodeDarkTemplate.js","../src/viz/colourMap.js","../src/viz/drillholeViz.js","../src/viz/structuralViz.js","../src/viz/TracePlot.jsx","../src/viz/useDrillholeTraceGrid.jsx","../src/viz/assayColorScale.js","../src/viz/view2d.js","../src/viz/view3dPayload.js","../src/viz/sceneSelectables.js","../src/viz/structuralScene.js","../src/viz/rasterOverlayScene.js","../src/viz/baselode3dCameraControls.js","../src/viz/drillholeScene.js","../src/viz/stripLogScene.js","../src/viz/selectionGlow.js","../src/viz/blockModelScene.js","../src/viz/sceneClickHandler.js","../src/viz/baselode3dScene.js","../src/viz/Baselode3DControls.jsx","../src/viz/BlockModelWidget.jsx","../src/viz/corePhotoViz.js","../src/viz/CorePhotoTable.jsx","../src/viz/CorePhotoViewer.jsx","../src/grade_blocks/gradeBlockLoader.js"],"sourcesContent":["/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Baselode Open Data Model for JavaScript/TypeScript\n *\n * Provides a consistent schema for data handling throughout the library.\n *\n * Individual data loaders apply common column mapping, but also accept user-provided column maps to handle variations in source data.\n */\n\n// Standard field names\nexport const HOLE_ID = \"hole_id\";\nexport const LATITUDE = \"latitude\";\nexport const LONGITUDE = \"longitude\";\nexport const ELEVATION = \"elevation\";\nexport const AZIMUTH = \"azimuth\";\nexport const DIP = \"dip\";\nexport const FROM = \"from\";\nexport const TO = \"to\";\nexport const MID = \"mid\";\nexport const PROJECT_ID = \"project_id\";\nexport const EASTING = \"easting\";\nexport const NORTHING = \"northing\";\nexport const CRS = \"crs\";\nexport const DEPTH = \"depth\";\nexport const ALPHA = \"alpha\";\nexport const BETA = \"beta\";\nexport const STRIKE = \"strike\";\nexport const GEOLOGY_CODE = \"geology_code\";\nexport const GEOLOGY_DESCRIPTION = \"geology_description\";\n\n/**\n * Minimum expected columns for drillhole collars\n * The collar forms the basis for hole_id and spatial location, so it is expected to exist in all datasets and be standardized as much as possible.\n */\nexport const BASELODE_DATA_MODEL_DRILL_COLLAR = {\n // A unique hole identifier across the entire dataset and all future data sets\n [HOLE_ID]: \"string\",\n // The hole ID from the original collar source\n \"datasource_hole_id\": \"string\",\n // The project ID or project code from the original collar source, if available\n [PROJECT_ID]: \"string\",\n // The latitude of the collar, in decimal degrees (WGS84)\n [LATITUDE]: \"number\",\n // The longitude of the collar, in decimal degrees (WGS84)\n [LONGITUDE]: \"number\",\n // The elevation of the collar, in meters above sea level (WGS84)\n [ELEVATION]: \"number\",\n // The easting coordinate of the collar, in meters (projected CRS)\n [EASTING]: \"number\",\n // The northing coordinate of the collar, in meters (projected CRS)\n [NORTHING]: \"number\",\n // The coordinate reference system of the collar coordinates for easting/northing, as an EPSG code or proj string\n [CRS]: \"string\"\n};\n\nexport const BASELODE_DATA_MODEL_DRILL_SURVEY = {\n // The unique hole id that maps to the collar and any other data tables\n [HOLE_ID]: \"string\",\n // The depth along the hole where the survey measurement was taken / started\n [DEPTH]: \"number\",\n // The depth along the hole where the survey measurement ended, if applicable (some surveys are point measurements and may not have a 'to' depth)\n [TO]: \"number\",\n // The azimuth of the hole at the survey depth, in degrees from north\n [AZIMUTH]: \"number\",\n // The dip of the hole at the survey depth, in degrees from horizontal (negative values indicate downward inclination)\n [DIP]: \"number\"\n};\n\nexport const BASELODE_DATA_MODEL_DRILL_ASSAY = {\n // The unique hole id that maps to the collar and any other data tables\n [HOLE_ID]: \"string\",\n // The depth along the hole where the assay interval starts\n [FROM]: \"number\",\n // The depth along the hole where the assay interval ends\n [TO]: \"number\",\n // The midpoint depth of the assay interval\n [MID]: \"number\",\n // assay value columns are variable and not standardized here. \n // Assays may be flattened (one column per assay type) or long (one row per assay type with an additional 'assay_type' column)\n};\n\nexport const BASELODE_DATA_MODEL_DRILL_GEOLOGY = {\n [HOLE_ID]: \"string\",\n [FROM]: \"number\",\n [TO]: \"number\",\n [MID]: \"number\",\n [GEOLOGY_CODE]: \"string\",\n [GEOLOGY_DESCRIPTION]: \"string\"\n};\n\n/**\n * Structural point data model schema\n */\nexport const BASELODE_DATA_MODEL_STRUCTURAL_POINT = {\n [HOLE_ID]: \"string\",\n [DEPTH]: \"number\",\n [DIP]: \"number\",\n [AZIMUTH]: \"number\",\n [ALPHA]: \"number\",\n [BETA]: \"number\",\n \"comments\": \"string\",\n};\n\n/**\n * Geophysics interval data model schema.\n * Value columns (gamma, density, resistivity, etc.) are variable and not standardized.\n * Null sentinels (e.g. -999.25 from LAS-derived sources) are replaced with null on load.\n */\nexport const BASELODE_DATA_MODEL_GEOPHYSICS = {\n [HOLE_ID]: \"string\",\n [FROM]: \"number\",\n [TO]: \"number\",\n [MID]: \"number\",\n // value columns are variable — not standardized here\n};\n\n/** Standard null sentinel value in LAS-derived geophysics data */\nexport const GEOPHYSICS_NULL_SENTINEL = -999.25;\n\n/**\n * This column map is used to make a 'best guess' for mapping common variations in source column names to the baselode data model.\n * It is applied in the standardizeColumns function, but users can also provide their own column map to override or extend this mapping as needed.\n * The keys from the input source are normalized to lowercase and stripped of whitespace for more robust matching.\n * This dictionary is stored for human readability, then pivoted to make lookup quicker in code.\n * Be cautious of not mapping a source column to multiple baselode columns, as this can lead to unpredictable results.\n */\nexport const DEFAULT_COLUMN_MAP = {\n [HOLE_ID]: [\"hole_id\", \"holeid\", \"hole id\", \"hole-id\"],\n \"datasource_hole_id\": [\"datasource_hole_id\", \"datasourceholeid\", \"datasource hole id\", \"datasource-hole-id\", \"company_hole_id\", \"companyholeid\", \"company hole id\", \"company-hole-id\"],\n [PROJECT_ID]: [\"project_id\", \"projectid\", \"project id\", \"project-id\", \"project_code\", \"projectcode\", \"project code\", \"project-code\", \"companyId\", \"company_id\", \"companyid\", \"company id\", \"company-id\", \"dataset\", \"project\"],\n [LATITUDE]: [\"latitude\", \"lat\"],\n [LONGITUDE]: [\"longitude\", \"lon\"],\n [ELEVATION]: [\"elevation\", \"rl\", \"elev\", \"z\"],\n [EASTING]: [\"easting\", \"x\"],\n [NORTHING]: [\"northing\", \"y\"],\n [CRS]: [\"crs\", \"epsg\", \"projection\"],\n [FROM]: [\"from\", \"depth_from\", \"from_depth\", \"samp_from\", \"sample_from\", \"sampfrom\", \"fromdepth\"],\n [TO]: [\"to\", \"depth_to\", \"to_depth\", \"samp_to\", \"sample_to\", \"sampto\", \"todepth\"],\n [GEOLOGY_CODE]: [\n \"geology_code\",\n \"geologycode\",\n \"lith1\",\n \"lith1code\",\n \"lith1_code\",\n \"lithology\",\n \"plot_lithology\",\n \"rock1\"\n ],\n [GEOLOGY_DESCRIPTION]: [\n \"geology_description\",\n \"geologydescription\",\n \"geology_comment\",\n \"geologycomment\",\n \"geology comment\",\n \"lithology_comment\",\n \"lithology comment\",\n \"description\",\n \"comments\"\n ],\n [AZIMUTH]: [\"azimuth\", \"az\", \"dip_direction\", \"dipdir\", \"dip direction\", \"dipdrn\", \"dipdirection\", \"dip_dir\", \"computed_plane_azimuth\", \"calc_dipdir\", \"calc_dipdir_deg\", \"dipdir_calc\", \"dipdirect_calc\"],\n [DIP]: [\"dip\", \"computed_plane_dip\", \"calc_dip\", \"calc_dip_deg\", \"dip_calc\"],\n [ALPHA]: [\"alpha\", \"alpha_angle\", \"alpha_angle_deg\", \"alpha_2\"],\n [BETA]: [\"beta\", \"beta_angle\", \"beta_angle_deg\", \"beta_2\"],\n \"declination\": [\"declination\", \"dec\"],\n [DEPTH]: [\"depth\", \"survey_depth\", \"surveydepth\", \"md\", \"measured_depth\", \"dept\"],\n [STRIKE]: [\"strike\", \"str\"]\n};\n\n/**\n * Pivot the DEFAULT_COLUMN_MAP for efficient reverse lookup\n * Maps normalized column names -> standardized baselode column names\n * @private\n */\nexport const _COLUMN_LOOKUP = {};\nfor (const [standardCol, variations] of Object.entries(DEFAULT_COLUMN_MAP)) {\n for (const variation of variations) {\n const normalized = variation.toLowerCase().trim();\n _COLUMN_LOOKUP[normalized] = standardCol;\n }\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { _COLUMN_LOOKUP, DEFAULT_COLUMN_MAP, HOLE_ID } from './datamodel.js';\n\n/**\n * Normalize a field name to lowercase with underscores\n * @param {string} name - The field name to normalize\n * @returns {string} - The normalized field name\n */\nexport function normalizeFieldName(name) {\n return (name || '').toString().trim().toLowerCase().replace(/\\s+/g, '_');\n}\n\n/**\n * Standardize column names in a row object using the baselode data model\n * @param {Object} row - The row object with arbitrary column names\n * @param {Object} [columnMap] - Optional column map to use (defaults to DEFAULT_COLUMN_MAP)\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Object} - The row object with standardized column names\n */\nexport function standardizeColumns(row, columnMap = null, sourceColumnMap = null) {\n const lookup = { ..._COLUMN_LOOKUP };\n \n // Add user-provided column mappings\n if (sourceColumnMap) {\n for (const [rawName, expectedName] of Object.entries(sourceColumnMap)) {\n if (rawName != null && expectedName != null) {\n const normalizedKey = normalizeFieldName(rawName);\n const normalizedValue = normalizeFieldName(expectedName);\n lookup[normalizedKey] = normalizedValue;\n }\n }\n }\n\n const renamed = {};\n for (const [col, value] of Object.entries(row)) {\n const key = normalizeFieldName(col);\n const mapped = lookup[key] || key;\n renamed[mapped] = value;\n }\n\n return renamed;\n}\n\n/**\n * Standardize an array of rows (e.g., from CSV parsing)\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map to use\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeRowArray(rows, columnMap = null, sourceColumnMap = null) {\n return rows.map(row => standardizeColumns(row, columnMap, sourceColumnMap));\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nexport const ASSAY_NON_VALUE_FIELDS = new Set([\n 'hole_id',\n 'holeid',\n 'id',\n 'holeId',\n 'project_code',\n 'project',\n 'latitude',\n 'longitude',\n 'lat',\n 'lng',\n 'elevation',\n 'dip',\n 'azimuth',\n 'holetype',\n 'shape',\n 'anumber',\n 'collarid',\n 'companyholeid',\n 'company_hole_id',\n 'samp_from',\n 'samp_to',\n 'sample_from',\n 'sample_to',\n 'from',\n 'to',\n 'depth_from',\n 'depth_to',\n 'fromdepth',\n 'todepth',\n 'comment',\n 'z'\n]);","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nconst DATA_LOG_PREFIX = '[baselode:data]';\n\n/**\n * Convert any value to an Error object\n * @param {*} error - Value to convert (Error, string, or other)\n * @param {string} fallbackMessage - Message to use if error is not Error or string\n * @returns {Error} Error object\n */\nexport function toError(error, fallbackMessage = 'Unknown error') {\n if (error instanceof Error) return error;\n const message = typeof error === 'string' && error.trim() ? error : fallbackMessage;\n return new Error(message);\n}\n\n/**\n * Wrap an error with contextual information about where it occurred\n * @param {string} context - Context description (e.g., function name)\n * @param {*} error - Original error\n * @param {string} fallbackMessage - Fallback message if error cannot be parsed\n * @returns {Error} Wrapped error with context and original as cause\n */\nexport function withDataErrorContext(context, error, fallbackMessage = 'Operation failed') {\n const baseError = toError(error, fallbackMessage);\n const wrapped = new Error(`${context}: ${baseError.message}`);\n wrapped.cause = baseError;\n return wrapped;\n}\n\n/**\n * Log a warning message with optional error object\n * @param {string} message - Warning message\n * @param {Error} [error] - Optional error object to log\n */\nexport function logDataWarning(message, error) {\n if (error !== undefined) {\n console.warn(`${DATA_LOG_PREFIX} ${message}`, error);\n return;\n }\n console.warn(`${DATA_LOG_PREFIX} ${message}`);\n}\n\n/**\n * Log an informational message\n * @param {string} message - Info message\n */\nexport function logDataInfo(message) {\n console.info(`${DATA_LOG_PREFIX} ${message}`);\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { ASSAY_NON_VALUE_FIELDS } from './assayFieldSets.js';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, FROM, TO, PROJECT_ID } from './datamodel.js';\n\n/**\n * Normalize a raw CSV row to use standardized column names\n * @private\n */\nconst normalizeRow = (rawRow, sourceColumnMap = null) => standardizeColumns(rawRow, null, sourceColumnMap);\n\n/**\n * Extract hole ID from a normalized row\n * @private\n * @param {Object} row - Normalized row object\n * @returns {{holeId: string}} Object containing hole ID\n */\nfunction extractIdFields(row) {\n const holeId = row[HOLE_ID];\n return { holeId };\n}\n\n/**\n * Extract and validate assay interval data from a row\n * @private\n * @param {Object} row - Normalized row object\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object|null} Interval object or null if invalid\n */\nfunction extractInterval(row, sourceColumnMap = null) {\n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n if (!holeId) return null;\n\n const project = row[PROJECT_ID] || row.project || row.project_code;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) return null;\n\n return {\n holeId,\n project,\n from,\n to,\n ...row\n };\n}\n\n/**\n * Convert array of intervals to a hole object with points\n * @private\n * @param {string} holeId - Hole identifier\n * @param {Array<Object>} intervals - Array of interval objects\n * @returns {{id: string, project: string, points: Array<Object>}} Hole object\n */\nfunction intervalsToHole(holeId, intervals) {\n const sorted = intervals.sort((a, b) => a.from - b.from);\n const points = [];\n sorted.forEach((iv) => {\n const { from, to, project, ...rest } = iv;\n const pointData = {\n z: from,\n from,\n to,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n ...rest\n };\n points.push(pointData);\n points.push({ ...pointData, z: to });\n });\n return { id: holeId, project: sorted[0]?.project, points };\n}\n\n/**\n * Parse assay CSV to extract unique hole IDs (quick pass, no interval data)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<string>>} Array of unique hole IDs\n */\nexport function parseAssayHoleIds(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const holeIds = new Set();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const hid = row[HOLE_ID];\n if (hid !== undefined && `${hid}`.trim() !== '') {\n holeIds.add(`${hid}`.trim());\n }\n },\n complete: () => resolve(Array.from(holeIds)),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIds', error))\n });\n });\n}\n\n/**\n * Check if a row has at least one non-null assay value\n * @private\n * @param {Object} row - Normalized row object\n * @returns {boolean} True if row contains assay data\n */\nfunction hasAssayValue(row) {\n return Object.entries(row || {}).some(([k, v]) => {\n if (ASSAY_NON_VALUE_FIELDS.has(k)) return false;\n if (v === undefined || v === null) return false;\n if (typeof v === 'string' && v.trim() === '') return false;\n return true;\n });\n}\n\n/**\n * Parse assay CSV to extract hole IDs that have at least one assay value (quick pass)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<{holeId: string}>>} Array of objects with hole IDs that have assay data\n */\nexport function parseAssayHoleIdsWithAssays(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const byHole = new Map();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n if (!hasAssayValue(row)) return;\n const ids = extractIdFields(row);\n const hid = ids.holeId;\n if (hid !== undefined && `${hid}`.trim() !== '') {\n const key = `${hid}`.trim();\n if (!byHole.has(key)) {\n byHole.set(key, {\n holeId: key\n });\n }\n }\n },\n complete: () => resolve(Array.from(byHole.values())),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIdsWithAssays', error))\n });\n });\n}\n\n/**\n * Parse assay CSV for a single hole's intervals\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to extract\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object|null>} Hole object with intervals or null if not found\n */\nexport function parseAssayHole(file, holeId, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const wanted = `${holeId}`.trim();\n if (!wanted) {\n reject(withDataErrorContext('parseAssayHole', new Error('Missing hole id')));\n return;\n }\n const intervals = [];\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (`${interval.holeId}`.trim() !== wanted) return;\n intervals.push(interval);\n },\n complete: () => {\n if (!intervals.length) {\n resolve(null);\n return;\n }\n const hole = intervalsToHole(wanted, intervals);\n resolve(hole);\n },\n error: (error) => reject(withDataErrorContext('parseAssayHole', error))\n });\n });\n}\n\n/**\n * Parse complete assay CSV file with all holes and intervals (eager load)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<{holes: Array<Object>}>} Object containing array of all holes with intervals\n */\nexport function parseAssaysCSV(file, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow) => {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (!byHole.has(interval.holeId)) byHole.set(interval.holeId, []);\n byHole.get(interval.holeId).push(interval);\n });\n\n const holes = Array.from(byHole.entries()).map(([hid, intervals]) => intervalsToHole(hid, intervals));\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseAssaysCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { normalizeFieldName, standardizeColumns } from './keying.js';\n\n/**\n * Normalize a CSV row by converting all keys to lowercase with underscores\n * @deprecated Use standardizeColumns from keying.js instead for full data model support\n */\nexport function normalizeCsvRow(row = {}) {\n const normalized = {};\n Object.entries(row || {}).forEach(([key, value]) => {\n if (!key) return;\n normalized[normalizeFieldName(key)] = value;\n });\n return normalized;\n}\n\n/**\n * Pick the first present value from a list of keys in a normalized row\n * @param {Object} normalized - The normalized row object\n * @param {Array<string>} keys - Array of keys to check\n * @param {*} fallback - Fallback value if none found\n * @returns {*} - The first present value or fallback\n */\nexport function pickFirstPresent(normalized = {}, keys = [], fallback) {\n for (const key of keys) {\n const value = normalized[key];\n if (value !== undefined && value !== null && `${value}`.trim() !== '') {\n return value;\n }\n }\n return fallback;\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/** Default number of trace plots in a grid */\nexport const DEFAULT_TRACE_PLOT_COUNT = 4;\n\n/**\n * Reorder hole IDs to place a focused hole first\n * @param {Array<string>} ids - Array of hole IDs\n * @param {string} focusId - Hole ID to place first\n * @returns {Array<string>} Reordered array with focusId first if found\n */\nexport function reorderHoleIds(ids = [], focusId = '') {\n if (!ids.length) return [];\n if (!focusId) return ids;\n const matchIdx = ids.findIndex((id) => id === focusId);\n if (matchIdx === -1) return ids;\n const selected = ids[matchIdx];\n const rest = ids.filter((_, idx) => idx !== matchIdx);\n return [selected, ...rest];\n}\n\n/**\n * Determine appropriate chart type for a property.\n * Comment columns always use 'comment'; categorical → 'categorical'; numeric → requested or default.\n *\n * @param {Object} options - Configuration options\n * @param {string} options.property - Property name\n * @param {string} options.chartType - Requested chart type\n * @param {Array<string>} options.categoricalProps - List of categorical property names\n * @param {Array<string>} options.commentProps - List of comment property names\n * @param {string} options.numericDefaultChartType - Default chart type for numeric properties\n * @returns {string} Coerced chart type ('comment', 'categorical', 'line', 'markers+line', etc.)\n */\nexport function coerceChartTypeForProperty({\n property = '',\n chartType = '',\n categoricalProps = [],\n commentProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n if (!property) return chartType || numericDefaultChartType;\n if (commentProps.includes(property)) return 'comment';\n if (categoricalProps.includes(property)) return 'categorical';\n if (property === 'dip') return 'tadpole';\n if (!chartType || chartType === 'categorical' || chartType === 'comment' || chartType === 'tadpole') return numericDefaultChartType;\n return chartType;\n}\n\n/**\n * Build trace plot configuration objects for a grid of hole IDs\n * @param {Object} options - Configuration options\n * @param {Array<string>} options.holeIds - Array of hole IDs\n * @param {string} options.focusedHoleId - Hole ID to focus (place first)\n * @param {number} options.plotCount - Number of plots in grid\n * @param {string} options.defaultProp - Default property to display\n * @param {Array<string>} options.categoricalProps - List of categorical properties\n * @param {Array<string>} options.commentProps - List of comment properties\n * @param {string} options.numericDefaultChartType - Default chart type for numeric props\n * @returns {Array<{holeId: string, property: string, chartType: string}>} Array of trace configurations\n */\nexport function buildTraceConfigsForHoleIds({\n holeIds = [],\n focusedHoleId = '',\n plotCount = DEFAULT_TRACE_PLOT_COUNT,\n defaultProp = '',\n categoricalProps = [],\n commentProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n const ordered = reorderHoleIds(holeIds, focusedHoleId);\n return Array.from({ length: plotCount }).map((_, idx) => {\n const holeId = ordered[idx] || holeIds[idx] || '';\n const chartType = coerceChartTypeForProperty({\n property: defaultProp,\n chartType: '',\n categoricalProps,\n commentProps,\n numericDefaultChartType\n });\n return {\n holeId,\n property: defaultProp,\n chartType\n };\n });\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Column display type classification for strip log visualization.\n *\n * Every loaded column is assigned a display type that drives:\n * - which columns appear in strip log property dropdowns\n * - which chart type options are offered for a given column\n */\n\n/** Numeric measurement column — rendered as bar, marker, or line */\nexport const DISPLAY_NUMERIC = 'numeric';\n\n/** Categorical string column — rendered as coloured interval bands */\nexport const DISPLAY_CATEGORICAL = 'categorical';\n\n/** Free-text comment column — rendered as labelled interval boxes */\nexport const DISPLAY_COMMENT = 'comment';\n\n/** Hidden column — not shown in strip log dropdowns */\nexport const DISPLAY_HIDDEN = 'hidden';\n\n/** Tadpole log — dip head + azimuth tail, shows both dip and azimuth */\nexport const DISPLAY_TADPOLE = 'tadpole';\n\n/**\n * Available chart type options for each display type.\n * Used to populate the chart-type dropdown in TracePlot.\n */\nexport const CHART_OPTIONS = {\n [DISPLAY_NUMERIC]: [\n { value: 'bar', label: 'Bars' },\n { value: 'markers', label: 'Markers' },\n { value: 'markers+line', label: 'Markers + Line' },\n { value: 'line', label: 'Line only' },\n ],\n [DISPLAY_CATEGORICAL]: [\n { value: 'categorical', label: 'Categorical bands' },\n ],\n [DISPLAY_COMMENT]: [\n { value: 'comment', label: 'Comments' },\n ],\n [DISPLAY_TADPOLE]: [\n { value: 'tadpole', label: 'Tadpole' },\n ],\n [DISPLAY_HIDDEN]: [],\n};\n\n/**\n * Column names (lowercased) that are always hidden from strip log views.\n * Covers hole IDs, project codes, coordinates, depth/interval fields, and geometry.\n */\nexport const HIDDEN_COLUMNS = new Set([\n // Hole identifiers\n 'hole_id', 'holeid', 'id', 'holetype',\n 'datasource_hole_id',\n 'anumber', 'collarid', 'companyholeid', 'company_hole_id', 'company_id',\n // Project codes\n 'project_id', 'project_code', 'project', 'projectcode', 'projectid',\n // Geographic coordinates\n 'latitude', 'longitude', 'lat', 'lon', 'lng',\n 'easting', 'northing', 'x', 'y', 'z',\n 'elevation', 'elev', 'rl',\n // Depth / interval columns\n 'from', 'to', 'mid', 'depth', 'md',\n 'samp_from', 'samp_to', 'sample_from', 'sample_to',\n 'depth_from', 'depth_to', 'fromdepth', 'todepth',\n // Geometry / CRS\n 'shape', 'geometry', 'crs', 'epsg',\n // Internal / synthetic columns\n 'data_source', '_hole_key', '_hole_id_key',\n]);\n\n/**\n * Column names (lowercased) that map to the comment display type.\n * These are free-text description columns rendered as labelled interval boxes.\n */\nexport const COMMENT_COLUMN_NAMES = new Set([\n 'comments', 'comment', 'notes', 'note',\n 'description', 'remarks', 'remark',\n 'log_description', 'struct_comment', 'structcomment',\n 'geology_description',\n]);\n\n/**\n * Classify columns in a dataset by their display type.\n *\n * Rules applied in order:\n * 1. Columns in HIDDEN_COLUMNS → DISPLAY_HIDDEN\n * 2. Columns in COMMENT_COLUMN_NAMES with ≥1 non-empty value → DISPLAY_COMMENT\n * 3. All-null/empty columns → DISPLAY_HIDDEN (silently dropped)\n * 4. Columns with at least one finite number → DISPLAY_NUMERIC\n * 5. Remaining non-empty columns → DISPLAY_CATEGORICAL\n *\n * @param {Array<Object>} rows - Flat array of row objects (assay or structural points)\n * @returns {{\n * byType: Object<string, string>,\n * numericCols: string[],\n * categoricalCols: string[],\n * commentCols: string[],\n * }}\n */\nexport function classifyColumns(rows) {\n if (!rows?.length) {\n return { byType: {}, numericCols: [], categoricalCols: [], commentCols: [] };\n }\n\n // Collect all column names across all rows\n const allCols = new Set(rows.flatMap((r) => Object.keys(r || {})));\n const byType = {};\n\n for (const col of allCols) {\n const normalized = col.toLowerCase().trim();\n\n // Always hidden: ID / coordinate / depth columns\n if (HIDDEN_COLUMNS.has(normalized) || HIDDEN_COLUMNS.has(col)) {\n byType[col] = DISPLAY_HIDDEN;\n continue;\n }\n\n // Comment-type: named text-description columns\n if (COMMENT_COLUMN_NAMES.has(normalized)) {\n const hasValue = rows.some((r) => {\n const v = r[col];\n return v != null && String(v).trim() !== '' && String(v) !== 'null';\n });\n byType[col] = hasValue ? DISPLAY_COMMENT : DISPLAY_HIDDEN;\n continue;\n }\n\n // Classify by content: all-empty → hidden; numeric → numeric; else → categorical\n let hasNumeric = false;\n let hasValue = false;\n for (const r of rows) {\n const v = r[col];\n if (v == null || (typeof v === 'string' && v.trim() === '')) continue;\n hasValue = true;\n if (typeof v === 'number' && Number.isFinite(v)) {\n hasNumeric = true;\n break;\n }\n }\n\n if (!hasValue) {\n byType[col] = DISPLAY_HIDDEN;\n } else if (hasNumeric) {\n byType[col] = DISPLAY_NUMERIC;\n } else {\n byType[col] = DISPLAY_CATEGORICAL;\n }\n }\n\n return {\n byType,\n numericCols: Object.entries(byType).filter(([, t]) => t === DISPLAY_NUMERIC).map(([k]) => k),\n categoricalCols: Object.entries(byType).filter(([, t]) => t === DISPLAY_CATEGORICAL).map(([k]) => k),\n commentCols: Object.entries(byType).filter(([, t]) => t === DISPLAY_COMMENT).map(([k]) => k),\n };\n}\n\n/**\n * Get the available chart type options for a given display type.\n * Returns NUMERIC options as a fallback for unknown types.\n *\n * @param {string} displayType - One of the DISPLAY_* constants\n * @returns {Array<{value: string, label: string}>}\n */\nexport function getChartOptions(displayType) {\n return CHART_OPTIONS[displayType] ?? CHART_OPTIONS[DISPLAY_NUMERIC];\n}\n\n/**\n * Get the default chart type value for a display type.\n *\n * @param {string} displayType\n * @returns {string}\n */\nexport function defaultChartType(displayType) {\n const opts = getChartOptions(displayType);\n if (!opts.length) return 'markers+line';\n if (displayType === DISPLAY_NUMERIC) return 'line';\n return opts[0].value;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { parseAssayHole, parseAssayHoleIdsWithAssays, parseAssaysCSV } from './assayLoader.js';\nimport { buildTraceConfigsForHoleIds, reorderHoleIds } from './traceGridConfig.js';\nimport { classifyColumns } from './columnMeta.js';\n\nexport { reorderHoleIds };\n\n/**\n * Derive property names from hole data using column display-type classification.\n *\n * @param {Array<Object>} holes - Array of hole objects with points containing assay data\n * @returns {{\n * numericProps: string[],\n * categoricalProps: string[],\n * commentProps: string[],\n * columnMeta: Object,\n * defaultProp: string\n * }} Property classification\n */\nexport function deriveAssayProps(holes = []) {\n const points = holes.flatMap((h) => h.points || []);\n const { numericCols, categoricalCols, commentCols, byType } = classifyColumns(points);\n\n const defaultProp = numericCols[0] || categoricalCols[0] || '';\n\n return {\n numericProps: numericCols,\n categoricalProps: categoricalCols,\n commentProps: commentCols,\n columnMeta: byType,\n defaultProp,\n };\n}\n\n/**\n * Load metadata (hole IDs) from an assay CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Array<{holeId: string}>>} Array of hole IDs with assay data\n */\nexport async function loadAssayMetadata(file, config = null) {\n const holeIds = await parseAssayHoleIdsWithAssays(file);\n return holeIds;\n}\n\n/**\n * Load assay intervals for a specific hole from CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to load\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Object|null>} Hole object with assay intervals or null if not found\n */\nexport async function loadAssayHole(file, holeId, config = null) {\n const hole = await parseAssayHole(file, holeId);\n return hole;\n}\n\n/**\n * Build complete assay state from hole data including property analysis and trace configs\n * @param {Array<Object>} holes - Array of hole objects with assay data\n * @param {string} focusedHoleId - Hole ID to focus on (will be first in trace configs)\n * @returns {Object|null} Assay state object with holes, properties, and trace configs\n */\nexport function buildAssayState(holes = [], focusedHoleId = '') {\n if (!holes.length) return null;\n const { numericProps, categoricalProps, commentProps, columnMeta, defaultProp } = deriveAssayProps(holes);\n const holeIds = holes.map((h) => h.id || h.holeId).filter(Boolean);\n const traceConfigs = buildTraceConfigsForHoleIds({\n holeIds,\n focusedHoleId,\n plotCount: 4,\n defaultProp,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'line'\n });\n return {\n holes,\n numericProps,\n categoricalProps,\n commentProps,\n columnMeta,\n defaultProp,\n traceConfigs\n };\n}\n\n/**\n * Load and parse complete assay CSV file into state object\n * @param {File|Blob} file - Assay CSV file\n * @param {string} focusedHoleId - Hole ID to focus on\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object>} Complete assay state\n * @throws {Error} If no valid assay intervals found\n */\nexport async function loadAssayFile(file, focusedHoleId = '', sourceColumnMap = null) {\n const { holes } = await parseAssaysCSV(file, sourceColumnMap);\n const state = buildAssayState(holes, focusedHoleId);\n if (!state) throw new Error('No valid assay intervals found.');\n return state;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, LATITUDE, LONGITUDE, AZIMUTH, DIP, DEPTH, PROJECT_ID } from './datamodel.js';\n\n/**\n * Parse survey CSV file containing downhole survey measurements\n * Expected columns: hole_id, depth, azimuth, dip\n * @param {File|Blob} file - Survey CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<Object>>} Array of normalized survey rows\n */\nexport function parseSurveyCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = results.data\n .map((row) => normalizeRow(row, sourceColumnMap))\n .filter((row) => row[HOLE_ID] && Number.isFinite(row[DEPTH]) && Number.isFinite(row[DIP]) && Number.isFinite(row[AZIMUTH]));\n resolve(rows);\n },\n error: (err) => reject(withDataErrorContext('parseSurveyCSV', err))\n });\n });\n}\n\n/**\n * Normalize a survey row to standardized field names\n * @private\n * @param {Object} row - Raw survey row\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object} Normalized row with standardized field names\n */\nfunction normalizeRow(row, sourceColumnMap = null) {\n const norm = standardizeColumns(row, null, sourceColumnMap);\n\n const holeId = norm[HOLE_ID];\n const project = norm[PROJECT_ID] || norm.project || norm.project_code;\n const lat = toNumber(norm[LATITUDE]);\n const lng = toNumber(norm[LONGITUDE]);\n const surveyDepth = toNumber(norm[DEPTH]);\n const dip = toNumber(norm[DIP]);\n const azimuth = toNumber(norm[AZIMUTH]);\n const maxDepth = toNumber(norm.maxdepth);\n\n return {\n raw: norm,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n [LATITUDE]: lat,\n [LONGITUDE]: lng,\n [DEPTH]: surveyDepth,\n [DIP]: dip,\n [AZIMUTH]: azimuth,\n maxdepth: maxDepth,\n // Legacy field names for backwards compatibility\n project_code: project,\n latitude: lat,\n longitude: lng,\n surveydepth: surveyDepth\n };\n}\n\n/**\n * Convert value to number, returning undefined if not finite\n * @private\n * @param {*} v - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nconst toNumber = (v) => {\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n};\n\n/**\n * Desurvey drillhole traces using minimum curvature method\n * Converts survey measurements (azimuth, dip, depth) to 3D coordinates (x, y, z)\n * @param {Array<Object>} collars - Array of collar objects with location data\n * @param {Array<Object>} surveys - Array of survey measurement objects\n * @returns {Array<{id: string, project: string, points: Array<{x: number, y: number, z: number, md: number, azimuth: number, dip: number, lat: number, lng: number}>, collar: Object}>} Array of desurveyed holes with 3D points\n */\nexport function desurveyTraces(collars, surveys) {\n const collarByKey = new Map();\n collars.forEach((c) => {\n const holeId = (c[HOLE_ID] || c.holeId || c.id || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!collarByKey.has(key)) {\n collarByKey.set(key, c);\n }\n });\n\n const refLat = collars[0]?.lat ?? collars[0]?.[LATITUDE] ?? 0;\n const refLng = collars[0]?.lng ?? collars[0]?.[LONGITUDE] ?? 0;\n const refMetersPerDegLat = 111132;\n const refMetersPerDegLon = 111320 * Math.cos((refLat * Math.PI) / 180);\n\n const grouped = new Map();\n surveys.forEach((s) => {\n const holeId = (s[HOLE_ID] || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(s);\n });\n\n const holes = [];\n grouped.forEach((stations, key) => {\n const collar = collarByKey.get(key);\n if (!collar) return;\n const sorted = stations\n .filter((s) => Number.isFinite(s[DEPTH] ?? s.surveydepth))\n .sort((a, b) => (a[DEPTH] ?? a.surveydepth) - (b[DEPTH] ?? b.surveydepth));\n if (!sorted.length) return;\n\n const lat0 = collar.lat ?? collar[LATITUDE];\n const lng0 = collar.lng ?? collar[LONGITUDE];\n const metersPerDegLat = 111132;\n const metersPerDegLon = 111320 * Math.cos((lat0 * Math.PI) / 180);\n const baseX = (lng0 - refLng) * refMetersPerDegLon;\n const baseY = (lat0 - refLat) * refMetersPerDegLat;\n\n const points = [];\n let accX = 0;\n let accY = 0;\n let accZ = 0;\n\n for (let i = 0; i < sorted.length; i += 1) {\n const curr = sorted[i];\n const prev = sorted[i - 1];\n const currDepth = curr[DEPTH] ?? curr.surveydepth;\n const currAzimuth = curr[AZIMUTH] ?? curr.azimuth;\n const currDip = curr[DIP] ?? curr.dip;\n \n if (!prev) {\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: 0,\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n continue;\n }\n\n const prevDepth = prev[DEPTH] ?? prev.surveydepth;\n const prevAzimuth = prev[AZIMUTH] ?? prev.azimuth;\n const prevDip = prev[DIP] ?? prev.dip;\n \n const deltaMD = currDepth - prevDepth;\n if (deltaMD <= 0) continue;\n\n const inc1 = toInclination(prevDip);\n const inc2 = toInclination(currDip);\n const az1 = degToRad(prevAzimuth);\n const az2 = degToRad(currAzimuth);\n\n const beta = Math.acos(\n Math.sin(inc1) * Math.sin(inc2) * Math.cos(az1 - az2) +\n Math.cos(inc1) * Math.cos(inc2)\n );\n\n const rf = beta > 1e-6 ? (2 / beta) * Math.tan(beta / 2) : 1;\n\n const dx = 0.5 * deltaMD * (Math.sin(inc1) * Math.cos(az1) + Math.sin(inc2) * Math.cos(az2)) * rf;\n const dy = 0.5 * deltaMD * (Math.sin(inc1) * Math.sin(az1) + Math.sin(inc2) * Math.sin(az2)) * rf;\n const dzDown = 0.5 * deltaMD * (Math.cos(inc1) + Math.cos(inc2)) * rf; // down is positive\n\n accX += dx;\n accY += dy;\n accZ += dzDown;\n\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: -accZ, // render with z up; depth down\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n }\n\n // convert local meters back to approx lat/lon for map overlay if needed\n const pointsWithGeo = points.map((p) => ({\n ...p,\n lat: lat0 + (p.y / metersPerDegLat),\n lng: lng0 + (p.x / metersPerDegLon)\n }));\n\n holes.push({\n id: collar[HOLE_ID] || collar.holeId || key,\n project: collar[PROJECT_ID] || collar.project_id || collar.project || '',\n points: pointsWithGeo,\n collar\n });\n });\n\n return holes;\n}\n\n/**\n * Convert degrees to radians\n * @private\n * @param {number} d - Degrees\n * @returns {number} Radians\n */\nconst degToRad = (d) => (d * Math.PI) / 180;\n\n/**\n * Convert dip (negative downward from horizontal) to inclination from vertical\n * @private\n * @param {number} dipDeg - Dip in degrees (negative = downward)\n * @returns {number} Inclination in radians from vertical (0 = vertical down)\n */\nconst toInclination = (dipDeg) => {\n const val = Number(dipDeg);\n const incDeg = 90 + (Number.isFinite(val) ? val : 0); // dip -60 => inc 30\n const clamped = Math.min(180, Math.max(0, incDeg));\n return degToRad(clamped);\n};\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { withDataErrorContext } from './dataErrorUtils.js';\n/**\n * Convert value to number with optional fallback\n * @private\n */function toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Normalize hole ID value to trimmed string\n * @private\n */\nfunction normalizeHoleIdValue(value) {\n if (value === undefined || value === null) return '';\n return `${value}`.trim();\n}\n\n/**\n * Canonicalize hole ID column across rows with varying column names\n * @private\n */\nfunction canonicalizeHoleIdRows(rows = [], holeIdCol = null) {\n const preferred = holeIdCol || 'hole_id';\n const candidates = [preferred, 'hole_id', 'holeId', 'id'];\n const resolved = candidates.find((col) => rows.some((row) => normalizeHoleIdValue(row?.[col])));\n if (!resolved) {\n throw withDataErrorContext('canonicalizeHoleIdRows', new Error(`hole id column '${preferred}' not found`));\n }\n return {\n aliasCol: resolved,\n rows: rows.map((row) => ({\n ...row,\n hole_id: normalizeHoleIdValue(row?.[resolved])\n }))\n };\n}\n\n/**\n * Convert degrees to radians\n * @private\n */\nfunction degToRad(angle) {\n return (Number(angle) * Math.PI) / 180;\n}\n\n/**\n * Calculate direction cosines from azimuth and dip\n * @private\n */\nfunction directionCosines(azimuth, dip) {\n const azRad = degToRad(azimuth);\n const dipRad = degToRad(dip);\n const ca = Math.cos(dipRad) * Math.sin(azRad);\n const cb = Math.cos(dipRad) * Math.cos(azRad);\n const cc = Math.sin(dipRad) * -1;\n return { ca, cb, cc };\n}\n\n/**\n * Calculate displacement vector for a survey segment using specified method\n * @private\n */\nfunction segmentDisplacement(deltaMd, az0, dip0, az1, dip1, method = 'minimum_curvature') {\n const dc0 = directionCosines(az0, dip0);\n const dc1 = directionCosines(az1, dip1);\n\n if (method === 'tangential') {\n return {\n dx: deltaMd * dc0.ca,\n dy: deltaMd * dc0.cb,\n dz: deltaMd * dc0.cc,\n azimuth: az0,\n dip: dip0\n };\n }\n\n if (method === 'balanced_tangential') {\n const azAvg = 0.5 * (az0 + az1);\n const dipAvg = 0.5 * (dip0 + dip1);\n const dcAvg = directionCosines(azAvg, dipAvg);\n return {\n dx: deltaMd * dcAvg.ca,\n dy: deltaMd * dcAvg.cb,\n dz: deltaMd * dcAvg.cc,\n azimuth: azAvg,\n dip: dipAvg\n };\n }\n\n const dot = (dc0.ca * dc1.ca) + (dc0.cb * dc1.cb) + (dc0.cc * dc1.cc);\n const dogleg = Math.acos(Math.max(-1, Math.min(1, dot)));\n const rf = dogleg > 1e-6 ? (2 * Math.tan(dogleg / 2)) / dogleg : 1;\n\n return {\n dx: 0.5 * deltaMd * (dc0.ca + dc1.ca) * rf,\n dy: 0.5 * deltaMd * (dc0.cb + dc1.cb) * rf,\n dz: 0.5 * deltaMd * (dc0.cc + dc1.cc) * rf,\n azimuth: az1,\n dip: dip1\n };\n}\n\nfunction desurvey(rowsCollars = [], rowsSurveys = [], options = {}) {\n const {\n step = 1,\n holeIdCol = null,\n method = 'minimum_curvature'\n } = options;\n\n const safeStep = Number.isFinite(Number(step)) && Number(step) > 0 ? Number(step) : 1;\n\n const collarsCanonical = canonicalizeHoleIdRows(rowsCollars, holeIdCol);\n const surveysCanonical = canonicalizeHoleIdRows(rowsSurveys, holeIdCol || collarsCanonical.aliasCol);\n\n if (!collarsCanonical.rows.length || !surveysCanonical.rows.length) return [];\n\n const collarsByHole = new Map();\n collarsCanonical.rows.forEach((row) => {\n if (!row.hole_id || collarsByHole.has(row.hole_id)) return;\n collarsByHole.set(row.hole_id, row);\n });\n\n const surveysByHole = new Map();\n surveysCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!surveysByHole.has(row.hole_id)) surveysByHole.set(row.hole_id, []);\n surveysByHole.get(row.hole_id).push(row);\n });\n\n const out = [];\n surveysByHole.forEach((stations, holeId) => {\n const collar = collarsByHole.get(holeId);\n if (!collar) return;\n\n const sorted = [...stations]\n .map((row) => ({\n ...row,\n from: toNumber(row.from),\n azimuth: toNumber(row.azimuth),\n dip: toNumber(row.dip)\n }))\n .filter((row) => Number.isFinite(row.from) && Number.isFinite(row.azimuth) && Number.isFinite(row.dip))\n .sort((a, b) => a.from - b.from);\n\n if (!sorted.length) return;\n\n let x = toNumber(collar.x, 0);\n let y = toNumber(collar.y, 0);\n let z = toNumber(collar.z, 0);\n let mdCursor = sorted[0].from;\n const azPrev = sorted[0].azimuth;\n const dipPrev = sorted[0].dip;\n\n const firstRecord = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: azPrev,\n dip: dipPrev\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n firstRecord[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(firstRecord);\n\n for (let idx = 0; idx < sorted.length - 1; idx += 1) {\n const s0 = sorted[idx];\n const s1 = sorted[idx + 1];\n const md0 = s0.from;\n const md1 = s1.from;\n const deltaMd = md1 - md0;\n if (deltaMd <= 0) continue;\n\n const segmentSteps = Math.max(1, Math.ceil(deltaMd / safeStep));\n const mdIncrement = deltaMd / segmentSteps;\n\n for (let stepIdx = 0; stepIdx < segmentSteps; stepIdx += 1) {\n mdCursor += mdIncrement;\n const weight = (mdCursor - md0) / deltaMd;\n const azInterp = s0.azimuth + weight * (s1.azimuth - s0.azimuth);\n const dipInterp = s0.dip + weight * (s1.dip - s0.dip);\n\n const disp = segmentDisplacement(mdIncrement, s0.azimuth, s0.dip, s1.azimuth, s1.dip, method);\n x += disp.dx;\n y += disp.dy;\n z += disp.dz;\n\n const record = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: method === 'minimum_curvature' ? azInterp : disp.azimuth,\n dip: method === 'minimum_curvature' ? dipInterp : disp.dip\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n record[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(record);\n }\n }\n });\n\n return out;\n}\n\n/**\n * Desurvey drillholes using minimum curvature method\n * @param {Array<Object>} collars - Collar data with hole_id, lat, lng\n * @param {Array<Object>} surveys - Survey data with hole_id, depth, azimuth, dip\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points with x, y, z, md coordinates\n */\nexport function minimumCurvatureDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'minimum_curvature' });\n}\n\n/**\n * Desurvey drillholes using tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function tangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'tangential' });\n}\n\n/**\n * Desurvey drillholes using balanced tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function balancedTangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'balanced_tangential' });\n}\n\n/**\n * Build desurveyed traces using minimum curvature method (alias for minimumCurvatureDesurvey)\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function buildTraces(collars, surveys, options = {}) {\n return minimumCurvatureDesurvey(collars, surveys, options);\n}\n\n/**\n * Find nearest trace point by measured depth\n * @private\n */\nfunction nearestByMeasuredDepth(traceRows, midMd) {\n if (!traceRows.length || !Number.isFinite(midMd)) return null;\n let best = null;\n let bestDist = Infinity;\n for (let i = 0; i < traceRows.length; i += 1) {\n const row = traceRows[i];\n const md = toNumber(row.md);\n if (!Number.isFinite(md)) continue;\n const dist = Math.abs(md - midMd);\n if (dist < bestDist) {\n bestDist = dist;\n best = row;\n }\n }\n return best;\n}\n\n/**\n * Attach 3D position data from desurveyed traces to assay intervals\n * Finds nearest trace point by mid-depth of each assay interval\n * @param {Array<Object>} assays - Assay data with hole_id, from, to\n * @param {Array<Object>} traces - Desurveyed trace data with hole_id, md, x, y, z\n * @param {Object} options - Options\n * @param {string} options.holeIdCol - Hole ID column name (default: 'hole_id')\n * @returns {Array<Object>} Assay rows enriched with x, y, z, md, azimuth, dip from nearest trace point\n */\nexport function attachAssayPositions(assays = [], traces = [], options = {}) {\n const holeIdCol = options.holeIdCol || 'hole_id';\n const assaysCanonical = canonicalizeHoleIdRows(assays, holeIdCol);\n const tracesCanonical = canonicalizeHoleIdRows(traces, holeIdCol);\n\n if (!assaysCanonical.rows.length || !tracesCanonical.rows.length) return [...assaysCanonical.rows];\n\n const tracesByHole = new Map();\n tracesCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!tracesByHole.has(row.hole_id)) tracesByHole.set(row.hole_id, []);\n tracesByHole.get(row.hole_id).push(row);\n });\n tracesByHole.forEach((rows, holeId) => {\n tracesByHole.set(holeId, [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0)));\n });\n\n return assaysCanonical.rows.map((assay) => {\n const from = toNumber(assay.from);\n const to = toNumber(assay.to);\n const midMd = Number.isFinite(from) && Number.isFinite(to) ? 0.5 * (from + to) : undefined;\n if (!assay.hole_id || !Number.isFinite(midMd)) return { ...assay };\n\n const nearest = nearestByMeasuredDepth(tracesByHole.get(assay.hole_id) || [], midMd);\n if (!nearest) return { ...assay };\n\n const merged = { ...assay };\n ['md', 'x', 'y', 'z', 'azimuth', 'dip'].forEach((key) => {\n if (nearest[key] === undefined) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = nearest[key];\n } else {\n merged[key] = nearest[key];\n }\n });\n return merged;\n });\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, EASTING, NORTHING, ELEVATION } from './datamodel.js';\n\n/**\n * Parse drillholes CSV with desurveyed trace points\n * Expect CSV columns: hole_id, x (easting), y (northing), z (elevation), order (optional) plus any attributes per point\n * @param {File|Blob|string} file - CSV file or data\n * @param {Object} [sourceColumnMap] - Optional user-provided column mappings\n * @returns {Promise<{holes: Array}>} - Parsed drillhole data\n */\nexport function parseDrillholesCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow, idx) => {\n const row = standardizeColumns(rawRow, null, sourceColumnMap);\n \n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n const x = row[EASTING] ?? row.x;\n const y = row[NORTHING] ?? row.y;\n const z = row[ELEVATION] ?? row.z;\n const order = row.order ?? idx;\n\n if (!holeId || x === null || x === undefined || y === null || y === undefined || z === null || z === undefined) return;\n\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push({\n ...row,\n holeId,\n order,\n x: Number(x) ?? 0,\n y: Number(y) ?? 0,\n z: Number(z) ?? 0\n });\n });\n\n const holes = Array.from(byHole.entries()).map(([holeId, pts]) => ({\n id: holeId,\n points: pts\n .sort((a, b) => a.order - b.order)\n .map((p) => ({\n ...p,\n x: Number(p.x) || 0,\n y: Number(p.y) || 0,\n z: Number(p.z) || 0\n }))\n }));\n\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseDrillholesCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { standardizeColumns as standardizeRow } from './keying.js';\nimport {\n HOLE_ID,\n LATITUDE,\n LONGITUDE,\n ELEVATION,\n AZIMUTH,\n DIP,\n FROM,\n TO,\n MID,\n PROJECT_ID,\n EASTING,\n NORTHING,\n CRS,\n DEPTH,\n GEOLOGY_CODE,\n GEOLOGY_DESCRIPTION,\n BASELODE_DATA_MODEL_DRILL_COLLAR,\n BASELODE_DATA_MODEL_DRILL_SURVEY,\n BASELODE_DATA_MODEL_DRILL_ASSAY,\n BASELODE_DATA_MODEL_DRILL_GEOLOGY\n} from './datamodel.js';\n\n// Re-export for backwards compatibility\nexport { DEFAULT_COLUMN_MAP } from './datamodel.js';\n\n/**\n * Convert source to array\n * @private\n */\nfunction toArray(source) {\n if (!source) return [];\n if (Array.isArray(source)) return [...source];\n return [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Sort rows by multiple columns\n * @private\n */\nfunction sortByColumns(rows = [], columns = []) {\n const out = [...rows];\n out.sort((a, b) => {\n for (let i = 0; i < columns.length; i += 1) {\n const col = columns[i];\n const av = a?.[col];\n const bv = b?.[col];\n if (av === bv) continue;\n if (av === undefined || av === null) return 1;\n if (bv === undefined || bv === null) return -1;\n if (typeof av === 'number' && typeof bv === 'number') return av - bv;\n return `${av}`.localeCompare(`${bv}`);\n }\n return 0;\n });\n return out;\n}\n\nfunction validateNoOverlappingIntervals(rows = [], label = 'Intervals') {\n if (!rows.length) return;\n const ordered = sortByColumns(rows, [HOLE_ID, FROM, TO]);\n const prevToByHole = new Map();\n\n ordered.forEach((row) => {\n const holeId = `${row?.[HOLE_ID] ?? ''}`.trim();\n const fromValue = Number(row?.[FROM]);\n const toValue = Number(row?.[TO]);\n if (!holeId || !Number.isFinite(fromValue) || !Number.isFinite(toValue)) return;\n\n const prevTo = prevToByHole.get(holeId);\n if (Number.isFinite(prevTo) && fromValue < prevTo) {\n throw withDataErrorContext(\n 'validateNoOverlappingIntervals',\n new Error(`${label} intervals overlap for hole '${holeId}': from=${fromValue} is less than previous to=${prevTo}`)\n );\n }\n prevToByHole.set(holeId, toValue);\n });\n}\n\n/**\n * Parse CSV data using PapaParse\n * @private\n */\nfunction parseCsv(source, papaParseConfig = {}) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n ...papaParseConfig,\n complete: (results) => resolve(Array.isArray(results?.data) ? results.data : []),\n error: (error) => reject(withDataErrorContext('loadTable(csv)', error))\n });\n });\n}\n\n/**\n * Standardize column names in an array of rows using the baselode data model\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map (unused, for backwards compatibility)\n * @param {Object} [sourceColumnMap] - Optional source column map for user-provided mappings\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeColumns(rows = [], columnMap = null, sourceColumnMap = null) {\n return rows.map((row) => standardizeRow(row, columnMap, sourceColumnMap));\n}\n\nexport async function loadTable(source, options = {}) {\n const {\n kind = 'csv',\n columnMap = null,\n sourceColumnMap = null,\n papaParseConfig = {}\n } = options;\n\n let rows;\n if (Array.isArray(source)) {\n rows = toArray(source);\n } else if (kind === 'csv') {\n rows = await parseCsv(source, papaParseConfig);\n } else if (kind === 'parquet' || kind === 'sql') {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind in JS runtime: ${kind}`));\n } else {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind: ${kind}`));\n }\n\n return standardizeColumns(rows, columnMap, sourceColumnMap);\n}\n\n/**\n * Load and validate drillhole collar data\n * Requires hole_id and coordinates (either lat/lng or easting/northing)\n * @param {File|Blob|Array<Object>|string} source - Collar data source\n * @param {Object} options - Loading options\n * @param {string} options.crs - Coordinate reference system (optional)\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated collar rows\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadCollars(source, options = {}) {\n const {\n crs = null,\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Check for hole_id\n const hasHoleId = standardized.some(row => HOLE_ID in row);\n if (!hasHoleId) {\n throw withDataErrorContext('loadCollars', new Error(`Collar table missing column: ${HOLE_ID}`));\n }\n\n // Check for required coordinate columns\n const hasXY = standardized.some(row => EASTING in row && NORTHING in row);\n const hasLatLon = standardized.some(row => LATITUDE in row && LONGITUDE in row);\n\n if (!hasXY && !hasLatLon) {\n throw withDataErrorContext('loadCollars', new Error('Collar table missing coordinate columns (need easting/northing or latitude/longitude)'));\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert coordinates to numbers\n if (LATITUDE in result) result[LATITUDE] = toNumber(result[LATITUDE]);\n if (LONGITUDE in result) result[LONGITUDE] = toNumber(result[LONGITUDE]);\n if (ELEVATION in result) result[ELEVATION] = toNumber(result[ELEVATION]);\n if (EASTING in result) result[EASTING] = toNumber(result[EASTING]);\n if (NORTHING in result) result[NORTHING] = toNumber(result[NORTHING]);\n\n // If datasource_hole_id is missing, copy from hole_id\n if (!('datasource_hole_id' in result) && HOLE_ID in result) {\n result.datasource_hole_id = result[HOLE_ID];\n }\n\n return result;\n });\n\n // Validate required fields\n const required = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (hasLatLon && (!Number.isFinite(row[LATITUDE]) || !Number.isFinite(row[LONGITUDE]))) {\n return false;\n }\n if (hasXY && !hasLatLon && (!Number.isFinite(row[EASTING]) || !Number.isFinite(row[NORTHING]))) {\n return false;\n }\n return true;\n });\n\n if (!required) {\n throw withDataErrorContext('loadCollars', new Error('Collar table has missing required values'));\n }\n\n return normalized;\n}\n\n/**\n * Load and validate drillhole survey data\n * Requires hole_id, depth, azimuth, and dip columns\n * @param {File|Blob|Array<Object>|string} source - Survey data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated survey rows, sorted by hole_id and depth\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadSurveys(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, DEPTH, AZIMUTH, DIP];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadSurveys', new Error(`Survey table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (DEPTH in result) result[DEPTH] = toNumber(result[DEPTH]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n if (AZIMUTH in result) result[AZIMUTH] = toNumber(result[AZIMUTH]);\n if (DIP in result) result[DIP] = toNumber(result[DIP]);\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[DEPTH])) return false;\n if (!Number.isFinite(row[AZIMUTH])) return false;\n if (!Number.isFinite(row[DIP])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadSurveys', new Error('Survey table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, DEPTH]);\n}\n\n/**\n * Load and validate assay/interval data\n * Requires hole_id, from, and to columns. Calculates mid depth automatically.\n * @param {File|Blob|Array<Object>|string} source - Assay data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated assay rows, sorted by hole_id, from, to\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadAssays(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, FROM, TO];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadAssays', new Error(`Assay table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (FROM in result) result[FROM] = toNumber(result[FROM]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n \n // Calculate midpoint\n if (FROM in result && TO in result && Number.isFinite(result[FROM]) && Number.isFinite(result[TO])) {\n result[MID] = 0.5 * (result[FROM] + result[TO]);\n }\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[FROM])) return false;\n if (!Number.isFinite(row[TO])) return false;\n if (!(row[TO] > row[FROM])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadAssays', new Error('Assay table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, FROM, TO]);\n}\n\n/**\n * Load and validate geology interval data for categorical strip-log plotting.\n * Requires hole_id, from, to and at least one geology categorical field.\n * @param {File|Blob|Array<Object>|string} source - Geology data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @returns {Promise<Array<Object>>} Array of validated geology rows, sorted by hole_id, from, to\n */\nexport async function loadGeology(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n\n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n const required = [HOLE_ID, FROM, TO];\n for (const col of required) {\n const hasColumn = standardized.some((row) => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadGeology', new Error(`Geology table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n\n if (FROM in result) result[FROM] = toNumber(result[FROM]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n\n if (FROM in result && TO in result && Number.isFinite(result[FROM]) && Number.isFinite(result[TO])) {\n // Expand equal bounds to a 1mm interval (mirrors Python loader behaviour)\n if (result[TO] === result[FROM]) {\n result[FROM] = Math.round(result[FROM] * 1000) / 1000;\n result[TO] = result[FROM] + 0.001;\n }\n result[MID] = 0.5 * (result[FROM] + result[TO]);\n }\n\n const hasCode = result[GEOLOGY_CODE] !== undefined && result[GEOLOGY_CODE] !== null && `${result[GEOLOGY_CODE]}`.trim() !== '';\n const hasDescription = result[GEOLOGY_DESCRIPTION] !== undefined && result[GEOLOGY_DESCRIPTION] !== null && `${result[GEOLOGY_DESCRIPTION]}`.trim() !== '';\n if (!hasCode && hasDescription) {\n result[GEOLOGY_CODE] = result[GEOLOGY_DESCRIPTION];\n }\n if (hasCode && !hasDescription) {\n result[GEOLOGY_DESCRIPTION] = result[GEOLOGY_CODE];\n }\n\n return result;\n });\n\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[FROM])) return false;\n if (!Number.isFinite(row[TO])) return false;\n if (!(row[TO] > row[FROM])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadGeology', new Error('Geology table has missing or invalid interval values'));\n }\n\n const hasCategory = normalized.some((row) => {\n const code = row[GEOLOGY_CODE];\n const description = row[GEOLOGY_DESCRIPTION];\n return (code !== undefined && code !== null && `${code}`.trim() !== '') ||\n (description !== undefined && description !== null && `${description}`.trim() !== '');\n });\n\n if (!hasCategory) {\n throw withDataErrorContext('loadGeology', new Error(`Geology table missing categorical columns: ${GEOLOGY_CODE} or ${GEOLOGY_DESCRIPTION}`));\n }\n\n validateNoOverlappingIntervals(normalized, 'Geology');\n\n if (!keepAll) {\n const keep = new Set(Object.keys(BASELODE_DATA_MODEL_DRILL_GEOLOGY));\n return sortByColumns(\n normalized.map((row) => Object.fromEntries(Object.entries(row).filter(([k]) => keep.has(k)))),\n [HOLE_ID, FROM, TO]\n );\n }\n\n return sortByColumns(normalized, [HOLE_ID, FROM, TO]);\n}\n\n/**\n * Join assay data to desurveyed trace data by matching key columns\n * Adds trace fields to assay rows, suffixing with '_trace' if field already exists\n * @param {Array<Object>} assays - Array of assay rows\n * @param {Array<Object>} traces - Array of trace/desurvey rows\n * @param {Object} options - Join options\n * @param {Array<string>} options.onCols - Columns to join on (default: [hole_id])\n * @returns {Array<Object>} Assay rows enriched with trace data\n */\nexport function joinAssaysToTraces(assays = [], traces = [], options = {}) {\n const onCols = Array.isArray(options.onCols) && options.onCols.length ? options.onCols : [HOLE_ID];\n if (!traces.length) return [...assays];\n\n const keyOf = (row) => onCols.map((col) => `${row?.[col] ?? ''}`).join('|');\n const tracesByKey = new Map();\n traces.forEach((trace) => {\n tracesByKey.set(keyOf(trace), trace);\n });\n\n return assays.map((assay) => {\n const trace = tracesByKey.get(keyOf(assay));\n if (!trace) return { ...assay };\n const merged = { ...assay };\n Object.entries(trace).forEach(([key, value]) => {\n if (onCols.includes(key)) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = value;\n } else {\n merged[key] = value;\n }\n });\n return merged;\n });\n}\n\n/**\n * Filter rows by project ID\n * @param {Array<Object>} rows - Array of rows with project_id field\n * @param {string|null} projectId - Project ID to filter by (null returns all rows)\n * @returns {Array<Object>} Filtered rows\n */\nexport function filterByProject(rows = [], projectId = null) {\n if (projectId === null || projectId === undefined) return [...rows];\n if (!rows.length) return [];\n \n // Check if rows have project_id column\n const hasProjectId = rows.some(row => PROJECT_ID in row);\n if (!hasProjectId) return [...rows];\n \n return rows.filter((row) => row?.[PROJECT_ID] === projectId);\n}\n\n/**\n * Coerce specified columns to numeric (finite number or undefined)\n * @param {Array<Object>} rows - Array of rows\n * @param {Array<string>} columns - Column names to coerce to numeric\n * @returns {Array<Object>} Rows with coerced numeric columns\n */\nexport function coerceNumeric(rows = [], columns = []) {\n return rows.map((row) => {\n const next = { ...row };\n columns.forEach((column) => {\n if (!(column in next)) return;\n const n = toNumber(next[column]);\n next[column] = n;\n });\n return next;\n });\n}\n\n/**\n * Assemble a complete drillhole dataset from component tables\n * @param {Object} components - Dataset components\n * @param {Array<Object>} components.collars - Collar data\n * @param {Array<Object>} components.surveys - Survey data\n * @param {Array<Object>} components.assays - Assay data\n * @param {Array<Object>} components.geology - Geology interval data\n * @param {Array<Object>} components.structures - Structural data (optional)\n * @param {Object} components.metadata - Dataset metadata (optional)\n * @returns {{collars: Array, surveys: Array, assays: Array, geology: Array, structures: Array, metadata: Object}} Complete dataset\n */\nexport function assembleDataset({\n collars = [],\n surveys = [],\n assays = [],\n geology = [],\n structures = [],\n metadata = {}\n} = {}) {\n return {\n collars: toArray(collars),\n surveys: toArray(surveys),\n assays: toArray(assays),\n geology: toArray(geology),\n structures: toArray(structures),\n metadata: metadata || {}\n };\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { withDataErrorContext } from './dataErrorUtils.js';\n\n// ---------------------------------------------------------------------------\n// Column-name normalisation\n// ---------------------------------------------------------------------------\n\n/** Canonical geometry columns expected on every block row. */\nconst GEOMETRY_COLS = ['x', 'y', 'z', 'dx', 'dy', 'dz'];\n\n/** Maps accepted source column name variants to canonical names. */\nconst BLOCK_COL_MAP = {\n x: ['x', 'easting', 'center_x', 'xc', 'xcentre', 'xcenter', 'x_centre', 'x_center', 'cx'],\n y: ['y', 'northing', 'center_y', 'yc', 'ycentre', 'ycenter', 'y_centre', 'y_center', 'cy'],\n z: ['z', 'elevation', 'center_z', 'zc', 'zcentre', 'zcenter', 'z_centre', 'z_center', 'cz'],\n dx: ['dx', 'size_x', 'sx', 'sizex', 'dim_x', 'block_size_x'],\n dy: ['dy', 'size_y', 'sy', 'sizey', 'dim_y', 'block_size_y'],\n dz: ['dz', 'size_z', 'sz', 'sizez', 'dim_z', 'block_size_z'],\n};\n\n/** Reverse lookup: lower-cased source name → canonical name */\nconst _blockColLookup = {};\nObject.entries(BLOCK_COL_MAP).forEach(([canon, variants]) => {\n variants.forEach((v) => { _blockColLookup[v.toLowerCase()] = canon; });\n});\n\n/**\n * Rename a block row's keys from source column names to canonical names.\n * Unrecognised keys are kept as-is.\n * @param {Object} row - Raw parsed row object\n * @returns {Object} Row with canonical geometry key names\n */\nexport function normalizeBlockRow(row) {\n const out = {};\n Object.entries(row).forEach(([key, value]) => {\n const canon = _blockColLookup[key.toLowerCase().trim()] || key;\n out[canon] = value;\n });\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// CSV parsing\n// ---------------------------------------------------------------------------\n\n/**\n * Parse block model CSV data.\n *\n * Accepts columns named ``x/y/z/dx/dy/dz`` (baselode canonical) or the\n * legacy ``center_x/center_y/center_z/size_x/size_y/size_z`` variants.\n * Both forms are normalised to the canonical names.\n *\n * @param {File|Blob|string} file - Block model CSV source\n * @returns {Promise<{data: Array<Object>, properties: Array<string>}>}\n * Parsed blocks (canonical column names) and property column names.\n */\nexport function parseBlockModelCSV(file) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const normalized = (results.data || []).map(normalizeBlockRow);\n const data = normalized.filter(\n (row) => row.x !== null && row.y !== null && row.z !== null\n );\n\n const propertyColumns = Object.keys(data[0] || {}).filter(\n (key) => !GEOMETRY_COLS.includes(key)\n );\n\n resolve({ data, properties: propertyColumns });\n },\n error: (error) => {\n reject(withDataErrorContext('parseBlockModelCSV', error));\n }\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Metadata\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a block model metadata JSON string or plain object.\n *\n * The metadata object may contain: ``name``, ``description``, ``crs``,\n * ``origin``, ``max_block_size``, ``min_block_size``, ``bbox_3d``,\n * ``outline_2d``, ``attributes``, ``extra``.\n *\n * @param {string|Object} source - JSON string or already-parsed object\n * @returns {Object} Parsed metadata object\n */\nexport function loadBlockModelMetadata(source) {\n if (typeof source === 'string') {\n try {\n return JSON.parse(source);\n } catch (err) {\n throw withDataErrorContext('loadBlockModelMetadata', err);\n }\n }\n if (source && typeof source === 'object') return source;\n throw withDataErrorContext('loadBlockModelMetadata', new Error('Invalid metadata source'));\n}\n\n// ---------------------------------------------------------------------------\n// Property statistics\n// ---------------------------------------------------------------------------\n\n/**\n * Calculate statistics for a property column in block model data.\n *\n * @param {Array<Object>} data - Block model data array\n * @param {string} property - Property column name to analyse\n * @returns {{type: 'numeric'|'categorical', min?: number, max?: number,\n * values: Array, categories?: Array}} Property statistics\n */\nexport function calculatePropertyStats(data, property) {\n const values = data\n .map((row) => row[property])\n .filter((v) => v !== null && v !== undefined);\n\n const isNumeric = values.length > 0 && values.every((v) => typeof v === 'number');\n\n if (isNumeric) {\n const min = Math.min(...values);\n const max = Math.max(...values);\n return { type: 'numeric', min, max, values };\n }\n\n const uniqueValues = [...new Set(values)];\n return { type: 'categorical', categories: uniqueValues, values };\n}\n\n/**\n * Compute per-property statistics for all non-geometry columns.\n *\n * @param {Array<Object>} data - Block model data array\n * @returns {Object} Map of ``propertyName → stats`` (same shape as\n * :func:`calculatePropertyStats` return value)\n */\nexport function getBlockStats(data) {\n if (!data || data.length === 0) return {};\n const propertyKeys = Object.keys(data[0]).filter(\n (key) => !GEOMETRY_COLS.includes(key)\n );\n const result = {};\n propertyKeys.forEach((key) => {\n result[key] = calculatePropertyStats(data, key);\n });\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Filtering and volume\n// ---------------------------------------------------------------------------\n\n/**\n * Filter block model rows by attribute criteria.\n *\n * @param {Array<Object>} data - Block model data array\n * @param {Object} criteria - Map of ``columnName → condition``.\n * Each condition may be:\n * - A scalar (exact equality)\n * - An object with operator keys: ``gt``, ``gte``, ``lt``, ``lte``,\n * ``eq``, ``ne``, ``in`` (array)\n * @returns {Array<Object>} Filtered array of block rows\n */\nexport function filterBlocks(data, criteria) {\n if (!criteria || typeof criteria !== 'object') return data;\n return data.filter((row) =>\n Object.entries(criteria).every(([col, condition]) => {\n const val = row[col];\n if (condition === null || condition === undefined) return true;\n if (typeof condition !== 'object' || Array.isArray(condition)) {\n return val === condition;\n }\n if ('gt' in condition && !(val > condition.gt)) return false;\n if ('gte' in condition && !(val >= condition.gte)) return false;\n if ('lt' in condition && !(val < condition.lt)) return false;\n if ('lte' in condition && !(val <= condition.lte)) return false;\n if ('eq' in condition && val !== condition.eq) return false;\n if ('ne' in condition && val === condition.ne) return false;\n if ('in' in condition && !condition.in.includes(val)) return false;\n return true;\n })\n );\n}\n\n/**\n * Calculate total block volume, optionally filtered by criteria.\n *\n * @param {Array<Object>} data - Block model data array (canonical column names)\n * @param {Object|null} [criteria] - Optional filter criteria (see :func:`filterBlocks`)\n * @returns {number} Sum of dx * dy * dz for matching blocks\n */\nexport function calculateBlockVolume(data, criteria = null) {\n const subset = criteria ? filterBlocks(data, criteria) : data;\n return subset.reduce((sum, row) => {\n const dx = Number(row.dx) || 0;\n const dy = Number(row.dy) || 0;\n const dz = Number(row.dz) || 0;\n return sum + dx * dy * dz;\n }, 0);\n}\n\n// ---------------------------------------------------------------------------\n// Colorisation\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a color for a property value based on its statistics.\n *\n * @param {*} value - Property value to colourise\n * @param {{type: 'numeric'|'categorical', min?: number, max?: number,\n * categories?: Array}|null} stats - Property statistics\n * @param {Object} THREEInstance - THREE.js instance for creating Color objects\n * @returns {THREE.Color} Color object for the value\n */\nexport function getColorForValue(value, stats, THREEInstance) {\n if (!stats) return new THREEInstance.Color('#888888');\n\n if (stats.type === 'numeric') {\n const range = stats.max - stats.min;\n const normalized = range === 0 ? 0.5 : (value - stats.min) / range;\n const hue = (1 - normalized) * 240; // blue=240, red=0\n return new THREEInstance.Color().setHSL(hue / 360, 0.8, 0.5);\n }\n\n const index = stats.categories.indexOf(value);\n const hue = (index / Math.max(stats.categories.length, 1)) * 360;\n return new THREEInstance.Color().setHSL(hue / 360, 0.7, 0.5);\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, FROM, TO, DEPTH, DIP, AZIMUTH } from './datamodel.js';\n\n/**\n * Normalize a raw CSV row to standardized column names.\n * @private\n */\nconst normalizeRow = (rawRow, sourceColumnMap = null) => standardizeColumns(rawRow, null, sourceColumnMap);\n\n/**\n * Determine if a set of rows represents point or interval data.\n * @private\n * @param {Array<Object>} rows - Normalized rows\n * @returns {'point'|'interval'|null}\n */\nfunction detectSchema(rows) {\n if (!rows.length) return null;\n const first = rows[0];\n const hasInterval = FROM in first && TO in first;\n const hasPoint = DEPTH in first && !hasInterval;\n if (hasInterval) return 'interval';\n if (hasPoint) return 'point';\n return null;\n}\n\n/**\n * Coerce a value to a finite number or null.\n * @private\n */\nfunction toNumber(v) {\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Normalize a single structural point row.\n * @private\n */\nfunction extractStructuralPoint(row) {\n const holeId = row[HOLE_ID] !== undefined ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) return null;\n const depth = toNumber(row[DEPTH]);\n if (depth === null) return null;\n\n return {\n [HOLE_ID]: holeId,\n [DEPTH]: depth,\n [DIP]: toNumber(row[DIP]),\n [AZIMUTH]: toNumber(row[AZIMUTH]),\n comments: row.comments != null ? `${row.comments}` : null,\n ...row,\n };\n}\n\n/**\n * Normalize a single structural interval row.\n * @private\n */\nfunction extractStructuralInterval(row) {\n const holeId = row[HOLE_ID] !== undefined ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) return null;\n const from = toNumber(row[FROM]);\n const to = toNumber(row[TO]);\n if (from === null || to === null || to <= from) return null;\n\n const mid = 0.5 * (from + to);\n return {\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n mid,\n [DIP]: toNumber(row[DIP]),\n [AZIMUTH]: toNumber(row[AZIMUTH]),\n classification: row.classification != null ? `${row.classification}` : null,\n comments: row.comments != null ? `${row.comments}` : null,\n ...row,\n };\n}\n\n/**\n * Validate an array of structural point rows.\n * Returns an object with valid rows and error details.\n *\n * @param {Array<Object>} rows - Normalized structural point rows\n * @returns {{ valid: Array<Object>, errors: Array<{row: Object, message: string}> }}\n */\nexport function validateStructuralPoints(rows) {\n const valid = [];\n const errors = [];\n\n for (const row of rows) {\n const messages = [];\n const dip = toNumber(row[DIP]);\n const az = toNumber(row[AZIMUTH]);\n\n if (dip !== null && (dip < 0 || dip > 90)) {\n messages.push(`dip ${dip} out of range [0, 90]`);\n }\n if (az !== null && (az < 0 || az >= 360)) {\n messages.push(`azimuth ${az} out of range [0, 360)`);\n }\n if (messages.length) {\n errors.push({ row, message: messages.join('; ') });\n } else {\n valid.push(row);\n }\n }\n\n return { valid, errors };\n}\n\n/**\n * Parse a structural points CSV (point schema: hole_id, depth, dip, azimuth, ...).\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<Array<Object>>} Array of structural point objects\n */\nexport function parseStructuralPointsCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const opts = {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = [];\n for (const rawRow of results.data) {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const point = extractStructuralPoint(row);\n if (point) rows.push(point);\n }\n resolve(rows);\n },\n error: (error) => reject(withDataErrorContext('parseStructuralPointsCSV', error)),\n };\n\n if (typeof source === 'string' && !source.startsWith('data:') && source.includes('\\n')) {\n Papa.parse(source, opts);\n } else {\n Papa.parse(source, opts);\n }\n });\n}\n\n/**\n * Parse a structural intervals CSV (interval schema: hole_id, from, to, dip, azimuth, ...).\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<Array<Object>>} Array of structural interval objects\n */\nexport function parseStructuralIntervalsCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = [];\n for (const rawRow of results.data) {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const interval = extractStructuralInterval(row);\n if (interval) rows.push(interval);\n }\n resolve(rows);\n },\n error: (error) => reject(withDataErrorContext('parseStructuralIntervalsCSV', error)),\n });\n });\n}\n\n/**\n * Group a flat array of structural rows into hole objects keyed by hole_id.\n *\n * The resulting hole objects use the same shape as assay holes\n * ({ holeId, points }) so they can be merged into useDrillholeTraceGrid.\n *\n * @param {Array<Object>} rows - Flat array of structural rows (already normalized)\n * @param {string} [holeIdCol='hole_id'] - Column name containing the hole identifier\n * @returns {Array<{ holeId: string, points: Array<Object> }>}\n */\nexport function groupRowsByHole(rows, holeIdCol = HOLE_ID) {\n const byId = new Map();\n for (const row of rows) {\n const id = row[holeIdCol] != null ? String(row[holeIdCol]).trim() : '';\n if (!id) continue;\n if (!byId.has(id)) byId.set(id, { holeId: id, points: [] });\n byId.get(id).points.push(row);\n }\n return Array.from(byId.values());\n}\n\n/**\n * Parse a structural CSV, auto-detecting point vs interval schema.\n *\n * @param {File|Blob|string} source - CSV file or text\n * @param {Object|null} sourceColumnMap - Optional column name overrides\n * @returns {Promise<{ schema: 'point'|'interval', rows: Array<Object> }>}\n */\nexport function parseStructuralCSV(source, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const normalized = results.data.map(r => normalizeRow(r, sourceColumnMap));\n const schema = detectSchema(normalized);\n\n if (!schema) {\n reject(withDataErrorContext('parseStructuralCSV',\n new Error(\"Structural CSV requires either 'depth' (point) or 'from'/'to' (interval) columns\")));\n return;\n }\n\n const rows = [];\n for (const row of normalized) {\n const parsed = schema === 'interval'\n ? extractStructuralInterval(row)\n : extractStructuralPoint(row);\n if (parsed) rows.push(parsed);\n }\n resolve({ schema, rows });\n },\n error: (error) => reject(withDataErrorContext('parseStructuralCSV', error)),\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { HOLE_ID, FROM, TO, MID, DEPTH, DIP, AZIMUTH } from './datamodel.js';\nimport { parseStructuralCSV, groupRowsByHole } from './structuralLoader.js';\n\n\n/**\n * Parse assay CSV text into an array of hole objects.\n *\n * Each interval row gets a pre-computed `mid` field (= (from + to) / 2) stored\n * under both the `mid` and `depth` keys so it can serve as the unified y-axis\n * depth value when rendered alongside structural point data.\n *\n * @param {string} csvText - Assay CSV content as a text string\n * @returns {Promise<Array<{holeId: string, points: Array<Object>}>>}\n */\nexport function parseAssayCsvTextToHoles(csvText) {\n return new Promise((resolve) => {\n Papa.parse(csvText, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n for (const rawRow of results.data) {\n const row = standardizeColumns(rawRow);\n const holeId = row[HOLE_ID] != null ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) continue;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const mid = (from + to) / 2;\n // Strip structural-domain columns that some assay exports (e.g. GSWA) include\n // as per-interval hole-orientation values — these are borehole survey attributes,\n // not structural plane measurements, and must not appear as selectable properties\n // alongside true structural data.\n // eslint-disable-next-line no-unused-vars\n const { [DIP]: _dip, [AZIMUTH]: _az, ...rowWithoutStructural } = row;\n const point = {\n ...rowWithoutStructural,\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n [MID]: mid,\n [DEPTH]: mid, // unified depth field for y-axis rendering\n _source: 'assay',\n };\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push(point);\n }\n const holes = Array.from(byHole.entries()).map(([holeId, points]) => ({\n holeId,\n points: points.sort((a, b) => a[FROM] - b[FROM]),\n }));\n resolve(holes);\n },\n });\n });\n}\n\n/**\n * Parse geology CSV text into an array of hole objects, keyed by hole_id.\n *\n * @param {string} csvText - Geology CSV content as a text string\n * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport function parseGeologyCsvText(csvText) {\n return new Promise((resolve) => {\n Papa.parse(csvText, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n for (const rawRow of results.data) {\n const row = standardizeColumns(rawRow);\n const holeId = (row[HOLE_ID] ?? '').toString().trim();\n if (!holeId) continue;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const mid = (from + to) / 2;\n // eslint-disable-next-line no-unused-vars\n const { [DIP]: _dip, [AZIMUTH]: _az, ...rest } = row;\n const point = {\n ...rest,\n [HOLE_ID]: holeId,\n [FROM]: from,\n [TO]: to,\n [MID]: mid,\n [DEPTH]: mid,\n _source: 'geology',\n };\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push(point);\n }\n resolve({\n holes: Array.from(byHole.entries()).map(([holeId, points]) => ({\n holeId,\n points: points.sort((a, b) => a[FROM] - b[FROM]),\n })),\n });\n },\n });\n });\n}\n\n/**\n * Build a unified drillhole dataset by merging assay intervals and structural\n * point/interval measurements from their respective CSV text sources.\n *\n * The result is a flat, holeId-keyed collection of hole objects. Each hole's\n * `points` array contains rows from both sources:\n * - Assay rows carry all geochemical columns; `depth` is set to the interval\n * midpoint ((from + to) / 2) pre-computed at load time.\n * - Structural rows carry dip/azimuth/description columns; `depth` comes\n * directly from the structural CSV (point schema) or the interval midpoint\n * (interval schema).\n * - Rows from each source are tagged with `_source: 'assay'|'structural'`.\n *\n * The returned holes can be passed directly to `useDrillholeTraceGrid` as\n * `extraHoles` (without a `sourceFile`) since all data is eager-loaded.\n *\n * @param {Object} options\n * @param {string} [options.assayCsv] - Assay CSV text\n * @param {string} [options.structuralCsv] - Structural CSV text\n * @param {string} [options.geologyCsv] - Geology CSV text\n * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport async function parseUnifiedDataset({ assayCsv, structuralCsv, geologyCsv } = {}) {\n const [assayHoles, structuralHoles, geologyHoles] = await Promise.all([\n assayCsv ? parseAssayCsvTextToHoles(assayCsv) : Promise.resolve([]),\n structuralCsv\n ? parseStructuralCSV(structuralCsv).then(({ rows }) =>\n groupRowsByHole(rows.map((r) => ({ ...r, _source: 'structural' })))\n )\n : Promise.resolve([]),\n geologyCsv ? parseGeologyCsvText(geologyCsv).then(({ holes }) => holes) : Promise.resolve([]),\n ]);\n\n // Merge holes from all sources by holeId\n const byId = new Map(assayHoles.map((h) => [h.holeId, { ...h, points: [...h.points] }]));\n for (const sh of [...structuralHoles, ...geologyHoles]) {\n const id = sh.holeId;\n if (!id) continue;\n if (byId.has(id)) {\n const existing = byId.get(id);\n byId.set(id, { ...existing, points: [...existing.points, ...(sh.points || [])] });\n } else {\n byId.set(id, sh);\n }\n }\n\n return { holes: Array.from(byId.values()) };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Calculate significant intercepts from assay interval data.\n *\n * A significant intercept is a contiguous downhole run of assay intervals where\n * every interval meets or exceeds {@link minGrade}, and the total run length is\n * at least {@link minLength}.\n *\n * @param {Array<Object>} intervals - Assay interval rows, each containing at\n * minimum the hole ID, from-depth, to-depth, and assay field columns.\n * @param {string} assayField - Name of the assay value property on each row\n * (e.g. `\"CU_PCT\"` or `\"AU_PPM\"`).\n * @param {number} minGrade - Minimum grade threshold. Intervals below this\n * value are excluded.\n * @param {number} minLength - Minimum contiguous downhole length required to\n * report an intercept.\n * @param {Object} [options={}]\n * @param {string} [options.fromCol=\"from\"] - Property name for the from-depth.\n * @param {string} [options.toCol=\"to\"] - Property name for the to-depth.\n * @param {string} [options.holeCol=\"hole_id\"] - Property name for the hole ID.\n * @returns {Array<Object>} One object per significant intercept with properties:\n * `hole_id`, `assay_field`, `from`, `to`, `length`, `avg_grade`,\n * `n_samples`, `label`.\n */\nexport function significantIntercepts(intervals, assayField, minGrade, minLength, options = {}) {\n const fromCol = options.fromCol || 'from';\n const toCol = options.toCol || 'to';\n const holeCol = options.holeCol || 'hole_id';\n\n if (!intervals || !intervals.length) return [];\n\n // Group by hole ID\n const byHole = {};\n for (const row of intervals) {\n const holeId = row[holeCol];\n if (holeId == null) continue;\n if (!byHole[holeId]) byHole[holeId] = [];\n byHole[holeId].push(row);\n }\n\n const results = [];\n\n for (const [holeId, holeIntervals] of Object.entries(byHole)) {\n // Sort by from depth\n const sorted = [...holeIntervals].sort((a, b) => Number(a[fromCol]) - Number(b[fromCol]));\n\n // Filter to above-threshold intervals with finite grades\n const qualifying = sorted.filter((row) => {\n const grade = Number(row[assayField]);\n return Number.isFinite(grade) && grade >= minGrade;\n });\n\n if (!qualifying.length) continue;\n\n // Group qualifying intervals into contiguous runs\n const runs = [];\n let currentRun = [];\n let prevTo = null;\n\n for (const row of qualifying) {\n const f = Number(row[fromCol]);\n const t = Number(row[toCol]);\n\n if (prevTo === null || Math.abs(f - prevTo) > 1e-6) {\n if (currentRun.length) runs.push(currentRun);\n currentRun = [row];\n } else {\n currentRun.push(row);\n }\n prevTo = t;\n }\n if (currentRun.length) runs.push(currentRun);\n\n for (const run of runs) {\n const totalFrom = Number(run[0][fromCol]);\n const totalTo = Number(run[run.length - 1][toCol]);\n const totalLength = totalTo - totalFrom;\n\n if (totalLength < minLength) continue;\n\n let weightedSum = 0;\n let totalWeight = 0;\n for (const row of run) {\n const grade = Number(row[assayField]);\n const len = Number(row[toCol]) - Number(row[fromCol]);\n weightedSum += grade * len;\n totalWeight += len;\n }\n const avgGrade = weightedSum / totalWeight;\n const nSamples = run.length;\n const label = `${totalLength.toFixed(1)} m @ ${avgGrade.toFixed(2)} ${assayField}`;\n\n results.push({\n [holeCol]: holeId,\n assay_field: assayField,\n [fromCol]: totalFrom,\n [toCol]: totalTo,\n length: totalLength,\n avg_grade: avgGrade,\n n_samples: nSamples,\n label,\n });\n }\n }\n\n return results;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { HOLE_ID, DEPTH, FROM, TO, MID, GEOPHYSICS_NULL_SENTINEL } from './datamodel.js';\n\n/**\n * Column name variants for depth in geophysics point logs.\n * These are tried (after normalisation) when the standard 'depth' column is absent.\n * @private\n */\nconst DEPTH_ALIASES = ['depth', 'md', 'measured_depth', 'dept', 'z'];\n\n/**\n * Resolve the depth value from a standardised geophysics row.\n *\n * Supports both point-based (single depth column) and interval-based (from/to)\n * schemas. For interval rows the midpoint is returned. The standard DEPTH\n * column is tried first; common aliases are checked as a fallback.\n *\n * @private\n * @param {object} row - Standardised row object\n * @returns {number|null}\n */\nfunction resolveDepth(row) {\n // Point-based: explicit depth column\n if (row[DEPTH] !== undefined) {\n const v = Number(row[DEPTH]);\n if (Number.isFinite(v)) return v;\n }\n for (const alias of DEPTH_ALIASES) {\n if (row[alias] !== undefined) {\n const v = Number(row[alias]);\n if (Number.isFinite(v)) return v;\n }\n }\n // Interval-based: compute midpoint from from/to\n const f = Number(row[FROM]);\n const t = Number(row[TO]);\n if (Number.isFinite(f) && Number.isFinite(t) && t >= f) return (f + t) / 2;\n return null;\n}\n\n/**\n * Discover the numeric value columns in a geophysics file.\n *\n * All columns that are not hole_id or a recognised depth alias are considered\n * candidate value columns. Only columns whose values parse as finite numbers\n * for the majority (>50 %) of rows are retained, so comment or flag columns\n * are dropped automatically.\n *\n * @private\n * @param {object[]} rows - Standardised rows\n * @returns {string[]} Ordered list of numeric value column names\n */\nfunction detectValueColumns(rows) {\n if (rows.length === 0) return [];\n const skipSet = new Set([HOLE_ID, DEPTH, FROM, TO, MID, ...DEPTH_ALIASES]);\n const candidates = Object.keys(rows[0]).filter((c) => !skipSet.has(c));\n\n return candidates.filter((col) => {\n let numericCount = 0;\n for (const row of rows) {\n const v = Number(row[col]);\n if (Number.isFinite(v)) numericCount++;\n }\n return numericCount / rows.length > 0.5;\n });\n}\n\n/**\n * Parse a geophysics point-log CSV into per-hole arrays of depths and values.\n *\n * Unlike assay intervals (from/to), geophysics logs contain one row per sample\n * depth — typically at a fixed spacing (e.g. 0.1 m for gamma probes). This\n * loader handles the higher sample density gracefully; there is no upper limit\n * on the number of rows.\n *\n * ### Column detection\n * - **hole_id** — standard variants recognised by `standardizeColumns`.\n * - **depth** — standard `depth` plus common aliases: `md`, `measured_depth`,\n * `dept`, `z`.\n * - **value columns** — every other numeric column is treated as a geophysics\n * channel (gamma, resistivity, SP, etc.). Column names are preserved as-is\n * after normalisation so callers can identify individual channels.\n *\n * ### Return value\n * Returns an array of **hole objects**, one per unique hole_id:\n * ```js\n * [\n * {\n * holeId: 'GSWA001',\n * channels: {\n * gamma: { depths: [0.0, 0.1, 0.2, ...], values: [45, 47, 52, ...] },\n * sp: { depths: [0.0, 0.1, 0.2, ...], values: [-12, -11, -9, ...] },\n * }\n * },\n * ...\n * ]\n * ```\n * The `channels` object keys match the normalised CSV column names. Each\n * channel only includes rows where both depth and value are finite numbers.\n *\n * @param {string} csvText - Raw CSV text\n * @param {object} [columnMap] - Optional `{ rawName: standardName }` overrides\n * @returns {Array<{holeId: string, channels: object}>}\n */\nexport function parseGeophysicsCSV(csvText, columnMap = null) {\n const result = Papa.parse(csvText, {\n header: true,\n skipEmptyLines: true,\n dynamicTyping: false,\n });\n\n const rawRows = result.data || [];\n if (rawRows.length === 0) return [];\n\n const rows = rawRows.map((r) => standardizeColumns(r, null, columnMap));\n\n // Discover value columns from the full dataset\n const valueCols = detectValueColumns(rows);\n\n // Group rows by hole\n const byHole = new Map();\n for (const row of rows) {\n const holeId = row[HOLE_ID] != null ? `${row[HOLE_ID]}`.trim() : '';\n if (!holeId) continue;\n\n const depth = resolveDepth(row);\n if (depth === null || depth < 0) continue;\n\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push({ depth, row });\n }\n\n const holes = [];\n for (const [holeId, samples] of byHole) {\n // Sort by depth (ascending)\n samples.sort((a, b) => a.depth - b.depth);\n\n const channels = {};\n for (const col of valueCols) {\n const depths = [];\n const values = [];\n for (const { depth, row } of samples) {\n const v = Number(row[col]);\n if (Number.isFinite(v) && v !== GEOPHYSICS_NULL_SENTINEL) {\n depths.push(depth);\n values.push(v);\n }\n }\n if (depths.length >= 2) {\n channels[col] = { depths, values };\n }\n }\n\n if (Object.keys(channels).length > 0) {\n holes.push({ holeId, channels });\n }\n }\n\n return holes;\n}\n\n/**\n * Convert the output of `parseGeophysicsCSV` into the strip log format\n * expected by `Baselode3DScene.setStripLogs`.\n *\n * A single channel from each hole becomes one strip log entry. If a hole has\n * multiple channels, pass `channel` to select which one (defaults to the first\n * channel found).\n *\n * @param {Array<{holeId: string, channels: object}>} geophysicsHoles\n * Output of `parseGeophysicsCSV`.\n * @param {string} [channel]\n * Channel name to use. When omitted, the first available channel per hole\n * is used.\n * @param {object} [options]\n * Strip log display options forwarded to each entry's `options` field.\n * See `normalizeStripLogOptions` for available keys.\n * @returns {Array<{holeId: string, depths: number[], values: number[], options: object}>}\n */\nexport function geophysicsToStripLogs(geophysicsHoles, channel = null, options = {}) {\n const stripLogs = [];\n for (const { holeId, channels } of geophysicsHoles) {\n const key = channel && channels[channel] ? channel : Object.keys(channels)[0];\n if (!key) continue;\n const { depths, values } = channels[key];\n stripLogs.push({ holeId, depths, values, options });\n }\n return stripLogs;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nimport { GEOPHYSICS_NULL_SENTINEL } from './datamodel.js';\n\n/**\n * Parse a single LAS header mnemonic line.\n *\n * LAS header lines have the format:\n * MNEM.UNIT value : description\n *\n * @private\n * @param {string} line\n * @returns {{ mnem: string, unit: string, value: string, description: string } | null}\n */\nfunction parseMnemonicLine(line) {\n const dotIdx = line.indexOf('.');\n if (dotIdx < 0) return null;\n\n const mnem = line.slice(0, dotIdx).trim().toUpperCase();\n const afterDot = line.slice(dotIdx + 1);\n\n // Unit is the first non-whitespace token after the dot\n const unitMatch = afterDot.match(/^(\\S*)\\s*(.*)/s);\n const unit = unitMatch ? unitMatch[1] : '';\n const valueAndDesc = unitMatch ? unitMatch[2] : afterDot;\n\n // Description follows the last colon\n const lastColon = valueAndDesc.lastIndexOf(':');\n const value = lastColon >= 0 ? valueAndDesc.slice(0, lastColon).trim() : valueAndDesc.trim();\n const description = lastColon >= 0 ? valueAndDesc.slice(lastColon + 1).trim() : '';\n\n return { mnem, unit, value, description };\n}\n\n/**\n * Split a LAS text into named sections.\n *\n * @private\n * @param {string} lasText\n * @returns {Array<{ type: string, lines: string[] }>}\n */\nfunction splitSections(lasText) {\n const sections = [];\n let current = null;\n\n for (const raw of lasText.split(/\\r?\\n/)) {\n const line = raw.trimEnd();\n if (line.startsWith('~')) {\n // Section type is the first letter after ~, uppercased\n const type = line.slice(1, 2).toUpperCase();\n current = { type, lines: [] };\n sections.push(current);\n } else if (current) {\n current.lines.push(line);\n }\n }\n\n return sections;\n}\n\n/**\n * Parse a LAS header section into a map of mnemonic → parsed entry.\n *\n * @private\n * @param {string[] | undefined} lines\n * @returns {Record<string, { mnem: string, unit: string, value: string, description: string }>}\n */\nfunction parseHeaderSection(lines = []) {\n const map = {};\n for (const line of lines) {\n const trimmed = line.trimStart();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const parsed = parseMnemonicLine(line);\n if (parsed) map[parsed.mnem] = parsed;\n }\n return map;\n}\n\n/**\n * Parse a LAS 1.2 or 2.0 file into the same per-hole channel format returned\n * by {@link parseGeophysicsCSV}.\n *\n * LAS (Log ASCII Standard) is the industry-standard format for wireline log\n * data. Measurements are point-based — one row per sample depth — so the\n * returned channel objects use a ``depth`` axis rather than from/to intervals.\n *\n * ### LAS → baselode mapping\n *\n * | LAS section | Field | Baselode field |\n * |-------------|--------------|------------------|\n * | `~WELL` | WELL/UWI/API | `holeId` |\n * | `~WELL` | NULL | null sentinel |\n * | `~CURVE` | first curve | depth axis |\n * | `~CURVE` | other curves | channel names |\n * | `~A` | data rows | depths + values |\n *\n * ### Return value\n *\n * Returns an array with a single element (LAS files describe one hole):\n * ```js\n * [\n * {\n * holeId: 'MKC435',\n * units: { gamma: 'GAPI', density: 'G/CC' },\n * channels: {\n * gamma: { depths: [0.1, 0.2, ...], values: [45, 52, ...] },\n * density: { depths: [1.1, 1.2, ...], values: [2.3, 2.4, ...] },\n * }\n * }\n * ]\n * ```\n *\n * The `units` object contains the LAS unit string for each channel (lowercased\n * mnemonic key). Channels with fewer than 2 valid samples are omitted.\n *\n * ### Limitations\n *\n * `WRAP YES` (multi-line data rows) is not supported; a console warning is\n * emitted and parsing continues on a best-effort basis.\n *\n * @param {string} lasText - Raw LAS file text\n * @param {object} [options]\n * @param {string} [options.holeId] - Override the hole identifier\n * @param {number} [options.nullSentinel] - Override the null sentinel value\n * @returns {Array<{ holeId: string, units: object, channels: object }>}\n */\nexport function parseLasFile(lasText, options = {}) {\n const sections = splitSections(lasText);\n\n const versionSection = sections.find((s) => s.type === 'V');\n const wellSection = sections.find((s) => s.type === 'W');\n const curveSection = sections.find((s) => s.type === 'C');\n const dataSection = sections.find((s) => s.type === 'A');\n\n if (!dataSection) return [];\n\n const version = parseHeaderSection(versionSection?.lines);\n const well = parseHeaderSection(wellSection?.lines);\n const curveEntries = [];\n for (const line of (curveSection?.lines ?? [])) {\n const trimmed = line.trimStart();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const parsed = parseMnemonicLine(line);\n if (parsed) curveEntries.push(parsed);\n }\n\n // Warn on unsupported WRAP YES\n const wrap = version.WRAP?.value?.trim().toUpperCase() ?? 'NO';\n if (wrap === 'YES') {\n console.warn(\n 'parseLasFile: WRAP YES is not supported. Data rows may be read incorrectly.'\n );\n }\n\n // Resolve hole_id\n const holeId =\n options.holeId ??\n (well.WELL?.value?.trim() || null) ??\n (well.UWI?.value?.trim() || null) ??\n (well.API?.value?.trim() || null) ??\n 'unknown';\n\n // Resolve null sentinel (LAS file value takes precedence over default)\n let nullSentinel = GEOPHYSICS_NULL_SENTINEL;\n if (well.NULL?.value) {\n const v = Number(well.NULL.value);\n if (Number.isFinite(v)) nullSentinel = v;\n }\n if (options.nullSentinel !== undefined) {\n nullSentinel = options.nullSentinel;\n }\n\n if (curveEntries.length === 0) return [];\n\n // First curve is always depth\n const valueCurves = curveEntries.slice(1); // skip depth curve\n\n // Collect units for each value channel\n const units = {};\n for (const { mnem, unit } of valueCurves) {\n units[mnem.toLowerCase()] = unit;\n }\n\n // Accumulate channel data\n const channelData = {};\n for (const { mnem } of valueCurves) {\n channelData[mnem.toLowerCase()] = { depths: [], values: [] };\n }\n\n for (const line of dataSection.lines) {\n const trimmed = line.trimStart();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n const tokens = trimmed.split(/\\s+/);\n if (tokens.length < 2) continue;\n\n const depth = Number(tokens[0]);\n if (!Number.isFinite(depth) || depth === nullSentinel) continue;\n\n for (let i = 0; i < valueCurves.length; i++) {\n const key = valueCurves[i].mnem.toLowerCase();\n const v = Number(tokens[i + 1]);\n if (Number.isFinite(v) && v !== nullSentinel) {\n channelData[key].depths.push(depth);\n channelData[key].values.push(v);\n }\n }\n }\n\n // Drop channels with fewer than 2 valid samples\n const channels = {};\n for (const [key, data] of Object.entries(channelData)) {\n if (data.depths.length >= 2) channels[key] = data;\n }\n\n if (Object.keys(channels).length === 0) return [];\n\n return [{ holeId, units, channels }];\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Interpolate a 3D position and drill direction along a desurveyed trace.\n *\n * @param {Array<{hole_id: string, md: number, x: number, y: number, z: number, azimuth?: number, dip?: number}>} sortedTraceRows\n * Trace points sorted ascending by md.\n * @param {number} targetMd - Measured depth to interpolate at.\n * @returns {{x: number, y: number, z: number, dx: number, dy: number, dz: number}|null}\n * Position and unit drill direction (dz negative = downward), or null if not possible.\n */\nexport function interpolateTrace(sortedTraceRows, targetMd) {\n if (!sortedTraceRows || sortedTraceRows.length === 0) return null;\n if (!Number.isFinite(targetMd)) return null;\n\n const n = sortedTraceRows.length;\n\n if (n === 1) {\n const p = sortedTraceRows[0];\n return { x: Number(p.x), y: Number(p.y), z: Number(p.z), dx: 0, dy: 0, dz: -1 };\n }\n\n // Find enclosing segment\n let segIdx = -1;\n for (let i = 0; i < n - 1; i++) {\n const md0 = Number(sortedTraceRows[i].md);\n const md1 = Number(sortedTraceRows[i + 1].md);\n if (targetMd >= md0 && targetMd <= md1) {\n segIdx = i;\n break;\n }\n }\n\n let p0, p1, t;\n\n if (segIdx === -1) {\n if (targetMd < Number(sortedTraceRows[0].md)) {\n // Before first point — extrapolate using first segment\n p0 = sortedTraceRows[0];\n p1 = sortedTraceRows[1];\n } else {\n // Beyond last point — extend using last segment\n p0 = sortedTraceRows[n - 2];\n p1 = sortedTraceRows[n - 1];\n }\n const md0 = Number(p0.md);\n const md1 = Number(p1.md);\n const segLen = md1 - md0;\n t = segLen > 0 ? (targetMd - md0) / segLen : (targetMd < md0 ? 0 : 1);\n } else {\n p0 = sortedTraceRows[segIdx];\n p1 = sortedTraceRows[segIdx + 1];\n const md0 = Number(p0.md);\n const md1 = Number(p1.md);\n const segLen = md1 - md0;\n t = segLen > 0 ? (targetMd - md0) / segLen : 0;\n }\n\n const x = Number(p0.x) + t * (Number(p1.x) - Number(p0.x));\n const y = Number(p0.y) + t * (Number(p1.y) - Number(p0.y));\n const z = Number(p0.z) + t * (Number(p1.z) - Number(p0.z));\n\n // Compute drill direction: prefer azimuth/dip, fall back to finite differences\n let dx, dy, dz;\n const az0 = Number(p0.azimuth);\n const dip0 = Number(p0.dip);\n const az1 = Number(p1.azimuth);\n const dip1 = Number(p1.dip);\n\n if (Number.isFinite(az0) && Number.isFinite(dip0)) {\n const az = Number.isFinite(az1) && Number.isFinite(dip1)\n ? az0 + t * (az1 - az0)\n : az0;\n const dip = Number.isFinite(az1) && Number.isFinite(dip1)\n ? dip0 + t * (dip1 - dip0)\n : dip0;\n const azRad = (az * Math.PI) / 180;\n const dipRad = (dip * Math.PI) / 180;\n // Match directionCosines convention: dz = -sin(dip) (downward positive)\n dx = Math.cos(dipRad) * Math.sin(azRad);\n dy = Math.cos(dipRad) * Math.cos(azRad);\n dz = -Math.sin(dipRad);\n } else {\n // Finite difference from segment\n const ddx = Number(p1.x) - Number(p0.x);\n const ddy = Number(p1.y) - Number(p0.y);\n const ddz = Number(p1.z) - Number(p0.z);\n const len = Math.sqrt(ddx * ddx + ddy * ddy + ddz * ddz);\n if (len < 1e-10) return { x, y, z, dx: 0, dy: 0, dz: -1 };\n dx = ddx / len;\n dy = ddy / len;\n dz = ddz / len;\n }\n\n // Normalize direction\n const dlen = Math.sqrt(dx * dx + dy * dy + dz * dz);\n if (dlen < 1e-10) return { x, y, z, dx: 0, dy: 0, dz: -1 };\n return { x, y, z, dx: dx / dlen, dy: dy / dlen, dz: dz / dlen };\n}\n\n/**\n * Convert core-relative alpha/beta angles to a geographic plane normal.\n *\n * The drill frame:\n * D = unit drill direction (positive = downward along hole)\n * R = normalize(cross(U, D)) where U = (0,0,1) or fallback (0,1,0)\n * B = normalize(cross(D, R))\n *\n * Sanity checks:\n * alpha=90 → N perpendicular to D\n * alpha=0 → N parallel to D\n *\n * @param {number} alphaDeg - Alpha angle in degrees; 90 = plane perpendicular to drill axis.\n * @param {number} betaDeg - Beta angle in degrees; rotation of reference mark around drill axis.\n * @param {{dx: number, dy: number, dz: number}} drillDir - Unit drill direction vector.\n * @param {Object} [opts]\n * @param {'R'|'B'} [opts.betaZeroAxis='B'] - Frame axis corresponding to beta=0.\n * @param {number} [opts.betaHandedness=1] - +1 for right-hand rotation, -1 for left-hand.\n * @returns {{nx: number, ny: number, nz: number}} Unit normal vector in ENU coordinates.\n */\nexport function alphaBetaToNormal(alphaDeg, betaDeg, drillDir, opts = {}) {\n const { betaZeroAxis = 'B', betaHandedness = 1 } = opts;\n const { dx, dy, dz } = drillDir;\n const D = [dx, dy, dz];\n\n // Up vector with fallback when D is nearly vertical\n let U = [0, 0, 1];\n const dotDU = D[0] * U[0] + D[1] * U[1] + D[2] * U[2];\n if (Math.abs(dotDU) > 0.99) {\n U = [0, 1, 0];\n }\n\n // R = normalize(cross(U, D))\n const crossUD = [\n U[1] * D[2] - U[2] * D[1],\n U[2] * D[0] - U[0] * D[2],\n U[0] * D[1] - U[1] * D[0],\n ];\n const crossUDLen = Math.sqrt(crossUD[0] ** 2 + crossUD[1] ** 2 + crossUD[2] ** 2);\n const R = crossUDLen > 1e-10\n ? [crossUD[0] / crossUDLen, crossUD[1] / crossUDLen, crossUD[2] / crossUDLen]\n : [1, 0, 0];\n\n // B = normalize(cross(D, R))\n const crossDR = [\n D[1] * R[2] - D[2] * R[1],\n D[2] * R[0] - D[0] * R[2],\n D[0] * R[1] - D[1] * R[0],\n ];\n const crossDRLen = Math.sqrt(crossDR[0] ** 2 + crossDR[1] ** 2 + crossDR[2] ** 2);\n const B = crossDRLen > 1e-10\n ? [crossDR[0] / crossDRLen, crossDR[1] / crossDRLen, crossDR[2] / crossDRLen]\n : [0, 1, 0];\n\n // Starting axis for beta rotation\n const N_perp0 = betaZeroAxis === 'R' ? R : B;\n\n // Rodrigues rotation: rotate N_perp0 around D by (beta * betaHandedness) radians\n const betaRad = (betaDeg * Math.PI) / 180 * betaHandedness;\n const cosB = Math.cos(betaRad);\n const sinB = Math.sin(betaRad);\n const dotND = N_perp0[0] * D[0] + N_perp0[1] * D[1] + N_perp0[2] * D[2];\n const crossDN = [\n D[1] * N_perp0[2] - D[2] * N_perp0[1],\n D[2] * N_perp0[0] - D[0] * N_perp0[2],\n D[0] * N_perp0[1] - D[1] * N_perp0[0],\n ];\n const N_perp = [\n N_perp0[0] * cosB + crossDN[0] * sinB + D[0] * dotND * (1 - cosB),\n N_perp0[1] * cosB + crossDN[1] * sinB + D[1] * dotND * (1 - cosB),\n N_perp0[2] * cosB + crossDN[2] * sinB + D[2] * dotND * (1 - cosB),\n ];\n\n // theta = (90 - alpha) * pi/180; N = normalize(cos(theta)*N_perp + sin(theta)*D)\n const theta = ((90 - alphaDeg) * Math.PI) / 180;\n const cosT = Math.cos(theta);\n const sinT = Math.sin(theta);\n const Nx = cosT * N_perp[0] + sinT * D[0];\n const Ny = cosT * N_perp[1] + sinT * D[1];\n const Nz = cosT * N_perp[2] + sinT * D[2];\n\n const NLen = Math.sqrt(Nx * Nx + Ny * Ny + Nz * Nz);\n if (NLen < 1e-10) return { nx: 0, ny: 0, nz: 1 };\n return { nx: Nx / NLen, ny: Ny / NLen, nz: Nz / NLen };\n}\n\n/**\n * Compute 3D positions and plane normals for structural measurements.\n *\n * Prioritises alpha/beta core angles over geographic dip/azimuth.\n * Rows with no matching trace or missing required fields are excluded.\n *\n * @param {Array<Object>} structures - Flat rows with {hole_id, depth?, mid?, alpha?, beta?, dip?, azimuth?, ...}\n * @param {Array<Object>} traceRows - Flat rows with {hole_id, md, x, y, z, azimuth?, dip?}\n * @param {Object} [opts] - Options forwarded to alphaBetaToNormal.\n * @returns {Array<Object>} Structure rows enriched with {x, y, z, nx, ny, nz}.\n */\nexport function computeStructuralPositions(structures, traceRows, opts = {}) {\n if (!structures?.length || !traceRows?.length) return [];\n\n // Group trace rows by hole_id (lowercase) sorted ascending by md\n const tracesByHole = new Map();\n for (const row of traceRows) {\n const holeId = row.hole_id != null ? `${row.hole_id}`.trim().toLowerCase() : '';\n if (!holeId) continue;\n if (!tracesByHole.has(holeId)) tracesByHole.set(holeId, []);\n tracesByHole.get(holeId).push(row);\n }\n for (const [, rows] of tracesByHole) {\n rows.sort((a, b) => Number(a.md) - Number(b.md));\n }\n\n const result = [];\n for (const s of structures) {\n const holeId = s.hole_id != null ? `${s.hole_id}`.trim().toLowerCase() : '';\n if (!holeId) continue;\n\n const holeTrace = tracesByHole.get(holeId);\n if (!holeTrace || holeTrace.length === 0) continue;\n\n const depth = s.depth != null ? Number(s.depth) : (s.mid != null ? Number(s.mid) : null);\n if (!Number.isFinite(depth)) continue;\n\n const pos = interpolateTrace(holeTrace, depth);\n if (!pos) continue;\n\n const { x, y, z, dx, dy, dz } = pos;\n\n let nx, ny, nz;\n const alpha = s.alpha != null ? Number(s.alpha) : null;\n const beta = s.beta != null ? Number(s.beta) : null;\n\n if (Number.isFinite(alpha)) {\n const betaVal = Number.isFinite(beta) ? beta : 0;\n const n = alphaBetaToNormal(alpha, betaVal, { dx, dy, dz }, opts);\n nx = n.nx;\n ny = n.ny;\n nz = n.nz;\n } else {\n const dip = s.dip != null ? Number(s.dip) : null;\n const az = s.azimuth != null ? Number(s.azimuth) : null;\n if (!Number.isFinite(dip) || !Number.isFinite(az)) continue;\n // Geographic formula matching dipAzimuthToNormal in structuralScene.js\n const dipRad = (dip * Math.PI) / 180;\n const azRad = (az * Math.PI) / 180;\n nx = Math.sin(azRad) * Math.sin(dipRad);\n ny = Math.cos(azRad) * Math.sin(dipRad);\n nz = Math.cos(dipRad);\n }\n\n result.push({ ...s, x, y, z, nx, ny, nz });\n }\n\n return result;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Baselode Light Plotly template.\n *\n * Defines the Baselode visual identity for Plotly charts. Apply this template\n * by including it in the layout object passed to Plotly:\n *\n * ```js\n * import { BASELODE_TEMPLATE } from 'baselode';\n * Plotly.react(el, data, { ...layout, template: BASELODE_TEMPLATE });\n * ```\n *\n * Baselode plotting helpers apply this template by default. Pass a different\n * template as an option to override the visual style.\n */\n\n/** @type {string} Name of the Baselode Plotly template. */\nexport const BASELODE_TEMPLATE_NAME = 'baselode';\n\n/** @type {string} Alias for the Baselode Light template name. */\nexport const BASELODE_LIGHT_TEMPLATE_NAME = BASELODE_TEMPLATE_NAME;\n\n/** @type {string[]} Default colorway used across Baselode charts. */\nexport const BASELODE_COLORWAY = [\n '#8b1e3f',\n '#2563eb',\n '#16a34a',\n '#f59e0b',\n '#7c3aed',\n '#0ea5e9',\n '#ef4444',\n '#10b981',\n '#f97316',\n '#8b5cf6',\n];\n\n/** @type {Object} Baselode Light colour palette. */\nexport const BASELODE_LIGHT = {\n bg: '#ffffff',\n panel: '#f8fafc',\n ink: '#1e293b',\n ink_soft: '#64748b',\n grid: '#e8e8e8',\n line: '#d0d0d0',\n accent: '#f59e0b',\n accent_2: '#fcd34d',\n muted_1: '#94a3b8',\n muted_2: '#cbd5e1',\n muted_3: '#e2e8f0',\n primary: '#8b1e3f',\n primary_2: '#a8324f',\n};\n\n/**\n * Baselode Plotly template object.\n *\n * This object can be passed directly as the ``template`` property of a Plotly\n * layout to apply Baselode's default visual style. Baselode plotting helpers\n * include this template in their returned layout objects automatically.\n *\n * @type {Object}\n */\nexport const BASELODE_TEMPLATE = {\n layout: {\n paper_bgcolor: BASELODE_LIGHT.bg,\n plot_bgcolor: BASELODE_LIGHT.bg,\n colorway: BASELODE_COLORWAY,\n font: {\n family: 'Inter, system-ui, sans-serif',\n size: 12,\n color: BASELODE_LIGHT.ink,\n },\n title: {\n font: { size: 14, color: BASELODE_LIGHT.ink },\n x: 0.05,\n },\n hovermode: 'x unified',\n hoverlabel: {\n bgcolor: BASELODE_LIGHT.bg,\n bordercolor: BASELODE_LIGHT.line,\n font: { size: 12, color: BASELODE_LIGHT.ink },\n },\n legend: {\n bgcolor: 'rgba(255,255,255,0.9)',\n bordercolor: BASELODE_LIGHT.muted_3,\n borderwidth: 1,\n font: { size: 11, color: BASELODE_LIGHT.ink },\n orientation: 'h',\n yanchor: 'bottom',\n y: 1.02,\n xanchor: 'left',\n x: 0.0,\n },\n xaxis: {\n showline: true,\n linewidth: 1,\n linecolor: BASELODE_LIGHT.line,\n mirror: false,\n ticks: 'outside',\n tickwidth: 1,\n tickcolor: BASELODE_LIGHT.line,\n ticklen: 4,\n showgrid: true,\n gridcolor: BASELODE_LIGHT.grid,\n gridwidth: 1,\n zeroline: false,\n title_font: { color: BASELODE_LIGHT.ink, size: 12 },\n tickfont: { color: BASELODE_LIGHT.ink_soft, size: 10 },\n },\n yaxis: {\n showline: true,\n linewidth: 1,\n linecolor: BASELODE_LIGHT.line,\n mirror: false,\n ticks: 'outside',\n tickwidth: 1,\n tickcolor: BASELODE_LIGHT.line,\n ticklen: 4,\n showgrid: true,\n gridcolor: BASELODE_LIGHT.grid,\n gridwidth: 1,\n zeroline: false,\n title_font: { color: BASELODE_LIGHT.ink, size: 12 },\n tickfont: { color: BASELODE_LIGHT.ink_soft, size: 10 },\n },\n modebar: {\n remove: ['select2d', 'lasso2d', 'autoScale2d'],\n },\n bargap: 0.18,\n bargroupgap: 0.08,\n },\n data: {\n scatter: [{\n mode: 'lines+markers',\n line: { width: 2, color: BASELODE_LIGHT.primary },\n marker: {\n size: 7,\n color: BASELODE_LIGHT.primary_2,\n line: { width: 1.5, color: BASELODE_LIGHT.bg },\n },\n }],\n bar: [{\n marker: {\n color: BASELODE_LIGHT.primary,\n line: { color: BASELODE_LIGHT.bg, width: 0 },\n },\n }],\n histogram: [{\n marker: {\n color: BASELODE_LIGHT.primary,\n line: { color: BASELODE_LIGHT.bg, width: 0 },\n },\n }],\n box: [{\n fillcolor: BASELODE_LIGHT.accent,\n line: { color: BASELODE_LIGHT.ink, width: 1.5 },\n marker: { color: BASELODE_LIGHT.ink },\n }],\n violin: [{\n fillcolor: BASELODE_LIGHT.accent,\n line: { color: BASELODE_LIGHT.ink, width: 1.5 },\n marker: { color: BASELODE_LIGHT.ink },\n }],\n heatmap: [{\n colorscale: [\n [0.00, '#ffffff'],\n [0.20, '#f1f5f9'],\n [0.40, '#cbd5e1'],\n [0.60, '#94a3b8'],\n [0.80, '#475569'],\n [1.00, '#1e293b'],\n ],\n colorbar: {\n outlinecolor: BASELODE_LIGHT.line,\n tickcolor: BASELODE_LIGHT.line,\n tickfont: { color: BASELODE_LIGHT.ink_soft },\n },\n }],\n contour: [{\n colorscale: [\n [0.00, '#ffffff'],\n [0.25, '#fef3c7'],\n [0.50, '#f59e0b'],\n [0.75, '#92400e'],\n [1.00, '#1e293b'],\n ],\n colorbar: {\n outlinecolor: BASELODE_LIGHT.line,\n tickcolor: BASELODE_LIGHT.line,\n tickfont: { color: BASELODE_LIGHT.ink_soft },\n },\n }],\n },\n};\n\n/** @type {Object} Alias for {@link BASELODE_TEMPLATE} — the Baselode Light theme. */\nexport const BASELODE_LIGHT_TEMPLATE = BASELODE_TEMPLATE;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Baselode Dark Plotly template.\n *\n * Applies the Baselode Dark visual identity: dark warm backgrounds, Inter\n * typography, subtle warm grid lines, and light ink primary colours accented\n * with the signature highlight yellow.\n *\n * ```js\n * import { BASELODE_DARK_TEMPLATE } from 'baselode';\n * Plotly.react(el, data, { ...layout, template: BASELODE_DARK_TEMPLATE });\n * ```\n */\n\n/** @type {Object} Baselode Dark colour palette. */\nexport const BASELODE_DARK = {\n bg: '#1b1b1f',\n panel: '#25252a',\n ink: '#f0f0e4',\n ink_soft: '#c8c8b8',\n grid: '#2a2a26',\n line: '#3a3a34',\n accent: '#ffffbb',\n accent_2: '#f3ef9b',\n muted_1: '#8a8a80',\n muted_2: '#5e5e56',\n muted_3: '#3a3a34',\n};\n\n/** @type {string} Name key for the Baselode Dark template. */\nexport const BASELODE_DARK_TEMPLATE_NAME = 'baselode-dark';\n\n/**\n * Baselode Dark Plotly template object.\n *\n * Pass directly as the ``template`` property of a Plotly layout to apply\n * the Baselode Dark visual style.\n *\n * @type {Object}\n */\nexport const BASELODE_DARK_TEMPLATE = {\n layout: {\n font: {\n family: 'Inter, Arial, sans-serif',\n color: BASELODE_DARK.ink,\n size: 14,\n },\n title: {\n x: 0.02,\n xanchor: 'left',\n font: {\n family: 'Inter, Arial, sans-serif',\n size: 22,\n color: BASELODE_DARK.ink,\n },\n },\n paper_bgcolor: BASELODE_DARK.bg,\n plot_bgcolor: BASELODE_DARK.bg,\n colorway: [\n BASELODE_DARK.ink,\n BASELODE_DARK.accent,\n BASELODE_DARK.muted_1,\n BASELODE_DARK.accent_2,\n BASELODE_DARK.muted_2,\n BASELODE_DARK.muted_3,\n ],\n margin: { l: 70, r: 30, t: 70, b: 60 },\n hovermode: 'x unified',\n hoverlabel: {\n bgcolor: BASELODE_DARK.panel,\n bordercolor: BASELODE_DARK.accent,\n font: {\n family: 'Inter, Arial, sans-serif',\n color: BASELODE_DARK.ink,\n size: 13,\n },\n },\n legend: {\n bgcolor: 'rgba(37,37,42,0.88)',\n bordercolor: BASELODE_DARK.line,\n borderwidth: 1,\n font: {\n family: 'Inter, Arial, sans-serif',\n color: BASELODE_DARK.ink,\n size: 12,\n },\n orientation: 'h',\n yanchor: 'bottom',\n y: 1.02,\n xanchor: 'left',\n x: 0.0,\n },\n xaxis: {\n showline: false,\n ticks: 'outside',\n tickwidth: 1,\n tickcolor: BASELODE_DARK.muted_1,\n ticklen: 6,\n showgrid: true,\n gridcolor: BASELODE_DARK.grid,\n gridwidth: 1,\n zeroline: false,\n title_font: { color: BASELODE_DARK.ink },\n tickfont: { color: BASELODE_DARK.ink_soft },\n },\n yaxis: {\n showline: false,\n ticks: 'outside',\n tickwidth: 1,\n tickcolor: BASELODE_DARK.muted_1,\n ticklen: 6,\n showgrid: true,\n gridcolor: BASELODE_DARK.grid,\n gridwidth: 1,\n zeroline: false,\n title_font: { color: BASELODE_DARK.ink },\n tickfont: { color: BASELODE_DARK.ink_soft },\n },\n bargap: 0.18,\n bargroupgap: 0.08,\n },\n data: {\n scatter: [{\n mode: 'lines+markers',\n line: { width: 2.5, color: BASELODE_DARK.ink },\n marker: {\n size: 7,\n color: BASELODE_DARK.ink,\n line: { width: 1.5, color: BASELODE_DARK.bg },\n },\n }],\n bar: [{\n marker: {\n color: BASELODE_DARK.ink,\n line: { color: BASELODE_DARK.bg, width: 0 },\n },\n }],\n histogram: [{\n marker: {\n color: BASELODE_DARK.ink,\n line: { color: BASELODE_DARK.bg, width: 0 },\n },\n }],\n box: [{\n fillcolor: BASELODE_DARK.accent,\n line: { color: BASELODE_DARK.ink, width: 1.5 },\n marker: { color: BASELODE_DARK.ink },\n }],\n violin: [{\n fillcolor: BASELODE_DARK.accent,\n line: { color: BASELODE_DARK.ink, width: 1.5 },\n marker: { color: BASELODE_DARK.ink },\n }],\n heatmap: [{\n colorscale: [\n [0.00, '#1b1b1f'],\n [0.20, '#2e2e28'],\n [0.40, '#5e5e50'],\n [0.60, '#c8c89a'],\n [0.80, '#f3ef9b'],\n [1.00, '#ffffbb'],\n ],\n colorbar: {\n outlinecolor: BASELODE_DARK.ink,\n tickcolor: BASELODE_DARK.ink,\n tickfont: { color: BASELODE_DARK.ink_soft },\n },\n }],\n contour: [{\n colorscale: [\n [0.00, '#1b1b1f'],\n [0.25, '#2e2e28'],\n [0.50, '#6b6b50'],\n [0.75, '#f3ef9b'],\n [1.00, '#ffffbb'],\n ],\n colorbar: {\n outlinecolor: BASELODE_DARK.ink,\n tickcolor: BASELODE_DARK.ink,\n tickfont: { color: BASELODE_DARK.ink_soft },\n },\n }],\n },\n};\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Semantic colour mapping system for Baselode graphs.\n *\n * This module provides the **semantic colour mapping** layer of Baselode graph\n * styling. Plotly templates handle overall chart appearance (fonts, layout,\n * backgrounds); this module maps *domain values* (commodities, lithologies,\n * categorical logging data) to specific colours.\n *\n * Built-in maps ship sensible defaults. Users can supply their own\n * dictionaries or override individual entries via {@link resolveColourMap}.\n *\n * @module colourMap\n */\n\n/**\n * Fallback colour used when a value is absent from any colour map.\n * @type {string}\n */\nexport const FALLBACK_COLOUR = '#7f7f7f';\n\n/**\n * Built-in semantic colours for common commodity / assay elements.\n * @type {Object.<string, string>}\n */\nexport const COMMODITY_COLOURS = {\n Au: '#FFD700', // gold\n Ag: '#C0C0C0', // silver\n Cu: '#B87333', // copper / orange-brown\n Fe: '#8B4513', // iron / rusty brown\n Ni: '#4CAF50', // nickel / green\n Zn: '#78909C', // zinc / blue-grey\n Pb: '#607D8B', // lead / slate\n Mo: '#9C27B0', // molybdenite / purple\n Co: '#2196F3', // cobalt / blue\n Li: '#FF5722', // lithium / deep orange\n Mn: '#795548', // manganese / brown\n Cr: '#009688', // chromium / teal\n V: '#673AB7', // vanadium / deep purple\n W: '#FF9800', // tungsten / amber\n Sn: '#9E9E9E', // tin / medium grey\n Ti: '#00BCD4', // titanium / cyan\n Al: '#FFEB3B', // aluminium / yellow\n U: '#8BC34A', // uranium / lime green\n};\n\n/**\n * Built-in semantic colours for common lithology categories.\n * @type {Object.<string, string>}\n */\nexport const LITHOLOGY_COLOURS = {\n // Sedimentary\n shale: '#607D8B',\n mudstone: '#78909C',\n siltstone: '#90A4AE',\n sandstone: '#F5CBA7',\n limestone: '#B0BEC5',\n dolomite: '#CFD8DC',\n conglomerate: '#D7CCC8',\n coal: '#212121',\n // Iron-formation\n BIF: '#8B4513',\n ironstone: '#A0522D',\n // Igneous – intrusive\n granite: '#EF9A9A',\n granodiorite: '#F48FB1',\n diorite: '#CE93D8',\n gabbro: '#546E7A',\n peridotite: '#33691E',\n pegmatite: '#FFF9C4',\n // Igneous – extrusive / volcanic\n basalt: '#37474F',\n andesite: '#78909C',\n rhyolite: '#FFCCBC',\n dacite: '#FFAB91',\n tuff: '#D7CCC8',\n breccia: '#BCAAA4',\n // Metamorphic\n schist: '#80CBC4',\n gneiss: '#4DB6AC',\n quartzite: '#E0F7FA',\n marble: '#F5F5F5',\n slate: '#455A64',\n phyllite: '#80DEEA',\n // Other\n quartz: '#ECEFF1',\n calcite: '#F9FBE7',\n vein: '#FFFFFF',\n unknown: '#9E9E9E',\n};\n\n/**\n * Registry of all built-in colour maps, keyed by lower-case name.\n * @type {Object.<string, Object.<string, string>>}\n */\nexport const BUILTIN_COLOUR_MAPS = {\n commodity: COMMODITY_COLOURS,\n lithology: LITHOLOGY_COLOURS,\n};\n\n/**\n * Return the colour for *value* from *colourMap*, or *fallback* if absent.\n *\n * The lookup is case-insensitive: both *value* and map keys are trimmed and\n * lower-cased before comparison.\n *\n * @param {string} value - Domain value to look up (e.g. `\"Cu\"`, `\"granite\"`).\n * @param {Object.<string, string>} colourMap - Mapping of domain values to CSS colour strings.\n * @param {string} [fallback=FALLBACK_COLOUR] - Colour to return when *value* is not found.\n * @returns {string} A CSS colour string.\n */\nexport function getColour(value, colourMap, fallback = FALLBACK_COLOUR) {\n if (!colourMap || value == null) return fallback;\n const key = String(value).trim();\n // Exact match first\n if (Object.prototype.hasOwnProperty.call(colourMap, key)) return colourMap[key];\n // Case-insensitive match\n const keyLower = key.toLowerCase();\n for (const [mapKey, mapColour] of Object.entries(colourMap)) {\n if (String(mapKey).trim().toLowerCase() === keyLower) return mapColour;\n }\n return fallback;\n}\n\n/**\n * Return a colour map object from a name or pass through a user-supplied object.\n *\n * @param {string|Object.<string, string>|null|undefined} nameOrMap\n * - `null` / `undefined`: returns an empty object.\n * - A `string`: looked up in {@link BUILTIN_COLOUR_MAPS}. Unknown names\n * throw a `RangeError`.\n * - An `object`: returned as-is (user-supplied mapping).\n * @returns {Object.<string, string>} Colour map object.\n * @throws {RangeError} If *nameOrMap* is a string that does not match any built-in map.\n * @throws {TypeError} If *nameOrMap* is not `null`, a string, or a plain object.\n */\nexport function resolveColourMap(nameOrMap) {\n if (nameOrMap == null) return {};\n if (typeof nameOrMap === 'string') {\n const key = nameOrMap.trim().toLowerCase();\n if (Object.prototype.hasOwnProperty.call(BUILTIN_COLOUR_MAPS, key)) {\n return BUILTIN_COLOUR_MAPS[key];\n }\n const available = Object.keys(BUILTIN_COLOUR_MAPS).sort().join(', ');\n throw new RangeError(\n `Unknown built-in colour map '${nameOrMap}'. Available maps: ${available}`\n );\n }\n if (\n typeof nameOrMap === 'object' &&\n Object.getPrototypeOf(nameOrMap) === Object.prototype\n ) return nameOrMap;\n throw new TypeError(\n `colourMap must be null, a string, or a plain object; got ${\n Array.isArray(nameOrMap) ? 'Array' : Object.prototype.toString.call(nameOrMap)\n }`\n );\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n// Shared drillhole 2D visualization helpers for reuse beyond the UI layer.\n// These helpers build Plotly-ready data/layout objects based on interval points.\n\nimport { getColour, resolveColourMap, COMMODITY_COLOURS } from './colourMap.js';\nimport { BASELODE_TEMPLATE } from './baselodeTemplate.js';\n\n/** Default color for numeric line traces */\nexport const NUMERIC_LINE_COLOR = '#8b1e3f';\n\n/** Default color for numeric markers */\nexport const NUMERIC_MARKER_COLOR = '#a8324f';\n\n/**\n * Auto-detect a commodity colour for a column name such as \"Au_ppm\" or \"Cu_eq\".\n * Splits on `_`, `-`, `/`, or whitespace and checks each token against\n * COMMODITY_COLOURS (exact match first, then case-insensitive).\n * Returns null when no commodity element is recognised.\n * @param {string} property\n * @returns {string|null}\n */\nfunction commodityColourForProperty(property) {\n if (!property) return null;\n const tokens = property.split(/[_\\-/\\s]+/);\n for (const token of tokens) {\n if (Object.prototype.hasOwnProperty.call(COMMODITY_COLOURS, token)) {\n return COMMODITY_COLOURS[token];\n }\n const low = token.toLowerCase();\n for (const [key, colour] of Object.entries(COMMODITY_COLOURS)) {\n if (key.toLowerCase() === low) return colour;\n }\n }\n return null;\n}\n\n/** Color for error bars */\nexport const ERROR_COLOR = '#6b7280';\n\n/** Default compact strip-log margins */\nexport const STRIPLOG_COMPACT_MARGIN = { l: 42, r: 4, t: 4, b: 30 };\n\n/** Default strip-log axis tick size */\nexport const STRIPLOG_AXIS_TICK_FONT_SIZE = 10;\n\n/** Default strip-log axis title size */\nexport const STRIPLOG_AXIS_TITLE_FONT_SIZE = 12;\n\nfunction normalizeAxisTitle(t) {\n if (!t) return {};\n return typeof t === 'string' ? { text: t } : t;\n}\n\nfunction applyStriplogLayoutDefaults(layout = {}) {\n const xTitle = normalizeAxisTitle(layout.xaxis && layout.xaxis.title);\n const yTitle = normalizeAxisTitle(layout.yaxis && layout.yaxis.title);\n return {\n ...layout,\n margin: STRIPLOG_COMPACT_MARGIN,\n autosize: true,\n width: undefined,\n xaxis: {\n ...(layout.xaxis || {}),\n tickfont: {\n ...((layout.xaxis && layout.xaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...xTitle,\n font: { ...(xTitle.font || {}), size: STRIPLOG_AXIS_TITLE_FONT_SIZE },\n },\n },\n yaxis: {\n ...(layout.yaxis || {}),\n automargin: true,\n tickfont: {\n ...((layout.yaxis && layout.yaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...yTitle,\n font: { ...(yTitle.font || {}), size: STRIPLOG_AXIS_TITLE_FONT_SIZE },\n },\n },\n };\n}\n\n/**\n * Check if a hole has data for a specific property\n * @param {Object} hole - Hole object with points array\n * @param {string} property - Property name to check\n * @returns {boolean} True if hole has at least one valid value for the property\n */\nexport function holeHasData(hole, property) {\n if (!hole || !property) return false;\n const pts = hole.points || [];\n for (let i = 0; i < pts.length; i += 1) {\n const value = pts[i]?.[property];\n if (value === undefined || value === null) continue;\n if (typeof value === 'number' && Number.isFinite(value)) return true;\n if (typeof value === 'string' && value.trim() !== '') return true;\n }\n return false;\n}\n\n/**\n * Build array of interval points for visualization from hole data\n * Extracts depth intervals and property values, deduplicates, and sorts by depth\n * @param {Object} hole - Hole object with points array\n * @param {string} property - Property name to extract\n * @param {boolean} isCategorical - Whether property is categorical (vs numeric)\n * @returns {Array<{z: number, val: *, from: number, to: number, errorPlus: number, errorMinus: number}>} Array of interval points\n */\nexport function buildIntervalPoints(hole, property, isCategorical) {\n if (!hole || !property) return [];\n const rawPoints = hole?.points || [];\n const out = [];\n const seen = new Set();\n rawPoints.forEach((p) => {\n let fromVal = Number(\n p.from ??\n p.samp_from ??\n p.sample_from ??\n p.fromdepth ??\n p.from_depth ??\n p.depth_from\n );\n let toVal = Number(\n p.to ??\n p.samp_to ??\n p.sample_to ??\n p.todepth ??\n p.to_depth ??\n p.depth_to\n );\n // Fall back to depth for point-schema data (e.g. structural measurements)\n if (!Number.isFinite(fromVal) || !Number.isFinite(toVal)) {\n const depthVal = Number(p.depth ?? p.md);\n if (Number.isFinite(depthVal)) {\n fromVal = depthVal;\n toVal = depthVal;\n }\n }\n const rawVal = p?.[property];\n if (!Number.isFinite(fromVal) || !Number.isFinite(toVal) || toVal < fromVal) return;\n if (rawVal === undefined || rawVal === null || rawVal === '') return;\n if (isCategorical && typeof rawVal === 'string' && /^(nan|null|none)$/i.test(rawVal.trim())) return;\n const key = `${property}:${fromVal}-${toVal}:${String(rawVal)}`;\n if (seen.has(key)) return;\n seen.add(key);\n const mid = (fromVal + toVal) / 2;\n const val = isCategorical ? rawVal : Number(rawVal);\n if (!isCategorical && !Number.isFinite(val)) return;\n out.push({\n z: mid,\n val,\n from: fromVal,\n to: toVal,\n errorPlus: toVal - mid,\n errorMinus: mid - fromVal\n });\n });\n return out.sort((a, b) => b.z - a.z);\n}\n\n/**\n * Build Plotly configuration for categorical property visualization\n * @private\n * @param {Array<Object>} points - Interval points array\n * @param {string} property - Property name for title\n * @param {Object|string|null} [colourMap] - Optional semantic colour map (object or built-in name)\n * @param {Object} [template] - Plotly template to include in layout\n * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildCategoricalConfig(points, property, colourMap, template) {\n if (!points.length) return { data: [], layout: {} };\n const safe = points\n .filter((point) => Number.isFinite(point?.from) && Number.isFinite(point?.to) && point.to > point.from)\n .map((point) => ({ ...point, category: `${point?.val ?? ''}`.trim() }))\n .filter((point) => point.category !== '' && !/^(nan|null|none)$/i.test(point.category))\n .sort((a, b) => a.from - b.from || a.to - b.to);\n\n if (!safe.length) return { data: [], layout: {} };\n\n const resolvedCmap = resolveColourMap(colourMap);\n\n const fallbackPalette = [\n '#1f77b4', // blue\n '#ff7f0e', // orange\n '#2ca02c', // green\n '#d62728', // red\n '#9467bd', // purple\n '#17becf', // cyan\n '#bcbd22', // olive\n '#e377c2', // pink\n '#8c564b', // brown\n '#393b79', // indigo\n '#e6550d', // deep orange\n '#31a354', // deep green\n '#756bb1', // violet\n '#636363', // dark gray\n ];\n const uniqueCategories = [...new Set(safe.map((point) => point.category))];\n\n function pickColour(cat, idx) {\n if (resolvedCmap && Object.keys(resolvedCmap).length > 0) {\n const c = getColour(cat, resolvedCmap, null);\n if (c !== null) return c;\n }\n return fallbackPalette[idx % fallbackPalette.length];\n }\n\n const colorByCategory = new Map(\n uniqueCategories.map((category, idx) => [category, pickColour(category, idx)])\n );\n\n // One bar trace per unique category. Each bar starts at `base` (from depth)\n // and has height (to - from). barmode:'overlay' lets non-overlapping intervals\n // from different traces coexist at the same x position.\n const traces = uniqueCategories.map((cat) => {\n const intervals = safe.filter((seg) => seg.category === cat);\n return {\n type: 'bar',\n x: intervals.map(() => 0.5),\n y: intervals.map((s) => s.to - s.from),\n base: intervals.map((s) => s.from),\n width: 1,\n marker: { color: colorByCategory.get(cat), line: { width: 0 } },\n name: cat,\n showlegend: false,\n customdata: intervals.map((s) => [s.from, s.to]),\n hovertemplate: `${property}: ${cat}<br>from: %{customdata[0]:.3f} to: %{customdata[1]:.3f}<extra></extra>`,\n };\n });\n\n const layout = {\n barmode: 'overlay',\n bargap: 0,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed', zeroline: false },\n showlegend: false,\n title: property || undefined,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data: traces, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Build Plotly configuration for numeric property visualization\n * @private\n * @param {Array<Object>} points - Interval points array\n * @param {string} property - Property name for axis label\n * @param {string} chartType - Chart type ('bar', 'markers', 'line', 'markers+line')\n * @param {string} [color] - Override colour for line/markers (e.g. commodity colour)\n * @param {Object} [template] - Plotly template to include in layout\n * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildNumericConfig(points, property, chartType, color, template) {\n if (!points.length) return { data: [], layout: {} };\n const isBar = chartType === 'bar';\n const isMarkersOnly = chartType === 'markers';\n const isLineOnly = chartType === 'line';\n\n const lineColor = color || NUMERIC_LINE_COLOR;\n const markerColor = color || NUMERIC_MARKER_COLOR;\n\n const baseTrace = {\n x: points.map((p) => p.val),\n y: points.map((p) => p.z),\n hovertemplate: `${property}: %{x}<br>from: %{customdata[0]:.3f} to: %{customdata[1]:.3f}<extra></extra>`,\n customdata: points.map((p) => [Math.min(p.from, p.to), Math.max(p.from, p.to)])\n };\n\n const errorConfig = {\n type: 'data',\n symmetric: false,\n array: points.map((p) => p.errorPlus),\n arrayminus: points.map((p) => p.errorMinus),\n thickness: 1.5,\n width: 2,\n color: ERROR_COLOR\n };\n\n const trace = isBar\n ? {\n ...baseTrace,\n type: 'bar',\n orientation: 'h',\n marker: { color: lineColor },\n error_y: errorConfig\n }\n : {\n ...baseTrace,\n type: 'scatter',\n mode: isMarkersOnly ? 'markers' : isLineOnly ? 'lines' : 'lines+markers',\n line: { color: lineColor, width: 2 },\n marker: { size: 7, color: markerColor },\n error_y: isLineOnly ? undefined : errorConfig\n };\n\n const layout = {\n xaxis: { title: property, zeroline: false },\n yaxis: { title: 'Depth (m)', autorange: 'reversed', zeroline: false },\n barmode: 'overlay',\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data: [trace], layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Build complete Plotly configuration for property visualization\n * @param {Object} options - Configuration options\n * @param {Array<Object>} options.points - Interval points to visualize\n * @param {boolean} options.isCategorical - Whether property is categorical\n * @param {string} options.property - Property name\n * @param {string} options.chartType - Chart type ('bar', 'markers', 'line', 'categorical', etc.)\n * @param {Object|string|null} [options.colourMap] - Optional semantic colour map (object or built-in name)\n * @param {Object} [options.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{data: Array, layout: Object}} Complete Plotly configuration\n */\nexport function buildPlotConfig({ points, isCategorical, property, chartType, colourMap, template }) {\n if (!points || !points.length || !property) return { data: [], layout: {} };\n if (isCategorical || chartType === 'categorical') {\n return buildCategoricalConfig(points, property, colourMap, template);\n }\n const colour = commodityColourForProperty(property);\n return buildNumericConfig(points, property, chartType, colour, template);\n}\n\n/**\n * Build a categorical strip-log Plotly config directly from interval rows.\n * @param {Array<Object>} rows - Interval rows (e.g. geology)\n * @param {Object} options - Field mapping options\n * @param {string} options.fromCol - From-depth column\n * @param {string} options.toCol - To-depth column\n * @param {string} options.categoryCol - Category label column\n * @param {Object|string|null} [options.colourMap] - Optional semantic colour map (object or built-in name)\n * @param {Object} [options.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{data: Array, layout: Object}} Plotly configuration for strip-log rendering\n */\nexport function buildCategoricalStripLogConfig(\n rows = [],\n {\n fromCol = 'from',\n toCol = 'to',\n categoryCol = 'geology_code',\n colourMap = null,\n template = undefined,\n } = {}\n) {\n const points = [];\n rows.forEach((row) => {\n const from = Number(row?.[fromCol]);\n const to = Number(row?.[toCol]);\n const category = row?.[categoryCol];\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) return;\n if (category === undefined || category === null || `${category}`.trim() === '') return;\n const mid = (from + to) / 2;\n points.push({\n z: mid,\n val: `${category}`,\n from,\n to,\n errorPlus: to - mid,\n errorMinus: mid - from\n });\n });\n\n points.sort((a, b) => b.z - a.z);\n return buildPlotConfig({\n points,\n isCategorical: true,\n property: categoryCol,\n chartType: 'categorical',\n colourMap,\n template,\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Plotly visualization builders for structural measurements.\n *\n * Provides:\n * - buildTadpoleConfig: 1D strip log tadpole plot (dip head + azimuth tail)\n * - buildStructuralStripConfig: categorical interval strip log\n * - buildStrikeDipSymbol: 2D map strike/dip symbol geometry\n */\n\nimport { AZIMUTH, DEPTH, DIP, FROM, TO } from '../data/datamodel.js';\nimport { BASELODE_TEMPLATE } from './baselodeTemplate.js';\n\nconst DEFAULT_PALETTE = [\n '#0f172a', '#1e3a5f', '#7c3aed', '#dc2626', '#16a34a',\n '#d97706', '#0ea5e9', '#db2777', '#65a30d', '#9333ea',\n];\n\nconst STRIPLOG_COMPACT_MARGIN = { l: 42, r: 4, t: 4, b: 30 };\nconst STRIPLOG_AXIS_TICK_FONT_SIZE = 10;\nconst STRIPLOG_AXIS_TITLE_FONT_SIZE = 12;\n\nfunction applyStriplogLayoutDefaults(layout = {}) {\n return {\n ...layout,\n margin: STRIPLOG_COMPACT_MARGIN,\n autosize: true,\n width: undefined,\n xaxis: {\n ...(layout.xaxis || {}),\n tickfont: {\n ...((layout.xaxis && layout.xaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...((layout.xaxis && layout.xaxis.title) || {}),\n font: {\n ...(((layout.xaxis && layout.xaxis.title && layout.xaxis.title.font) || {})),\n size: STRIPLOG_AXIS_TITLE_FONT_SIZE,\n },\n },\n },\n yaxis: {\n ...(layout.yaxis || {}),\n automargin: true,\n tickfont: {\n ...((layout.yaxis && layout.yaxis.tickfont) || {}),\n size: STRIPLOG_AXIS_TICK_FONT_SIZE,\n },\n title: {\n ...((layout.yaxis && layout.yaxis.title) || {}),\n font: {\n ...(((layout.yaxis && layout.yaxis.title && layout.yaxis.title.font) || {})),\n size: STRIPLOG_AXIS_TITLE_FONT_SIZE,\n },\n },\n },\n };\n}\n\n/**\n * Build a Plotly tadpole log config for structural point measurements.\n *\n * Each measurement renders as a circle (head) at its depth with a tail\n * pointing toward the dip direction. Tail length is proportional to dip magnitude.\n *\n * @param {Array<Object>} points - Structural point rows\n * @param {Object} opts\n * @param {number} [opts.tailScale=0.3] - Controls tail length relative to dip magnitude\n * @param {string|null} [opts.colorBy=null] - Column name to color heads by (e.g. 'defect')\n * @param {string[]} [opts.palette] - Color palette\n * @param {string} [opts.depthCol='depth'] - Column for measured depth\n * @param {string} [opts.dipCol='dip'] - Column for dip angle\n * @param {string} [opts.azCol='azimuth'] - Column for dip direction\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildTadpoleConfig(points, {\n tailScale = 5,\n colorBy = null,\n palette = DEFAULT_PALETTE,\n depthCol = DEPTH,\n dipCol = DIP,\n azCol = AZIMUTH,\n template = undefined,\n} = {}) {\n const valid = points.filter(p =>\n p[depthCol] != null && p[dipCol] != null && p[azCol] != null\n );\n\n if (!valid.length) {\n return { data: [], layout: {} };\n }\n\n // Build color map for categories\n const colorMap = {};\n if (colorBy) {\n const categories = [...new Set(valid.map(p => p[colorBy]).filter(v => v != null))].sort();\n categories.forEach((cat, i) => { colorMap[cat] = palette[i % palette.length]; });\n }\n\n // Group by category for legend traces\n const byCat = new Map();\n const shapes = [];\n\n for (const p of valid) {\n const depth = Number(p[depthCol]);\n const dip = Number(p[dipCol]);\n const az = Number(p[azCol]);\n const cat = colorBy ? (p[colorBy] ?? '_default') : '_default';\n const color = colorBy ? (colorMap[cat] ?? '#0f172a') : '#0f172a';\n\n if (!byCat.has(cat)) {\n byCat.set(cat, { xs: [], ys: [], dips: [], azs: [], color });\n }\n const group = byCat.get(cat);\n // Head positioned at x=dip (degrees)\n group.xs.push(dip);\n group.ys.push(depth);\n group.dips.push(dip);\n group.azs.push(az);\n\n // Tail: starts at (dip, depth), direction encodes azimuth.\n // Length scales with dip magnitude (in degree units on the x-axis).\n const azRad = (az * Math.PI) / 180;\n const length = tailScale * (Math.abs(dip) / 90);\n const dx = Math.sin(azRad) * length; // x-component (degrees)\n const dy = Math.cos(azRad) * length; // y-component (degrees, visual only)\n\n shapes.push({\n type: 'line',\n x0: dip, y0: depth,\n x1: dip + dx, y1: depth + dy,\n line: { color, width: 2 },\n });\n }\n\n const data = [];\n const showLegend = colorBy && byCat.size > 1;\n\n for (const [cat, group] of byCat.entries()) {\n data.push({\n type: 'scatter',\n x: group.xs,\n y: group.ys,\n mode: 'markers',\n name: cat !== '_default' ? String(cat) : undefined,\n marker: { size: 8, color: group.color },\n showlegend: showLegend && cat !== '_default',\n customdata: group.dips.map((d, i) => [d, group.azs[i]]),\n hovertemplate: 'Depth: %{y}<br>Dip: %{customdata[0]}<br>Az: %{customdata[1]}<extra></extra>',\n });\n }\n\n const layout = {\n shapes,\n height: 400,\n margin: { l: 40, r: 10, t: 10, b: 40 },\n xaxis: {\n title: 'Dip (°)',\n autorange: true,\n fixedrange: true,\n zeroline: true,\n tickvals: [-90, -60, -30, 0, 30, 60, 90],\n },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: !!showLegend,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout };\n}\n\n/**\n * Build a Plotly categorical strip log config for structural interval measurements.\n *\n * @param {Array<Object>} intervals - Structural interval rows\n * @param {Object} opts\n * @param {string} [opts.labelCol='defect'] - Column for interval label/color\n * @param {string[]} [opts.palette] - Color palette\n * @param {string} [opts.fromCol='from'] - From depth column\n * @param {string} [opts.toCol='to'] - To depth column\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildStructuralStripConfig(intervals, {\n labelCol = 'structure_type',\n palette = DEFAULT_PALETTE,\n fromCol = FROM,\n toCol = TO,\n template = undefined,\n} = {}) {\n const records = intervals\n .filter(iv => iv[fromCol] != null && iv[toCol] != null && Number(iv[toCol]) > Number(iv[fromCol]))\n .filter(iv => {\n const lv = iv[labelCol];\n if (lv == null) return false;\n const s = String(lv).trim();\n return s !== '' && !/^(nan|null|none)$/i.test(s);\n })\n .map(iv => ({ from: Number(iv[fromCol]), to: Number(iv[toCol]), label: String(iv[labelCol]).trim() }))\n .sort((a, b) => a.from - b.from);\n\n if (!records.length) {\n return { data: [], layout: {} };\n }\n\n const shapes = [];\n const textY = [];\n const texts = [];\n\n records.forEach((rec, idx) => {\n shapes.push({\n type: 'rect',\n xref: 'x', yref: 'y',\n x0: 0, x1: 1,\n y0: rec.from, y1: rec.to,\n fillcolor: palette[idx % palette.length],\n line: { width: 0 },\n layer: 'below',\n });\n textY.push(0.5 * (rec.from + rec.to));\n texts.push(rec.label);\n });\n\n const data = [{\n type: 'scatter',\n x: Array(texts.length).fill(0.5),\n y: textY,\n mode: 'text',\n text: texts,\n textposition: 'middle center',\n showlegend: false,\n hoverinfo: 'text',\n }];\n\n const layout = {\n shapes,\n height: 400,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Word-wrap text at word boundaries, inserting Plotly HTML line breaks.\n * @private\n * @param {string} text\n * @param {number} charsPerLine\n * @returns {string}\n */\nfunction wrapComment(text, charsPerLine) {\n if (!text) return '';\n const words = String(text).trim().split(/\\s+/);\n const lines = [];\n let current = '';\n for (const word of words) {\n if (current && current.length + 1 + word.length > charsPerLine) {\n lines.push(current);\n current = word;\n } else {\n current = current ? `${current} ${word}` : word;\n }\n }\n if (current) lines.push(current);\n return lines.join('<br>');\n}\n\n/**\n * Build a Plotly comments log config — depth intervals with text annotations overlaid.\n *\n * Each interval is drawn as a lightly shaded rectangle spanning its from/to depth.\n * Non-empty comments are word-wrapped and centered inside the rectangle.\n * Intervals with no comment show a thin border only.\n *\n * @param {Array<Object>} intervals - Interval rows (must have from, to, and a comment column)\n * @param {Object} opts\n * @param {string} [opts.commentCol='comments'] - Column containing comment text\n * @param {string} [opts.fromCol='from'] - From depth column\n * @param {string} [opts.toCol='to'] - To depth column\n * @param {string} [opts.bgColor='#f1f5f9'] - Fill color for intervals with a comment\n * @param {string} [opts.borderColor='#cbd5e1'] - Rectangle border color\n * @param {string} [opts.textColor='#1e293b'] - Comment text color\n * @param {number} [opts.charsPerLine=18] - Characters before word-wrapping\n * @param {Object} [opts.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {{ data: Array, layout: Object }} Plotly figure config\n */\nexport function buildCommentsConfig(intervals, {\n commentCol = 'comments',\n fromCol = FROM,\n toCol = TO,\n bgColor = '#f1f5f9',\n borderColor = '#cbd5e1',\n textColor = '#1e293b',\n charsPerLine = 18,\n template = undefined,\n} = {}) {\n const records = intervals\n .filter(iv => iv[fromCol] != null && iv[toCol] != null && Number(iv[toCol]) > Number(iv[fromCol]))\n .map(iv => {\n const raw = iv[commentCol];\n const comment = (raw != null && String(raw).trim() !== '' && String(raw) !== 'null')\n ? String(raw).trim()\n : '';\n return { from: Number(iv[fromCol]), to: Number(iv[toCol]), comment };\n })\n .sort((a, b) => a.from - b.from);\n\n if (!records.length) {\n return { data: [], layout: {} };\n }\n\n const shapes = [];\n const textXs = [];\n const textYs = [];\n const texts = [];\n const hovers = [];\n\n for (const rec of records) {\n const mid = 0.5 * (rec.from + rec.to);\n const hasComment = !!rec.comment;\n\n shapes.push({\n type: 'rect',\n xref: 'x', yref: 'y',\n x0: 0, x1: 1,\n y0: rec.from, y1: rec.to,\n fillcolor: hasComment ? bgColor : 'rgba(0,0,0,0)',\n line: { color: borderColor, width: 1 },\n layer: 'below',\n });\n\n if (hasComment) {\n textXs.push(0.5);\n textYs.push(mid);\n texts.push(wrapComment(rec.comment, charsPerLine));\n hovers.push(`${rec.from.toFixed(3)}–${rec.to.toFixed(3)} m<br>${wrapComment(rec.comment, 40)}`);\n }\n }\n\n const data = textXs.length ? [{\n type: 'scatter',\n x: textXs,\n y: textYs,\n mode: 'text',\n text: texts,\n textposition: 'middle center',\n textfont: { color: textColor, size: 10 },\n hovertext: hovers,\n hoverinfo: 'text',\n showlegend: false,\n }] : [];\n\n const layout = {\n shapes,\n height: 400,\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed' },\n showlegend: false,\n template: template !== undefined ? template : BASELODE_TEMPLATE,\n };\n\n return { data, layout: applyStriplogLayoutDefaults(layout) };\n}\n\n/**\n * Compute 2D map strike/dip symbol geometry for a single structural measurement.\n *\n * Returns the geometry needed to draw a strike line and dip tick on a map.\n *\n * @param {Object} point - Structural measurement with x, y, dip, azimuth\n * @param {Object} opts\n * @param {number} [opts.symbolSize=10] - Strike line half-length in map units\n * @param {string} [opts.xCol='easting'] - X coordinate column\n * @param {string} [opts.yCol='northing'] - Y coordinate column\n * @returns {{ strike: number, dipValue: number, x: number, y: number,\n * strikeX0: number, strikeY0: number, strikeX1: number, strikeY1: number,\n * tickX1: number, tickY1: number } | null}\n */\nexport function buildStrikeDipSymbol(point, {\n symbolSize = 10,\n xCol = 'easting',\n yCol = 'northing',\n} = {}) {\n const x = point[xCol] != null ? Number(point[xCol]) : null;\n const y = point[yCol] != null ? Number(point[yCol]) : null;\n const dip = point[DIP] != null ? Number(point[DIP]) : null;\n const az = point[AZIMUTH] != null ? Number(point[AZIMUTH]) : null;\n\n if (x === null || y === null || dip === null || az === null) return null;\n\n const strike = ((az - 90) + 360) % 360;\n const strikeRad = (strike * Math.PI) / 180;\n const azRad = (az * Math.PI) / 180;\n\n // Strike line half-endpoints\n const dxS = symbolSize * Math.sin(strikeRad);\n const dyS = symbolSize * Math.cos(strikeRad);\n\n // Dip tick from center (length scaled by dip magnitude)\n const tickLen = symbolSize * 0.4 * (dip / 90);\n const dxD = tickLen * Math.sin(azRad);\n const dyD = tickLen * Math.cos(azRad);\n\n return {\n strike,\n dipValue: dip,\n x,\n y,\n strikeX0: x - dxS,\n strikeY0: y - dyS,\n strikeX1: x + dxS,\n strikeY1: y + dyS,\n tickX1: x + dxD,\n tickY1: y + dyD,\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useRef, useState } from 'react';\nimport Plotly from 'plotly.js-dist-min';\nimport { buildPlotConfig } from './drillholeViz.js';\nimport { buildCommentsConfig, buildTadpoleConfig } from './structuralViz.js';\nimport { getChartOptions, DISPLAY_COMMENT, DISPLAY_CATEGORICAL, DISPLAY_NUMERIC, DISPLAY_TADPOLE } from '../data/columnMeta.js';\n\nconst DEFAULT_NUMERIC_CHART_TYPE = 'markers+line';\n\n/**\n * Resolve chart type from available options.\n * @private\n */\nfunction resolveChartType(displayType, requestedChartType) {\n const chartOptions = getChartOptions(displayType);\n if (chartOptions.some((opt) => opt.value === requestedChartType)) return requestedChartType;\n return chartOptions[0]?.value || DEFAULT_NUMERIC_CHART_TYPE;\n}\n\n/**\n * Plotly-based trace plot component for drillhole data.\n * Renders 1D strip logs with chart type options driven by column display type.\n *\n * @param {Object} props\n * @param {Object} props.config - Plot configuration {holeId, property, chartType}\n * @param {Object} props.graph - Graph data {hole, points, displayType, isCategorical, isComment, loading}\n * @param {Array} props.holeOptions - Available holes for dropdown\n * @param {Array} props.propertyOptions - Available properties for dropdown\n * @param {Function} props.onConfigChange - Handler for configuration changes\n * @param {Object} [props.template] - Plotly template to apply. Defaults to the Baselode template.\n * @returns {JSX.Element}\n */\nfunction TracePlot({ config, graph, holeOptions = [], propertyOptions = [], onConfigChange, template }) {\n const containerRef = useRef(null);\n const hole = graph?.hole;\n const points = graph?.points || [];\n const property = config?.property || '';\n const chartType = config?.chartType || DEFAULT_NUMERIC_CHART_TYPE;\n const selectedHoleId = config?.holeId || '';\n\n // Derive display type from graph metadata (set by useDrillholeTraceGrid)\n const displayType = graph?.displayType\n || (graph?.isComment ? DISPLAY_COMMENT : (graph?.isCategorical ? DISPLAY_CATEGORICAL : DISPLAY_NUMERIC));\n\n const chartOptions = getChartOptions(displayType);\n const effectiveChartType = resolveChartType(displayType, chartType);\n\n const [renderError, setRenderError] = useState('');\n\n useEffect(() => {\n const isComment = displayType === DISPLAY_COMMENT;\n const isTadpole = displayType === DISPLAY_TADPOLE;\n if (!hole || !property) return;\n // For comment type, allow empty points (empty intervals still draw border boxes)\n // For tadpole, points are raw hole points — allow empty array through so buildTadpoleConfig can return empty gracefully\n if (!isComment && !isTadpole && points.length === 0) return;\n const target = containerRef.current;\n if (!target) return;\n\n let plotData;\n try {\n if (isComment) {\n plotData = buildCommentsConfig(points, { commentCol: property, fromCol: 'from', toCol: 'to' });\n } else if (isTadpole) {\n plotData = buildTadpoleConfig(points);\n } else {\n plotData = buildPlotConfig({\n points,\n isCategorical: displayType === DISPLAY_CATEGORICAL,\n property,\n chartType: effectiveChartType,\n template,\n });\n }\n } catch (err) {\n console.error('Plot build error', err);\n setRenderError(err?.message || 'Plot build error');\n return;\n }\n\n if (!plotData?.data || plotData.data.length === 0) {\n if (!isComment) return;\n }\n\n const plotConfig = {\n displayModeBar: true,\n responsive: true,\n useResizeHandler: true,\n modeBarButtonsToRemove: ['select2d', 'lasso2d', 'autoScale2d']\n };\n\n try {\n setRenderError('');\n Plotly.react(target, plotData.data, plotData.layout, plotConfig);\n requestAnimationFrame(() => {\n if (target && target.parentElement) {\n Plotly.Plots.resize(target);\n }\n });\n } catch (err) {\n console.error('Plot render error', err);\n setRenderError(err?.message || 'Plot render error');\n }\n\n return () => {\n if (target) {\n try {\n Plotly.purge(target);\n } catch (err) {\n console.warn('Plot purge error', err);\n }\n }\n };\n }, [hole, property, effectiveChartType, displayType, points, template]);\n\n useEffect(() => {\n const target = containerRef.current;\n if (!target || typeof ResizeObserver === 'undefined') return undefined;\n const resizeObserver = new ResizeObserver(() => {\n try {\n if (target && target.data) {\n Plotly.Plots.resize(target);\n }\n } catch (err) {\n console.warn('Plot resize error', err);\n }\n });\n resizeObserver.observe(target);\n return () => resizeObserver.disconnect();\n }, []);\n\n if (!hole || !property) {\n return (\n <div className=\"plot-card empty\">\n <div className=\"placeholder\">{config?.holeId ? (graph?.loading ? `Loading ${config.holeId}...` : 'Select a property') : 'Loading demo data...'}</div>\n </div>\n );\n }\n\n if (displayType !== DISPLAY_COMMENT && displayType !== DISPLAY_TADPOLE && points.length === 0) {\n return (\n <div className=\"plot-card empty\">\n <div className=\"placeholder\">No data</div>\n </div>\n );\n }\n\n if (renderError) {\n return (\n <div className=\"plot-card empty\">\n <div className=\"placeholder\">Plot error: {renderError}</div>\n </div>\n );\n }\n\n return (\n <div className=\"plot-card\">\n <div className=\"plot-title\">\n <select\n className=\"plot-select\"\n value={selectedHoleId}\n onChange={(e) => onConfigChange && onConfigChange({ holeId: e.target.value })}\n >\n {holeOptions.map((h) => {\n const holeId = typeof h === 'string' ? h : h.holeId;\n const label = typeof h === 'string' ? h : h.label || h.holeId;\n return (\n <option key={holeId} value={holeId}>{label}</option>\n );\n })}\n </select>\n </div>\n <div className=\"plot-controls column\">\n {propertyOptions.length > 0 && (\n <select\n className=\"plot-select\"\n value={property}\n onChange={(e) => onConfigChange && onConfigChange({ property: e.target.value })}\n >\n {propertyOptions.map((p) => (\n <option key={p} value={p}>{p}</option>\n ))}\n </select>\n )}\n {chartOptions.length > 1 && (\n <select\n className=\"plot-select\"\n value={effectiveChartType}\n onChange={(e) => onConfigChange && onConfigChange({ chartType: e.target.value })}\n >\n {chartOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n )}\n </div>\n <div className=\"plotly-chart\" ref={containerRef} />\n </div>\n );\n}\n\nexport default TracePlot;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n deriveAssayProps,\n loadAssayHole,\n loadAssayMetadata\n} from '../data/assayDataLoader.js';\nimport {\n buildTraceConfigsForHoleIds,\n coerceChartTypeForProperty,\n reorderHoleIds\n} from '../data/traceGridConfig.js';\nimport { buildIntervalPoints, holeHasData } from './drillholeViz.js';\n\n/**\n * Merge two arrays of hole objects by holeId.\n * For holes that appear in both arrays, their points are concatenated.\n * @private\n */\nfunction mergeHoleSets(primary, extra) {\n if (!extra?.length) return primary;\n const byId = new Map(primary.map((h) => [h.id || h.holeId, { ...h }]));\n for (const eh of extra) {\n const id = eh.id || eh.holeId;\n if (!id) continue;\n if (byId.has(id)) {\n const existing = byId.get(id);\n byId.set(id, { ...existing, points: [...(existing.points || []), ...(eh.points || [])] });\n } else {\n byId.set(id, eh);\n }\n }\n return Array.from(byId.values());\n}\n\n/**\n * Build comment-type interval points from a hole.\n * Unlike buildIntervalPoints, this keeps intervals with empty/null comment values\n * so that buildCommentsConfig can draw the interval border rectangles.\n * @private\n */\nfunction buildCommentPoints(hole, property) {\n if (!hole || !property) return [];\n const seen = new Set();\n const out = [];\n for (const p of hole.points || []) {\n const from = Number(p.from ?? p.samp_from ?? p.depth_from ?? p.from_depth);\n const to = Number(p.to ?? p.samp_to ?? p.depth_to ?? p.to_depth);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;\n const key = `${from}-${to}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ from, to, [property]: p[property] ?? '' });\n }\n return out;\n}\n\n/**\n * React hook for managing a grid of drillhole trace plots.\n * Handles loading assay data, optional extra hole data (e.g. structural intervals),\n * column metadata classification, and trace config coordination.\n *\n * @param {Object} options\n * @param {string} options.initialFocusedHoleId - Initial focused hole ID\n * @param {File|Blob|null} options.sourceFile - Assay data CSV file\n * @param {Array<Object>} options.extraHoles - Pre-parsed extra hole data (e.g. structural)\n * @param {number} options.plotCount - Number of plots in grid (default: 4)\n * @returns {Object} Hook state and actions\n */\nexport default function useDrillholeTraceGrid({\n initialFocusedHoleId = '',\n sourceFile = null,\n extraHoles = [],\n plotCount = 4\n} = {}) {\n const [holes, setHoles] = useState([]);\n const [holeIds, setHoleIds] = useState([]);\n const [numericProps, setNumericProps] = useState([]);\n const [categoricalProps, setCategoricalProps] = useState([]);\n const [commentProps, setCommentProps] = useState([]);\n const [columnMeta, setColumnMeta] = useState({});\n const [defaultProp, setDefaultProp] = useState('');\n const [traceConfigs, setTraceConfigs] = useState([]);\n const [error, setError] = useState('');\n const [focusedHoleId, setFocusedHoleId] = useState(initialFocusedHoleId || '');\n const [loadingHoles, setLoadingHoles] = useState([]);\n const loadedSourceFileRef = useRef(null);\n\n const applyAssayState = (state) => {\n if (!state) return;\n setError('');\n setHoles(state.holes || []);\n setHoleIds((state.holes || []).map((h) => ({\n holeId: h.id || h.holeId\n })).filter((h) => h.holeId));\n setNumericProps(state.numericProps || []);\n setCategoricalProps(state.categoricalProps || []);\n setCommentProps(state.commentProps || []);\n setColumnMeta(state.columnMeta || {});\n setDefaultProp(state.defaultProp || '');\n setTraceConfigs(state.traceConfigs || []);\n };\n\n // Load metadata (hole IDs) from assay CSV on first mount\n useEffect(() => {\n if (!sourceFile || loadedSourceFileRef.current === sourceFile) return;\n loadedSourceFileRef.current = sourceFile;\n loadAssayMetadata(sourceFile)\n .then((ids) => {\n if (!ids) return;\n const uniqueIds = Array.from(new Map(ids.map((h) => [h.holeId, h])).values());\n setHoleIds(uniqueIds);\n setTraceConfigs(buildTraceConfigsForHoleIds({\n holeIds: uniqueIds.map((h) => h.holeId),\n focusedHoleId,\n plotCount,\n defaultProp: '',\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n }));\n })\n .catch((err) => {\n console.info('Assay metadata load skipped:', err.message);\n });\n }, [sourceFile, focusedHoleId, plotCount, categoricalProps, commentProps]);\n\n // Inject extra holes (structural etc.) into holeIds — always, regardless of whether\n // an assay sourceFile is also present. This ensures structural-only holes appear in\n // the dropdown even when assay data is loaded from a separate file.\n useEffect(() => {\n if (!extraHoles?.length) return;\n const ids = extraHoles\n .map((h) => ({ holeId: h.id || h.holeId }))\n .filter((h) => h.holeId);\n setHoleIds((prev) => {\n const existing = new Set(prev.map((h) => h.holeId));\n const newIds = ids.filter((h) => !existing.has(h.holeId));\n return newIds.length ? [...prev, ...newIds] : prev;\n });\n }, [extraHoles]);\n\n useEffect(() => {\n setError((prev) => (prev && prev.startsWith('Loading ') && prev.includes(' for hole ') ? prev : ''));\n }, [traceConfigs]);\n\n useEffect(() => {\n if (!holeIds.length) {\n setTraceConfigs([]);\n return;\n }\n const orderedHoleIds = reorderHoleIds(holeIds.map((h) => h.holeId), focusedHoleId);\n setTraceConfigs((prev) => {\n const next = Array.from({ length: plotCount }).map((_, idx) => {\n const existing = prev[idx] || {};\n const holeId = holeIds.some((h) => h.holeId === existing.holeId) ? existing.holeId : orderedHoleIds[idx] || holeIds[idx]?.holeId || '';\n const property = existing.property || defaultProp;\n const chartType = coerceChartTypeForProperty({\n property,\n chartType: existing.chartType,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n });\n return { holeId, property, chartType };\n });\n return next;\n });\n }, [holeIds, focusedHoleId, defaultProp, categoricalProps, commentProps, plotCount]);\n\n // Load assay hole data on demand as trace configs change\n useEffect(() => {\n if (!sourceFile) return;\n const needed = traceConfigs.map((cfg) => cfg.holeId).filter(Boolean);\n needed.forEach((holeId) => {\n const already = holes.some((h) => (h.id || h.holeId) === holeId);\n const loading = loadingHoles.includes(holeId);\n if (already || loading) return;\n setLoadingHoles((prev) => [...prev, holeId]);\n loadAssayHole(sourceFile, holeId)\n .then((hole) => {\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n if (!hole) return;\n setHoles((prev) => {\n const merged = mergeHoleSets(\n [...prev.filter((h) => (h.id || h.holeId) !== holeId), hole],\n extraHoles\n );\n const props = deriveAssayProps(merged);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) {\n setDefaultProp(props.defaultProp);\n setTraceConfigs((configs) => configs.map((cfg) => ({\n ...cfg,\n property: cfg.property || props.defaultProp,\n chartType: coerceChartTypeForProperty({\n property: cfg.property || props.defaultProp,\n chartType: cfg.chartType,\n categoricalProps: props.categoricalProps,\n commentProps: props.commentProps,\n numericDefaultChartType: 'markers+line'\n })\n })));\n }\n return merged;\n });\n })\n .catch((err) => {\n console.error(err);\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n setError(err.message || `Error loading hole ${holeId}`);\n });\n });\n }, [traceConfigs, sourceFile, holes, loadingHoles, defaultProp, extraHoles]);\n\n // Merge extra holes whenever they change (and assay holes are present)\n useEffect(() => {\n if (!extraHoles?.length) return;\n setHoles((prev) => {\n if (!prev.length) {\n // No assay data yet — seed from extra holes only\n const props = deriveAssayProps(extraHoles);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) setDefaultProp(props.defaultProp);\n return extraHoles;\n }\n const merged = mergeHoleSets(prev, extraHoles);\n const props = deriveAssayProps(merged);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n setCommentProps(props.commentProps);\n setColumnMeta(props.columnMeta);\n if (!defaultProp && props.defaultProp) setDefaultProp(props.defaultProp);\n return merged;\n });\n }, [extraHoles]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const propertyOptions = useMemo(\n () => [...numericProps, ...categoricalProps, ...commentProps],\n [numericProps, categoricalProps, commentProps]\n );\n\n const labeledHoleOptions = useMemo(\n () => holeIds\n .map((h) => ({ holeId: h.holeId, label: h.holeId }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n [holeIds]\n );\n\n const traceGraphs = useMemo(() => {\n const allProps = [...numericProps, ...categoricalProps, ...commentProps];\n return Array.from({ length: plotCount }).map((_, idx) => {\n const cfg = traceConfigs[idx] || {};\n const hole = holes.find((h) => (h.id || h.holeId) === cfg.holeId) || null;\n\n // Per-hole property list: only columns this hole actually has data for\n const holePropertyOptions = hole\n ? allProps.filter((p) => holeHasData(hole, p))\n : allProps;\n\n let property = cfg.property || defaultProp;\n // Auto-select first available property if the configured one has no data for this hole\n if (hole && !holePropertyOptions.includes(property)) {\n property = holePropertyOptions[0] || property;\n }\n\n const isComment = commentProps.includes(property);\n const isCategorical = !isComment && categoricalProps.includes(property);\n const isTadpole = !isComment && !isCategorical && property === 'dip';\n const displayType = isComment ? 'comment' : isTadpole ? 'tadpole' : (isCategorical ? 'categorical' : 'numeric');\n\n const chartType = isTadpole ? 'tadpole' : cfg.chartType || (isComment ? 'comment' : (isCategorical ? 'categorical' : 'markers+line'));\n const holeId = cfg.holeId || hole?.id || hole?.holeId || '';\n\n const points = isTadpole\n ? (hole?.points || [])\n : isComment\n ? buildCommentPoints(hole, property)\n : buildIntervalPoints(hole, property, isCategorical);\n\n return {\n config: { holeId, property, chartType },\n hole,\n loading: loadingHoles.includes(cfg.holeId),\n isCategorical,\n isComment,\n isTadpole,\n displayType,\n points,\n propertyOptions: holePropertyOptions,\n label: holeId\n };\n });\n }, [traceConfigs, holes, defaultProp, categoricalProps, commentProps, loadingHoles, plotCount, numericProps]);\n\n const handleConfigChange = (index, patch) => {\n setTraceConfigs((prev) => {\n const next = [...prev];\n const base = next[index] || {};\n const merged = { ...base, ...patch };\n if (patch.property) {\n merged.chartType = coerceChartTypeForProperty({\n property: patch.property,\n chartType: merged.chartType,\n categoricalProps,\n commentProps,\n numericDefaultChartType: 'markers+line'\n });\n }\n next[index] = merged;\n return next;\n });\n };\n\n return {\n error,\n focusedHoleId,\n setFocusedHoleId,\n setError,\n holeCount: holeIds.length,\n numericProps,\n categoricalProps,\n commentProps,\n columnMeta,\n propertyOptions,\n labeledHoleOptions,\n traceGraphs,\n handleConfigChange\n };\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/** Default 10-color palette for assay visualization (blue to red gradient) */\nexport const ASSAY_COLOR_PALETTE_10 = [\n '#313695',\n '#4575b4',\n '#74add1',\n '#abd9e9',\n '#e0f3f8',\n '#fee090',\n '#fdae61',\n '#f46d43',\n '#d73027',\n '#a50026'\n];\n\n/**\n * Build an equal-range color scale from numeric values\n * Uses percentile-based binning for better distribution with outliers\n * @param {Array<number>} values - Array of numeric values to analyze\n * @param {Array<string>} colors - Array of color hex strings to use for bins\n * @returns {{min: number|null, max: number|null, step: number|null, bins: Array<{index: number, min: number, max: number, label: string}>, colors: Array<string>}} Color scale object\n */\nexport function buildEqualRangeColorScale(values = [], colors = ASSAY_COLOR_PALETTE_10) {\n // Filter to finite values and sort\n const finiteValues = values.filter((v) => Number.isFinite(v));\n \n if (!finiteValues.length) {\n return {\n min: null,\n max: null,\n step: null,\n bins: [],\n colors\n };\n }\n\n const sorted = finiteValues.slice().sort((a, b) => a - b);\n const min = sorted[0];\n const max = sorted[sorted.length - 1];\n const binCount = colors.length;\n\n if (max === min) {\n const bins = colors.map((_, index) => ({\n index,\n min,\n max,\n label: `${min}`\n }));\n return {\n min,\n max,\n step: 0,\n bins,\n colors\n };\n }\n\n // Use percentile-based binning for better distribution\n const bins = colors.map((_, index) => {\n const percentileLow = index / binCount;\n const percentileHigh = (index + 1) / binCount;\n const idxLow = Math.floor(percentileLow * sorted.length);\n const idxHigh = Math.min(sorted.length - 1, Math.floor(percentileHigh * sorted.length));\n const lower = sorted[idxLow];\n const upper = index === binCount - 1 ? max : sorted[idxHigh];\n \n return {\n index,\n min: lower,\n max: upper,\n label: formatBinLabel(lower, upper)\n };\n });\n\n const step = (max - min) / binCount;\n\n return {\n min,\n max,\n step,\n bins,\n colors\n };\n}\n\n/**\n * Format bin label with appropriate precision\n * @private\n */\nfunction formatBinLabel(min, max) {\n const formatVal = (v) => {\n if (!Number.isFinite(v)) return 'n/a';\n if (Math.abs(v) >= 1000) return v.toFixed(0);\n if (Math.abs(v) >= 10) return v.toFixed(1);\n if (Math.abs(v) >= 0.1) return v.toFixed(2);\n return v.toFixed(3);\n };\n return `${formatVal(min)} – ${formatVal(max)}`;\n}\n\n/**\n * Get the bin index for a value in a color scale\n * @param {number} value - Value to find bin for\n * @param {Object} scale - Color scale object from buildEqualRangeColorScale\n * @returns {number} Bin index (0 to bins.length-1) or -1 if invalid\n */\nexport function getEqualRangeBinIndex(value, scale) {\n if (!Number.isFinite(value) || !scale || !Array.isArray(scale.bins) || !scale.bins.length) {\n return -1;\n }\n\n if (scale.max === scale.min) {\n return value === scale.min ? 0 : -1;\n }\n\n // Find the bin that contains this value\n for (let i = 0; i < scale.bins.length; i += 1) {\n const bin = scale.bins[i];\n if (value >= bin.min && (value <= bin.max || i === scale.bins.length - 1)) {\n return i;\n }\n }\n\n // Value is out of range\n return -1;\n}\n\n/**\n * Get the color for a value using an equal-range color scale\n * @param {number} value - Value to get color for\n * @param {Object} scale - Color scale object from buildEqualRangeColorScale\n * @param {string} fallbackColor - Color to use if value is invalid or out of range\n * @returns {string} Color hex string\n */\nexport function getEqualRangeColor(value, scale, fallbackColor = '#8b1e3f') {\n const index = getEqualRangeBinIndex(value, scale);\n if (index < 0) return fallbackColor;\n return scale.colors[index] || fallbackColor;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n * @param {*} rows - Value to convert to array\n * @returns {Array} Array\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n * @param {*} value - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Normalize a point to ensure x, y, z are finite numbers\n * @private\n * @param {Object} point - Point object\n * @returns {Object} Normalized point\n */\nfunction normalizePoint(point = {}) {\n return {\n ...point,\n x: toNumber(point.x),\n y: toNumber(point.y),\n z: toNumber(point.z)\n };\n}\n\n/**\n * Project 3D trace points onto a 2D cross-section plane\n * @param {Array<Object>} traces - Array of trace points with x, y, z coordinates\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @returns {Array<Object>} Trace points with added 'along' and 'across' section coordinates\n */\nexport function projectTraceToSection(traces = [], origin = [0, 0], azimuth = 0) {\n const [ox, oy] = origin;\n const azRad = (Number(azimuth) * Math.PI) / 180;\n const cosA = Math.cos(azRad);\n const sinA = Math.sin(azRad);\n\n return toArray(traces)\n .map(normalizePoint)\n .map((row) => {\n if (!Number.isFinite(row.x) || !Number.isFinite(row.y)) return { ...row };\n const dx = row.x - ox;\n const dy = row.y - oy;\n return {\n ...row,\n along: (dx * sinA) + (dy * cosA),\n across: (dx * cosA) - (dy * sinA)\n };\n });\n}\n\n/**\n * Filter trace points to those within a width window around a section line\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width (points within ±width/2 are included)\n * @returns {Array<Object>} Filtered trace points within section window\n */\nexport function sectionWindow(traces = [], origin = [0, 0], azimuth = 0, width = 50) {\n const projected = projectTraceToSection(traces, origin, azimuth);\n const half = 0.5 * Number(width || 0);\n if (!Number.isFinite(half) || half <= 0) return projected;\n return projected.filter((row) => Number.isFinite(row.across) && Math.abs(row.across) <= half);\n}\n\n/**\n * Generate plan view of traces, optionally filtered by depth slice\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>|null} depthSlice - Optional [top, bottom] depth range to filter\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Trace points for plan view\n */\nexport function planView(traces = [], depthSlice = null, colorBy = null) {\n let rows = toArray(traces).map(normalizePoint);\n if (Array.isArray(depthSlice) && depthSlice.length === 2) {\n const [top, bottom] = depthSlice;\n rows = rows.filter((row) => Number.isFinite(row.z) && row.z <= Number(top) && row.z >= Number(bottom));\n }\n if (colorBy) {\n rows = rows.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return rows;\n}\n\n/**\n * Generate cross-section view of traces within a window\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Section trace points with along/across coordinates\n */\nexport function sectionView(traces = [], origin = [0, 0], azimuth = 0, width = 50, colorBy = null) {\n let section = sectionWindow(traces, origin, azimuth, width);\n if (colorBy) {\n section = section.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return section;\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Extract hole ID from a row using common field name variations\n * @private\n */\nfunction getHoleId(row = {}) {\n return row.hole_id ?? row.holeId ?? row.id;\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Convert trace points to line segments grouped by hole ID\n * @param {Array<Object>} traces - Array of trace points with x, y, z, md coordinates\n * @param {string|null} colorBy - Optional property name for per-point color values\n * @returns {Array<{hole_id: string, x: Array<number>, y: Array<number>, z: Array<number>, color: Array|null}>} Array of line segments\n */\nexport function tracesAsSegments(traces = [], colorBy = null) {\n const grouped = new Map();\n\n toArray(traces).forEach((row) => {\n const holeId = getHoleId(row);\n if (holeId === undefined || holeId === null || `${holeId}`.trim() === '') return;\n const key = `${holeId}`;\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(row);\n });\n\n const segments = [];\n grouped.forEach((rows, holeId) => {\n const sorted = [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0));\n const payload = {\n hole_id: holeId,\n x: sorted.map((row) => toNumber(row.x, 0)),\n y: sorted.map((row) => toNumber(row.y, 0)),\n z: sorted.map((row) => toNumber(row.z, 0)),\n color: null\n };\n if (colorBy) {\n payload.color = sorted.map((row) => row?.[colorBy]);\n }\n segments.push(payload);\n });\n\n return segments;\n}\n\n/**\n * Convert interval data to tube/cylinder representations for 3D rendering\n * @param {Array<Object>} intervals - Array of interval objects with from/to depths and x, y, z coordinates\n * @param {number} radius - Tube radius\n * @param {string|null} colorBy - Optional property name for color value\n * @returns {Array<{hole_id: string, x: number, y: number, z: number, from: number, to: number, radius: number, color_value: *}>} Array of tube specifications\n */\nexport function intervalsAsTubes(intervals = [], radius = 1, colorBy = null) {\n return toArray(intervals).map((row) => ({\n hole_id: getHoleId(row),\n from: row?.from,\n to: row?.to,\n radius,\n color: colorBy ? row?.[colorBy] : null,\n value: colorBy ? row?.[colorBy] : null\n }));\n}\n\n/**\n * Extract annotation labels from interval data for 3D text display\n * @param {Array<Object>} intervals - Array of interval objects\n * @param {string|null} labelCol - Property name to use for label text\n * @returns {Array<{hole_id: string, label: string, depth: number}>} Array of annotations with computed mid-depth\n */\nexport function annotationsFromIntervals(intervals = [], labelCol = null) {\n if (!labelCol) return [];\n return toArray(intervals)\n .filter((row) => Object.prototype.hasOwnProperty.call(row || {}, labelCol))\n .map((row) => ({\n hole_id: getHoleId(row),\n label: row?.[labelCol],\n depth: 0.5 * ((toNumber(row?.from, 0)) + (toNumber(row?.to, 0)))\n }));\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Rebuild `sceneCtx.selectables` from all renderable mesh lists.\n * Called after any data-type module adds or removes objects from the scene.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function syncSelectables(sceneCtx) {\n sceneCtx.selectables = [\n ...sceneCtx.blocks,\n ...sceneCtx.drillMeshes,\n ...sceneCtx.structuralMeshes,\n ];\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { AZIMUTH, DIP } from '../data/datamodel.js';\nimport { computeStructuralPositions } from '../data/structuralPositions.js';\nimport { syncSelectables } from './sceneSelectables.js';\n\nconst DEFAULT_COLOR_MAP = {\n bedding: '#2563eb',\n foliation: '#16a34a',\n joint: '#9333ea',\n fault: '#dc2626',\n vein: '#f59e0b',\n 'shear zone': '#0ea5e9',\n 'fault zone': '#ef4444',\n};\n\n/**\n * Resolve a color string for a structure type from a color map.\n * @private\n * @param {string|null} structureType\n * @param {Object|null} colorMap\n * @returns {number} THREE.js hex color integer\n */\nfunction resolveColor(structureType, colorMap) {\n const map = colorMap || DEFAULT_COLOR_MAP;\n const key = (structureType || '').toLowerCase().trim();\n const hex = map[key] || '#888888';\n return new THREE.Color(hex).getHex();\n}\n\n/**\n * Compute a disc plane normal vector from dip and azimuth in ENU coordinates.\n *\n * Convention: azimuth is clockwise from North, dip is measured from horizontal.\n * The normal points upward (positive Z in elevation-positive convention).\n *\n * @param {number} dip - Dip angle in degrees [0, 90]\n * @param {number} azimuth - Dip direction azimuth in degrees [0, 360)\n * @returns {THREE.Vector3} Unit normal vector in ENU coordinates\n */\nexport function dipAzimuthToNormal(dip, azimuth) {\n const dipRad = (dip * Math.PI) / 180;\n const azRad = (azimuth * Math.PI) / 180;\n return new THREE.Vector3(\n Math.sin(azRad) * Math.sin(dipRad), // East component\n Math.cos(azRad) * Math.sin(dipRad), // North component\n Math.cos(dipRad) // Up component\n ).normalize();\n}\n\n/**\n * Build Three.js disc meshes for structural measurements.\n *\n * Each structural measurement with valid 3D coordinates is rendered as a\n * thin cylinder (disc) oriented perpendicular to the plane normal derived\n * from the dip/azimuth values.\n *\n * userData is set on each mesh for hover/picking support.\n *\n * @param {Array<Object>} structures - Each row must have x/easting, y/northing, z/elevation,\n * dip, and azimuth fields.\n * @param {Object} opts\n * @param {number} [opts.radius=5] - Disc radius in scene units\n * @param {number} [opts.discThickness=0.2] - Disc thickness in scene units\n * @param {number} [opts.opacity=0.7] - Material opacity [0, 1]\n * @param {number} [opts.segments=32] - Cylinder radial segments (higher = smoother)\n * @param {Object|null} [opts.colorMap] - Map from defect string to hex color string\n * @returns {THREE.Group} Group containing one Mesh per valid measurement\n */\nexport function buildStructuralDiscs(structures, opts = {}) {\n const {\n radius = 5,\n discThickness = 0.2,\n opacity = 0.7,\n segments = 32,\n colorMap = null,\n } = opts;\n\n const group = new THREE.Group();\n const yAxis = new THREE.Vector3(0, 1, 0);\n\n for (const s of structures) {\n const xVal = s.x != null ? s.x : (s.easting != null ? s.easting : null);\n const yVal = s.y != null ? s.y : (s.northing != null ? s.northing : null);\n const zVal = s.z != null ? s.z : (s.elevation != null ? s.elevation : null);\n\n if (xVal == null || yVal == null || zVal == null) continue;\n if (!Number.isFinite(xVal) || !Number.isFinite(yVal) || !Number.isFinite(zVal)) continue;\n\n const dipVal = s[DIP] != null ? Number(s[DIP]) : null;\n const azVal = s[AZIMUTH] != null ? Number(s[AZIMUTH]) : null;\n\n let normal;\n if (s.nx != null && Number.isFinite(s.nx) && s.ny != null && Number.isFinite(s.ny) && s.nz != null && Number.isFinite(s.nz)) {\n normal = new THREE.Vector3(s.nx, s.ny, s.nz).normalize();\n } else {\n if (dipVal == null || azVal == null || !Number.isFinite(dipVal) || !Number.isFinite(azVal)) continue;\n normal = dipAzimuthToNormal(dipVal, azVal);\n }\n\n const geom = new THREE.CylinderGeometry(radius, radius, discThickness, segments, 1, false);\n const mat = new THREE.MeshStandardMaterial({\n color: resolveColor(s['structure_type'], colorMap),\n transparent: true,\n opacity,\n side: THREE.DoubleSide,\n });\n\n const mesh = new THREE.Mesh(geom, mat);\n mesh.position.set(xVal, yVal, zVal);\n\n // CylinderGeometry default axis is Y; rotate so Y aligns with normal\n mesh.quaternion.setFromUnitVectors(yAxis, normal);\n\n mesh.userData = {\n type: 'structure',\n hole_id: s.hole_id,\n depth: s.depth ?? s.mid,\n structure_type: s['structure_type'],\n dip: dipVal,\n azimuth: azVal,\n comments: s.comments,\n };\n\n group.add(mesh);\n }\n\n return group;\n}\n\n// ---------------------------------------------------------------------------\n// Scene-level functions (operate on a Baselode3DScene instance)\n// ---------------------------------------------------------------------------\n\n/**\n * Compute positions for structural measurements and add disc meshes to the scene.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {Array} structures - Structural measurement rows\n * @param {Array} holes - Desurveyed hole objects with `points`\n * @param {object} [opts]\n * @param {number} [opts.maxDiscs=3000] - Cap on rendered discs for performance\n */\nexport function setStructuralDiscs(sceneCtx, structures, holes, opts = {}) {\n if (!sceneCtx.scene) return;\n clearStructuralDiscs(sceneCtx);\n if (!structures?.length || !holes?.length) return;\n\n const { maxDiscs = 3000 } = opts;\n let input = structures;\n if (input.length > maxDiscs) {\n const step = input.length / maxDiscs;\n const sampled = [];\n for (let i = 0; i < maxDiscs; i++) {\n sampled.push(input[Math.floor(i * step)]);\n }\n input = sampled;\n }\n\n const traceRows = holes.flatMap(h => (h.points || []).map(p => ({ ...p, hole_id: h.id })));\n const enriched = computeStructuralPositions(input, traceRows, opts);\n if (!enriched.length) return;\n\n sceneCtx.structuralGroup = buildStructuralDiscs(enriched, opts);\n sceneCtx.scene.add(sceneCtx.structuralGroup);\n sceneCtx.structuralGroup.traverse(child => {\n if (child.isMesh) sceneCtx.structuralMeshes.push(child);\n });\n syncSelectables(sceneCtx);\n}\n\n/**\n * Remove all structural disc meshes from the scene and dispose GPU resources.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function clearStructuralDiscs(sceneCtx) {\n if (sceneCtx.structuralGroup) {\n sceneCtx.scene.remove(sceneCtx.structuralGroup);\n sceneCtx.structuralGroup.traverse(child => {\n if (child.isMesh) {\n child.geometry.dispose();\n child.material.dispose();\n }\n });\n sceneCtx.structuralGroup = null;\n }\n sceneCtx.structuralMeshes = [];\n syncSelectables(sceneCtx);\n}\n\n/**\n * Show or hide the structural discs group.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {boolean} visible\n */\nexport function setStructuralDiscsVisible(sceneCtx, visible) {\n if (sceneCtx.structuralGroup) {\n sceneCtx.structuralGroup.visible = Boolean(visible);\n }\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\n\nlet _overlayIdCounter = 0;\n\n/**\n * Normalize caller-supplied bounds to the canonical { minX, minY, maxX, maxY } form.\n * Accepts either { minX, minY, maxX, maxY } or { x, y, width, height }.\n *\n * @param {object} bounds\n * @returns {{ minX: number, minY: number, maxX: number, maxY: number }}\n * @throws {Error} if the resulting width or height is zero or negative\n */\nexport function normalizeBounds(bounds) {\n let minX, minY, maxX, maxY;\n\n if ('width' in bounds || 'height' in bounds || ('x' in bounds && !('maxX' in bounds))) {\n const x = Number(bounds.x ?? 0);\n const y = Number(bounds.y ?? 0);\n const width = Number(bounds.width ?? 0);\n const height = Number(bounds.height ?? 0);\n minX = x;\n minY = y;\n maxX = x + width;\n maxY = y + height;\n } else {\n minX = Number(bounds.minX);\n minY = Number(bounds.minY);\n maxX = Number(bounds.maxX);\n maxY = Number(bounds.maxY);\n }\n\n if (maxX - minX <= 0) {\n throw new Error(\n `Invalid raster bounds: width must be positive (got minX=${minX}, maxX=${maxX})`\n );\n }\n if (maxY - minY <= 0) {\n throw new Error(\n `Invalid raster bounds: height must be positive (got minY=${minY}, maxY=${maxY})`\n );\n }\n\n return { minX, minY, maxX, maxY };\n}\n\n/**\n * Load a THREE.Texture from a source descriptor.\n *\n * Supported source types:\n * - `{ type: 'url', url: string }` – load from a URL or data URI\n * - `{ type: 'file', file: File }` – load from a browser File object\n * - `{ type: 'texture', texture: THREE.Texture }` – use a pre-built texture\n *\n * @param {{ type: string, url?: string, file?: File, texture?: THREE.Texture }} source\n * @returns {Promise<THREE.Texture>}\n */\nfunction loadTexture(source) {\n if (source.type === 'texture') {\n return Promise.resolve(source.texture);\n }\n\n let url;\n let createdObjectUrl = false;\n\n if (source.type === 'url') {\n url = source.url;\n } else if (source.type === 'file') {\n url = URL.createObjectURL(source.file);\n createdObjectUrl = true;\n } else {\n return Promise.reject(\n new Error(`Unsupported raster source type: \"${source.type}\"`)\n );\n }\n\n return new Promise((resolve, reject) => {\n const loader = new THREE.TextureLoader();\n loader.load(\n url,\n (texture) => {\n if (createdObjectUrl) URL.revokeObjectURL(url);\n resolve(texture);\n },\n undefined,\n (err) => {\n if (createdObjectUrl) URL.revokeObjectURL(url);\n reject(\n new Error(\n `Failed to load raster texture from \"${url}\": ${err?.message ?? err}`\n )\n );\n }\n );\n });\n}\n\n/**\n * Create a raster overlay layer from the supplied options.\n *\n * The overlay is returned as a self-contained layer descriptor object. Add it\n * to a scene with addRasterOverlay().\n *\n * Placement: the image is rendered as a flat plane on the X/Y axis at the\n * given elevation. The left edge aligns with minX, the right with maxX, the\n * bottom with minY, and the top with maxY. Image orientation follows\n * THREE.js default texture behaviour (flipY=true), so north-up map images\n * are placed correctly without any additional rotation.\n *\n * @param {object} options\n * @param {string} [options.id] - Unique identifier; auto-generated if omitted\n * @param {string} [options.name] - Human-readable display name\n * @param {{ type: string }} options.source - Image source descriptor\n * @param {object} options.bounds - Placement bounds\n * @param {number} [options.elevation=0] - Z position in scene units\n * @param {number} [options.opacity=1] - Initial opacity [0, 1]; clamped if out of range\n * @param {boolean} [options.visible=true] - Initial visibility\n * @param {number} [options.renderOrder=0] - THREE.js renderOrder for draw-order control\n * @returns {Promise<object>} Raster overlay layer descriptor\n */\nexport async function createRasterOverlay(options) {\n const { source, bounds, elevation = 0, visible = true, renderOrder = 0 } = options;\n\n const id = options.id ?? `raster-overlay-${++_overlayIdCounter}`;\n const name = options.name ?? id;\n\n let opacity = options.opacity ?? 1;\n if (opacity < 0 || opacity > 1) {\n console.warn(\n `[baselode] raster overlay \"${id}\": opacity ${opacity} is outside [0, 1] — clamped`\n );\n opacity = Math.max(0, Math.min(1, opacity));\n }\n\n if (!source) throw new Error('raster overlay: options.source is required');\n if (!bounds) throw new Error('raster overlay: options.bounds is required');\n\n const normalizedBounds = normalizeBounds(bounds);\n const { minX, minY, maxX, maxY } = normalizedBounds;\n\n const width = maxX - minX;\n const height = maxY - minY;\n const centerX = (minX + maxX) / 2;\n const centerY = (minY + maxY) / 2;\n\n const texture = await loadTexture(source);\n\n // PlaneGeometry lies in the XY plane by default. THREE.js TextureLoader sets\n // flipY=true, which means the image top-row maps to world maxY and the\n // image bottom-row maps to world minY — correct for north-up map images.\n const geometry = new THREE.PlaneGeometry(width, height);\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n opacity,\n side: THREE.DoubleSide,\n depthWrite: false,\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.position.set(centerX, centerY, elevation);\n mesh.renderOrder = renderOrder;\n mesh.visible = visible;\n\n return { id, name, mesh, texture, bounds: normalizedBounds, elevation, opacity, visible };\n}\n\n// ---------------------------------------------------------------------------\n// Scene-level helpers — all operate on a Baselode3DScene instance\n// ---------------------------------------------------------------------------\n\n/**\n * Add a raster overlay layer to the scene.\n *\n * The layer must have been created with createRasterOverlay(). Duplicate ids\n * replace the existing entry (the old mesh is removed and disposed first).\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {object} layer - Layer returned by createRasterOverlay()\n */\nexport function addRasterOverlay(sceneCtx, layer) {\n if (!sceneCtx.scene) return;\n if (sceneCtx.rasterOverlays.has(layer.id)) {\n removeRasterOverlay(sceneCtx, layer.id);\n }\n sceneCtx.rasterOverlays.set(layer.id, layer);\n sceneCtx.scene.add(layer.mesh);\n}\n\n/**\n * Remove a raster overlay from the scene and dispose its GPU resources.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id - Overlay id\n */\nexport function removeRasterOverlay(sceneCtx, id) {\n const layer = sceneCtx.rasterOverlays.get(id);\n if (!layer) return;\n sceneCtx.scene?.remove(layer.mesh);\n layer.mesh.geometry.dispose();\n layer.mesh.material.dispose();\n if (layer.texture) layer.texture.dispose();\n sceneCtx.rasterOverlays.delete(id);\n}\n\n/**\n * Set the opacity of a raster overlay at runtime without recreating geometry.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id - Overlay id\n * @param {number} opacity - New opacity [0, 1]; clamped if out of range\n */\nexport function setRasterOverlayOpacity(sceneCtx, id, opacity) {\n const layer = sceneCtx.rasterOverlays.get(id);\n if (!layer) return;\n const clamped = Math.max(0, Math.min(1, Number(opacity)));\n layer.opacity = clamped;\n layer.mesh.material.opacity = clamped;\n layer.mesh.material.needsUpdate = true;\n}\n\n/**\n * Show or hide a raster overlay without destroying it.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id - Overlay id\n * @param {boolean} visible\n */\nexport function setRasterOverlayVisibility(sceneCtx, id, visible) {\n const layer = sceneCtx.rasterOverlays.get(id);\n if (!layer) return;\n layer.visible = Boolean(visible);\n layer.mesh.visible = layer.visible;\n}\n\n/**\n * Update the elevation (Z position) of a raster overlay at runtime.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id - Overlay id\n * @param {number} elevation\n */\nexport function setRasterOverlayElevation(sceneCtx, id, elevation) {\n const layer = sceneCtx.rasterOverlays.get(id);\n if (!layer) return;\n layer.elevation = Number(elevation);\n layer.mesh.position.setZ(layer.elevation);\n}\n\n/**\n * Get a raster overlay by id, or undefined if not found.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {string} id\n * @returns {object|undefined}\n */\nexport function getRasterOverlay(sceneCtx, id) {\n return sceneCtx.rasterOverlays.get(id);\n}\n\n/**\n * Return all raster overlay layers in insertion order.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @returns {object[]}\n */\nexport function listRasterOverlays(sceneCtx) {\n return Array.from(sceneCtx.rasterOverlays.values());\n}\n\n/**\n * Remove all raster overlays from the scene and dispose all GPU resources.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function clearRasterOverlays(sceneCtx) {\n for (const id of [...sceneCtx.rasterOverlays.keys()]) {\n removeRasterOverlay(sceneCtx, id);\n }\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Build a string signature from view state for comparison (to detect changes)\n * @param {Object} viewState - View state object with camera, target, up vectors\n * @returns {string} String signature representing the view state\n */\nexport function buildViewSignature(viewState) {\n if (!viewState) return '';\n const toNum = (v) => Number.isFinite(v) ? v.toFixed(3) : 'nan';\n return [\n toNum(viewState.camera?.x),\n toNum(viewState.camera?.y),\n toNum(viewState.camera?.z),\n toNum(viewState.target?.x),\n toNum(viewState.target?.y),\n toNum(viewState.target?.z),\n toNum(viewState.up?.x),\n toNum(viewState.up?.y),\n toNum(viewState.up?.z)\n ].join('|');\n}\n\n/**\n * Extract current view state from 3D scene state\n * @param {Object} state - Baselode3D scene state with camera and controls\n * @returns {Object|null} View state object or null if state invalid\n */\nexport function getViewState(state) {\n if (!state.camera || !state.controls) return null;\n return {\n camera: {\n x: state.camera.position.x,\n y: state.camera.position.y,\n z: state.camera.position.z\n },\n target: {\n x: state.controls.target.x,\n y: state.controls.target.y,\n z: state.controls.target.z\n },\n up: {\n x: state.camera.up.x,\n y: state.camera.up.y,\n z: state.camera.up.z\n }\n };\n}\n\n/**\n * Apply a view state to the 3D scene camera and controls\n * @param {Object} state - Baselode3D scene state\n * @param {Object} viewState - View state to apply\n * @returns {boolean} True if successfully applied\n */\nexport function setViewState(state, viewState) {\n if (!state.camera || !state.controls || !viewState) return false;\n const camera = viewState.camera || {};\n const target = viewState.target || {};\n const up = viewState.up || {};\n\n const values = [camera.x, camera.y, camera.z, target.x, target.y, target.z, up.x, up.y, up.z];\n if (!values.every(Number.isFinite)) return false;\n\n state.camera.position.set(camera.x, camera.y, camera.z);\n state.controls.target.set(target.x, target.y, target.z);\n state.camera.up.set(up.x, up.y, up.z);\n state.camera.lookAt(target.x, target.y, target.z);\n state.controls.update();\n state._lastViewSignature = buildViewSignature(viewState);\n return true;\n}\n\n/**\n * Emit view change event if view has changed (throttled to 250ms)\n * @param {Object} state - Baselode3D scene state with viewChangeHandler\n */\nexport function emitViewChangeIfNeeded(state) {\n if (!state.viewChangeHandler) return;\n const now = Date.now();\n if (now - state._lastViewEmitMs < 250) return;\n const viewState = getViewState(state);\n if (!viewState) return;\n const signature = buildViewSignature(viewState);\n if (signature === state._lastViewSignature) return;\n state._lastViewSignature = signature;\n state._lastViewEmitMs = now;\n state.viewChangeHandler(viewState);\n}\n\n/**\n * Fit camera to view all content within specified bounds\n * @param {Object} state - Baselode3D scene state\n * @param {Object} bounds - Bounding box {minX, maxX, minY, maxY, minZ, maxZ}\n */\nexport function fitCameraToBounds(state, { minX, maxX, minY, maxY, minZ, maxZ }) {\n const centerX = (minX + maxX) / 2;\n const centerY = (minY + maxY) / 2;\n const centerZ = (minZ + maxZ) / 2;\n const sizeX = maxX - minX;\n const sizeY = maxY - minY;\n const sizeZ = maxZ - minZ;\n const maxDim = Math.max(sizeX, sizeY, sizeZ, 1);\n const distance = maxDim * 2;\n\n state.controls.target.set(centerX, centerY, centerZ);\n state.camera.position.set(centerX + distance, centerY + distance, centerZ + distance);\n state.camera.lookAt(centerX, centerY, centerZ);\n state.controls.update();\n}\n\n/**\n * Recenter camera to origin at specified distance\n * @param {Object} state - Baselode3D scene state\n * @param {number} distance - Distance from origin\n */\nexport function recenterCameraToOrigin(state, distance = 1000) {\n if (!state.camera || !state.controls) return;\n state.controls.target.set(0, 0, 0);\n state.camera.position.set(distance, distance, distance);\n state.camera.lookAt(0, 0, 0);\n state.controls.update();\n}\n\n/**\n * Position camera looking straight down from above\n * @param {Object} state - Baselode3D scene state\n * @param {number} distance - Distance above origin\n */\nexport function lookDown(state, distance = 2000) {\n if (!state.camera || !state.controls) return;\n state.controls.target.set(0, 0, 0);\n state.camera.position.set(0, 0, distance);\n state.camera.up.set(0, 1, 0);\n state.camera.lookAt(0, 0, 0);\n state.controls.update();\n}\n\n/**\n * Pan the camera view by screen-space delta\n * @param {Object} state - Baselode3D scene state\n * @param {number} dx - Horizontal pan delta\n * @param {number} dy - Vertical pan delta\n */\nexport function pan(state, dx = 0, dy = 0) {\n if (!state.controls) return;\n if (typeof state.controls.pan === 'function') {\n state.controls.pan(dx, dy);\n state.controls.update();\n }\n}\n\n/**\n * Zoom camera in or out by scale factor\n * @param {Object} state - Baselode3D scene state\n * @param {number} scale - Scale factor (>1 zooms out, <1 zooms in)\n */\nexport function dolly(state, scale = 1.1) {\n if (!state.controls || typeof state.controls.dollyIn !== 'function' || typeof state.controls.dollyOut !== 'function') return;\n if (scale > 1) {\n state.controls.dollyOut(scale);\n } else {\n state.controls.dollyIn(1 / scale);\n }\n state.controls.update();\n}\n\n/**\n * Focus camera on last computed bounds with optional padding\n * @param {Object} state - Baselode3D scene state with lastBounds property\n * @param {number} padding - Padding multiplier for bounds (1.2 = 20% larger view)\n */\nexport function focusOnLastBounds(state, padding = 1.2) {\n if (!state.lastBounds) return;\n const {\n minX, maxX, minY, maxY, minZ, maxZ\n } = state.lastBounds;\n const sizeX = (maxX - minX) * padding;\n const sizeY = (maxY - minY) * padding;\n const sizeZ = (maxZ - minZ) * padding;\n const centerX = (minX + maxX) / 2;\n const centerY = (minY + maxY) / 2;\n const centerZ = (minZ + maxZ) / 2;\n const maxDim = Math.max(sizeX, sizeY, sizeZ, 1);\n const distance = maxDim * 2;\n state.controls.target.set(centerX, centerY, centerZ);\n state.camera.position.set(centerX + distance, centerY + distance, centerZ + distance);\n state.camera.lookAt(centerX, centerY, centerZ);\n state.controls.update();\n}\n\n/** Minimum and maximum permitted camera FOV in degrees. */\nexport const FOV_MIN_DEG = 1;\nexport const FOV_MAX_DEG = 120;\n\n/**\n * Change the camera field-of-view while keeping the visible scene the same apparent size.\n * Adjusts camera distance so the frustum height at the orbit target is preserved.\n * FOV is clamped to [FOV_MIN_DEG, FOV_MAX_DEG] to avoid numerical issues near 0° or 180°.\n * @param {Object} state - Baselode3D scene state with camera and controls\n * @param {number} fovDeg - Desired FOV in degrees\n * @returns {boolean} True if the FOV was applied, false if state is invalid\n */\nexport function setFov(state, fovDeg) {\n if (!state.camera || !state.controls) return false;\n if (!Number.isFinite(fovDeg)) return false;\n const clampedFov = Math.min(FOV_MAX_DEG, Math.max(FOV_MIN_DEG, fovDeg));\n\n const target = state.controls.target;\n const currentDist = state.camera.position.distanceTo(target);\n const currentFovRad = (state.camera.fov * Math.PI) / 180;\n const frustumHeight = 2 * currentDist * Math.tan(currentFovRad / 2);\n\n const newFovRad = (clampedFov * Math.PI) / 180;\n const newDist = frustumHeight / (2 * Math.tan(newFovRad / 2));\n\n const dir = state.camera.position.clone().sub(target).normalize();\n state.camera.position.copy(target).addScaledVector(dir, newDist);\n state.camera.fov = clampedFov;\n state.camera.updateProjectionMatrix();\n state.controls.update();\n return true;\n}\n\n/**\n * Switch between orbit and fly camera control modes\n * @param {Object} state - Baselode3D scene state with orbit and fly controls\n * @param {string} mode - Control mode ('orbit' or 'fly')\n */\nexport function setControlMode(state, mode = 'orbit') {\n state.controlMode = mode === 'fly' ? 'fly' : 'orbit';\n if (state.controlMode === 'fly') {\n if (state.controls) state.controls.enabled = false;\n if (state.flyControls) state.flyControls.enabled = true;\n } else {\n if (state.flyControls) state.flyControls.enabled = false;\n if (state.controls) {\n state.controls.enabled = true;\n state.camera.getWorldDirection(state._tmpDir);\n const target = state.camera.position.clone().addScaledVector(state._tmpDir, 10);\n state.controls.target.copy(target);\n state.controls.update();\n }\n }\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { buildEqualRangeColorScale, getEqualRangeBinIndex, getEqualRangeColor } from './assayColorScale.js';\nimport { fitCameraToBounds } from './baselode3dCameraControls.js';\nimport { syncSelectables } from './sceneSelectables.js';\n\n/** Default color for low or zero assay values */\nconst LOW_ASSAY_GREY = '#9ca3af';\n\n/**\n * Get measured depth range for a segment between two points\n */\nexport function getMeasuredDepthRange(p1, p2) {\n const md1 = Number(p1?.md);\n const md2 = Number(p2?.md);\n if (!Number.isFinite(md1) || !Number.isFinite(md2)) return null;\n const segStart = Math.min(md1, md2);\n const segEnd = Math.max(md1, md2);\n if (segEnd <= segStart) return null;\n return { segStart, segEnd };\n}\n\n/**\n * Calculate weighted average assay value for a segment overlapping with assay intervals\n */\nexport function getWeightedIntervalValue(assayIntervals, segStart, segEnd) {\n let weightedSum = 0;\n let weightTotal = 0;\n\n for (let i = 0; i < assayIntervals.length; i += 1) {\n const candidate = assayIntervals[i];\n const from = Number(candidate?.from);\n const to = Number(candidate?.to);\n const value = Number(candidate?.value);\n if (!Number.isFinite(from) || !Number.isFinite(to) || !Number.isFinite(value) || to <= from) continue;\n const overlapStart = Math.max(segStart, from);\n const overlapEnd = Math.min(segEnd, to);\n const overlap = overlapEnd - overlapStart;\n if (overlap <= 0) continue;\n weightedSum += value * overlap;\n weightTotal += overlap;\n }\n\n if (weightTotal <= 0) return null;\n const value = weightedSum / weightTotal;\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * Get THREE.Color for an assay value based on color scale\n */\nexport function getAssaySegmentColor(value, assayScale) {\n if (!Number.isFinite(value)) return new THREE.Color(LOW_ASSAY_GREY);\n const binIndex = getEqualRangeBinIndex(value, assayScale);\n if (binIndex < 0) return new THREE.Color(LOW_ASSAY_GREY);\n const colorHex = getEqualRangeColor(value, assayScale, LOW_ASSAY_GREY);\n return new THREE.Color(colorHex);\n}\n\n/**\n * Get a deterministic hex color for a categorical value using FNV-1a hash → HSL\n */\nexport function getCategoryHexColor(category) {\n if (!category || !String(category).trim()) return LOW_ASSAY_GREY;\n const h = seededUnit(String(category).toLowerCase().trim());\n return '#' + new THREE.Color().setHSL(h, 0.70, 0.50).getHexString();\n}\n\n/**\n * Normalize drillhole rendering options with defaults\n */\nexport function normalizeDrillholeRenderOptions(options = {}) {\n return {\n preserveView: Boolean(options.preserveView),\n assayIntervalsByHole: options.assayIntervalsByHole || null,\n selectedAssayVariable: options.selectedAssayVariable || '',\n isCategoricalVariable: Boolean(options.isCategoricalVariable),\n };\n}\n\n/**\n * Collect all numeric assay values from interval data\n */\nexport function collectAssayValues(assayIntervalsByHole, selectedAssayVariable) {\n if (!assayIntervalsByHole || !selectedAssayVariable) return [];\n const allAssayValues = [];\n Object.values(assayIntervalsByHole).forEach((intervals) => {\n (intervals || []).forEach((interval) => {\n const value = Number(interval?.value);\n if (Number.isFinite(value)) allAssayValues.push(value);\n });\n });\n return allAssayValues;\n}\n\n/**\n * Build user data object for drillhole mesh\n */\nexport function buildHoleUserData(hole) {\n return {\n holeId: hole.id,\n project: hole.project\n };\n}\n\n/**\n * Normalize hole key to lowercase trimmed string for case-insensitive matching\n */\nexport function normalizeHoleKey(value) {\n return `${value ?? ''}`.trim().toLowerCase();\n}\n\n/**\n * Generate a deterministic per-segment color based on hole ID and segment index\n */\nexport function randomSegmentColor(holeId, segmentIndex) {\n const seed = `${holeId ?? ''}:${segmentIndex ?? 0}`;\n const base = seededUnit(seed);\n const band = ((segmentIndex ?? 0) % 14) / 14;\n const hue = (base * 0.15 + band * 0.85) % 1;\n const color = new THREE.Color();\n color.setHSL(hue, 1.0, 0.5);\n return color;\n}\n\n/**\n * Produce a deterministic float in [0, 1) from an input string (FNV-1a hash)\n */\nexport function seededUnit(input) {\n const text = `${input ?? ''}`;\n let hash = 2166136261;\n for (let i = 0; i < text.length; i += 1) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0) / 4294967295;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers (not exported)\n// ---------------------------------------------------------------------------\n\nfunction getDominantCategory(intervals, segStart, segEnd) {\n let best = null;\n let bestOverlap = 0;\n for (const iv of intervals) {\n const from = Number(iv?.from);\n const to = Number(iv?.to);\n if (!Number.isFinite(from) || !Number.isFinite(to)) continue;\n const overlap = Math.min(segEnd, to) - Math.max(segStart, from);\n if (overlap > bestOverlap) { bestOverlap = overlap; best = iv?.value; }\n }\n return best;\n}\n\nfunction resolveAssayIntervalsForHole(hole, assayIntervalsByHole) {\n if (!assayIntervalsByHole || !hole) return [];\n const holeId = hole.id || hole.holeId;\n if (!holeId) return [];\n\n const exact = assayIntervalsByHole[holeId];\n if (Array.isArray(exact) && exact.length) return exact;\n\n const normalized = normalizeHoleKey(holeId);\n if (normalized) {\n const byNormalized = assayIntervalsByHole[normalized];\n if (Array.isArray(byNormalized) && byNormalized.length) return byNormalized;\n }\n\n return [];\n}\n\nfunction getSegmentColor({ selectedAssayVariable, assayIntervals, assayScale, holeId, segmentIndex, p1, p2, isCategorical }) {\n if (!selectedAssayVariable) {\n return randomSegmentColor(holeId, segmentIndex);\n }\n if (selectedAssayVariable === '__HAS_ASSAY__') {\n if (!assayIntervals?.length) return new THREE.Color(LOW_ASSAY_GREY);\n const depthRange = getMeasuredDepthRange(p1, p2);\n if (!depthRange) return new THREE.Color(LOW_ASSAY_GREY);\n const hasData = assayIntervals.some((interval) => {\n const from = Number(interval?.from);\n const to = Number(interval?.to);\n if (!Number.isFinite(from) || !Number.isFinite(to)) return false;\n const overlapStart = Math.max(depthRange.segStart, from);\n const overlapEnd = Math.min(depthRange.segEnd, to);\n return overlapEnd > overlapStart;\n });\n return hasData ? new THREE.Color('#ff8c42') : new THREE.Color(LOW_ASSAY_GREY);\n }\n if (!assayIntervals?.length) return new THREE.Color(LOW_ASSAY_GREY);\n const depthRange = getMeasuredDepthRange(p1, p2);\n if (!depthRange) return new THREE.Color(LOW_ASSAY_GREY);\n if (isCategorical) {\n const cat = getDominantCategory(assayIntervals, depthRange.segStart, depthRange.segEnd);\n return new THREE.Color(getCategoryHexColor(cat));\n }\n const value = getWeightedIntervalValue(assayIntervals, depthRange.segStart, depthRange.segEnd);\n return getAssaySegmentColor(value, assayScale);\n}\n\n// ---------------------------------------------------------------------------\n// Public scene functions\n// ---------------------------------------------------------------------------\n\n/**\n * Build cylinder meshes for all drillholes and add them to the scene.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {Array} holes - Array of desurveyed hole objects with `points`\n * @param {object} [options]\n */\nexport function setDrillholes(sceneCtx, holes, options = {}) {\n if (!sceneCtx.scene) return;\n\n clearDrillholes(sceneCtx);\n if (!holes || holes.length === 0) return;\n\n const { preserveView, assayIntervalsByHole, selectedAssayVariable, isCategoricalVariable } = normalizeDrillholeRenderOptions(options);\n const allAssayValues = isCategoricalVariable ? [] : collectAssayValues(assayIntervalsByHole, selectedAssayVariable);\n const assayScale = buildEqualRangeColorScale(allAssayValues);\n\n let minX = Infinity, maxX = -Infinity;\n let minY = Infinity, maxY = -Infinity;\n let minZ = Infinity, maxZ = -Infinity;\n\n const tmpVec = new THREE.Vector3();\n const up = new THREE.Vector3(0, 1, 0);\n\n holes.forEach((hole, idx) => {\n const goldenAngle = 137.5;\n const hue = ((idx * goldenAngle) % 360) / 360;\n const defaultColor = new THREE.Color().setHSL(hue, 0.75, 0.55);\n const points = (hole.points || []).map((p) => {\n minX = Math.min(minX, p.x);\n maxX = Math.max(maxX, p.x);\n minY = Math.min(minY, p.y);\n maxY = Math.max(maxY, p.y);\n minZ = Math.min(minZ, p.z);\n maxZ = Math.max(maxZ, p.z);\n const point = new THREE.Vector3(p.x, p.y, p.z);\n point.md = p.md;\n return point;\n });\n\n if (points.length < 2) {\n if (points.length === 1) {\n const sphereGeom = new THREE.SphereGeometry(5, 12, 12);\n const sphereMat = new THREE.MeshLambertMaterial({\n color: defaultColor,\n emissive: defaultColor,\n emissiveIntensity: 0.2\n });\n const sphere = new THREE.Mesh(sphereGeom, sphereMat);\n sphere.position.copy(points[0]);\n sphere.userData = buildHoleUserData(hole);\n sceneCtx.scene.add(sphere);\n sceneCtx.drillLines.push(sphere);\n sceneCtx.drillMeshes.push(sphere);\n }\n return;\n }\n\n const group = new THREE.Group();\n group.userData = buildHoleUserData(hole);\n const assayIntervals = selectedAssayVariable\n ? resolveAssayIntervalsForHole(hole, assayIntervalsByHole)\n : [];\n\n for (let i = 0; i < points.length - 1; i += 1) {\n const p1 = points[i];\n const p2 = points[i + 1];\n const dir = tmpVec.subVectors(p2, p1);\n const len = dir.length();\n if (len <= 0.001) continue;\n const radius = 2.2;\n const cylinderGeom = new THREE.CylinderGeometry(radius, radius, len, 6, 1, true);\n const segmentColor = getSegmentColor({\n selectedAssayVariable,\n assayIntervals,\n assayScale,\n holeId: hole.id,\n segmentIndex: i,\n p1,\n p2,\n isCategorical: isCategoricalVariable,\n });\n const cylinderMat = new THREE.MeshLambertMaterial({\n color: segmentColor,\n flatShading: true,\n emissive: segmentColor,\n emissiveIntensity: 0.15\n });\n const mesh = new THREE.Mesh(cylinderGeom, cylinderMat);\n mesh.position.copy(p1.clone().addScaledVector(dir, 0.5));\n mesh.quaternion.setFromUnitVectors(up, dir.clone().normalize());\n mesh.userData = buildHoleUserData(hole);\n group.add(mesh);\n sceneCtx.drillMeshes.push(mesh);\n }\n\n sceneCtx.scene.add(group);\n sceneCtx.drillLines.push(group);\n });\n\n if (sceneCtx.camera && sceneCtx.controls) {\n sceneCtx.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n if (!preserveView) {\n fitCameraToBounds(sceneCtx, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n }\n syncSelectables(sceneCtx);\n}\n\n/**\n * Remove all drillhole meshes from the scene and dispose GPU resources.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function clearDrillholes(sceneCtx) {\n sceneCtx.drillLines.forEach((line) => {\n sceneCtx.scene.remove(line);\n if (line.isGroup) {\n line.traverse((child) => {\n if (child.isMesh) {\n child.geometry.dispose();\n child.material.dispose();\n }\n });\n } else if (line.isMesh) {\n line.geometry.dispose();\n line.material.dispose();\n }\n });\n sceneCtx.drillLines = [];\n sceneCtx.drillMeshes = [];\n syncSelectables(sceneCtx);\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\n\n/**\n * Default panel width in scene units.\n * @type {number}\n */\nexport const STRIP_LOG_DEFAULT_PANEL_WIDTH = 20;\n\n/**\n * Default lateral offset from the drillhole collar in scene units.\n * @type {number}\n */\nexport const STRIP_LOG_DEFAULT_LATERAL_OFFSET = 15;\n\n/**\n * Default line color for the strip log graph.\n * @type {string}\n */\nexport const STRIP_LOG_DEFAULT_COLOR = '#00bcd4';\n\n/**\n * Normalise strip log options with defaults.\n *\n * @param {object} [options]\n * @returns {{ panelWidth: number, lateralOffset: number, color: string, valueMin: number|null, valueMax: number|null }}\n */\nexport function normalizeStripLogOptions(options = {}) {\n return {\n panelWidth: options.panelWidth != null ? Number(options.panelWidth) : STRIP_LOG_DEFAULT_PANEL_WIDTH,\n lateralOffset: options.lateralOffset != null ? Number(options.lateralOffset) : STRIP_LOG_DEFAULT_LATERAL_OFFSET,\n color: options.color || STRIP_LOG_DEFAULT_COLOR,\n valueMin: options.valueMin != null ? Number(options.valueMin) : null,\n valueMax: options.valueMax != null ? Number(options.valueMax) : null,\n };\n}\n\n/**\n * Compute the vertical extent of a hole's points.\n *\n * Returns `{ topY, botY, height }` where `topY >= botY` (Y = elevation in the\n * scene's coordinate system). Returns `null` when the extent is degenerate.\n *\n * @param {Array<{x:number, y:number, z:number}>} points\n * @returns {{ topY: number, botY: number, height: number }|null}\n */\nexport function getHoleVerticalExtent(points) {\n if (!points || points.length < 2) return null;\n let topZ = -Infinity;\n let botZ = Infinity;\n for (const p of points) {\n if (p.z > topZ) topZ = p.z;\n if (p.z < botZ) botZ = p.z;\n }\n const height = topZ - botZ;\n if (height < 0.001) return null;\n return { topZ, botZ, height };\n}\n\n/**\n * Map depth/value data pairs onto panel-local 2D coordinates.\n *\n * Panel-local space: X runs left (min value) to right (max value); Y runs top\n * (shallow) to bottom (deep) along the hole direction. Z = 0.01 offsets the line\n * slightly in front of the panel face. The caller is responsible for applying the\n * panel's world-space quaternion to these local-space points.\n *\n * @param {number[]} depths - downhole measured depths for each sample\n * @param {number[]} values\n * @param {number} panelWidth\n * @param {number} panelHeight - total length of the panel (= hole measured depth at toe)\n * @param {number|null} valueMin - explicit min override (null = auto)\n * @param {number|null} valueMax - explicit max override (null = auto)\n * @param {number|null} depthScale - measured depth at toe used to anchor depth=0 at the\n * collar and scale positions correctly along the hole. When null the depths are\n * auto-scaled between their own min/max (legacy behaviour).\n * @returns {THREE.Vector3[]} panel-local points (Z = 0.01 to sit in front of the panel)\n */\nexport function buildStripLogLinePoints(depths, values, panelWidth, panelHeight, valueMin, valueMax, depthScale) {\n if (!Array.isArray(depths) || !Array.isArray(values)) return [];\n\n const len = Math.min(depths.length, values.length);\n const valid = [];\n for (let i = 0; i < len; i++) {\n if (Number.isFinite(depths[i]) && Number.isFinite(values[i])) {\n valid.push({ d: depths[i], v: values[i] });\n }\n }\n if (valid.length < 2) return [];\n\n // When depthScale is provided, depth 0 = collar and depthScale = toe, so each\n // sample lands at its true position along the hole axis. Otherwise fall back\n // to auto-scaling across the data's own depth range (legacy behaviour).\n const depthRef = (depthScale != null && depthScale > 0) ? depthScale : null;\n const minDepth = depthRef != null ? 0 : Math.min(...valid.map((p) => p.d));\n const maxDepth = depthRef != null ? depthRef : Math.max(...valid.map((p) => p.d));\n const depthRange = maxDepth - minDepth || 1;\n\n const autoMin = Math.min(...valid.map((p) => p.v));\n const autoMax = Math.max(...valid.map((p) => p.v));\n const vMin = valueMin != null ? valueMin : autoMin;\n const vMax = valueMax != null ? valueMax : autoMax;\n const valRange = vMax - vMin || 1;\n\n return valid.map(({ d, v }) => {\n const tDepth = (d - minDepth) / depthRange; // 0=collar → 1=toe\n const tVal = Math.max(0, Math.min(1, (v - vMin) / valRange));\n const localX = -panelWidth / 2 + tVal * panelWidth;\n // localY increases along +holeDir (downhole). Mesh origin is at the collar,\n // so localY = 0 → collar and localY = +panelHeight → toe.\n const localY = tDepth * panelHeight;\n return new THREE.Vector3(localX, localY, 0.01);\n });\n}\n\n/**\n * Build a flat ribbon BufferGeometry by extruding a polyline in the XY plane.\n *\n * For each consecutive pair of points a quad is generated perpendicular to the\n * segment direction, giving a solid filled line of the requested half-width.\n * Using a Mesh + MeshBasicMaterial avoids the WebGL line-width restriction and\n * the LineMaterial shader complexity.\n *\n * @param {THREE.Vector3[]} points - Line points in panel-local XY space\n * @param {number} halfWidth - Half the desired ribbon width\n * @returns {THREE.BufferGeometry|null}\n */\nfunction buildLineRibbonGeometry(points, halfWidth) {\n const n = points.length;\n if (n < 2) return null;\n\n const positions = [];\n const indices = [];\n let vi = 0;\n\n for (let i = 0; i < n - 1; i++) {\n const p1 = points[i];\n const p2 = points[i + 1];\n const dx = p2.x - p1.x;\n const dy = p2.y - p1.y;\n const len = Math.sqrt(dx * dx + dy * dy);\n if (len < 1e-6) continue;\n\n // Perpendicular unit vector in XY, scaled to half-width\n const nx = (-dy / len) * halfWidth;\n const ny = (dx / len) * halfWidth;\n const z = 0.01;\n\n positions.push(\n p1.x + nx, p1.y + ny, z,\n p1.x - nx, p1.y - ny, z,\n p2.x + nx, p2.y + ny, z,\n p2.x - nx, p2.y - ny, z,\n );\n indices.push(vi, vi + 1, vi + 2, vi + 1, vi + 3, vi + 2);\n vi += 4;\n }\n\n if (positions.length === 0) return null;\n const geom = new THREE.BufferGeometry();\n geom.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));\n geom.setIndex(indices);\n return geom;\n}\n\n/**\n * Build a Three.js Group containing the strip log trace for one hole.\n *\n * No backing panel or border — just a solid ribbon tracing the assay values\n * floating in scene space beside the hole. The trace is oriented parallel to\n * the collar→toe axis and offset laterally from the hole by `lateralOffset`\n * scene units.\n *\n * @param {object} hole - Hole object with `id` and `points`\n * @param {object} stripLog - Strip log definition (holeId, depths, values, options)\n * @returns {THREE.Group|null}\n */\nexport function buildStripLogGroup(hole, stripLog) {\n const points = hole.points || [];\n if (points.length < 2) return null;\n\n const collar = points[0];\n const toe = points[points.length - 1];\n\n // Hole axis: collar → toe\n const holeDirRaw = new THREE.Vector3(\n toe.x - collar.x,\n toe.y - collar.y,\n toe.z - collar.z,\n );\n const holeLength = holeDirRaw.length();\n if (holeLength < 0.001) return null;\n const holeDir = holeDirRaw.clone().normalize();\n\n const opts = normalizeStripLogOptions(stripLog.options);\n const { panelWidth, lateralOffset, color, valueMin, valueMax } = opts;\n\n // Lateral direction: perpendicular to hole axis, as horizontal as possible\n const worldZ = new THREE.Vector3(0, 0, 1);\n let lateralDir = new THREE.Vector3().crossVectors(holeDir, worldZ);\n if (lateralDir.lengthSq() < 1e-6) {\n lateralDir.set(1, 0, 0);\n } else {\n lateralDir.normalize();\n }\n\n const panelNormal = new THREE.Vector3().crossVectors(lateralDir, holeDir).normalize();\n\n // Origin of the trace: collar shifted laterally by the offset\n const traceOrigin = new THREE.Vector3(collar.x, collar.y, collar.z)\n .addScaledVector(lateralDir, lateralOffset);\n\n // Rotation: local X → lateralDir (value axis), local Y → holeDir (depth axis)\n const rotMatrix = new THREE.Matrix4().makeBasis(lateralDir, holeDir, panelNormal);\n const quaternion = new THREE.Quaternion().setFromRotationMatrix(rotMatrix);\n\n // Use the hole's measured depth at the toe as the depth scale so that each\n // sample's depth maps to its true position along the hole axis.\n const measuredDepths = points.map((p) => p.md).filter(Number.isFinite);\n const depthScale = measuredDepths.length > 0 ? Math.max(...measuredDepths) : holeLength;\n\n const linePoints = buildStripLogLinePoints(\n stripLog.depths,\n stripLog.values,\n panelWidth,\n holeLength,\n valueMin,\n valueMax,\n depthScale,\n );\n\n if (linePoints.length < 2) return null;\n\n const group = new THREE.Group();\n group.userData = { holeId: hole.id, isStripLog: true };\n\n const halfWidth = panelWidth * 0.025;\n const ribbonGeom = buildLineRibbonGeometry(linePoints, halfWidth);\n if (!ribbonGeom) return null;\n\n const ribbonMat = new THREE.MeshBasicMaterial({\n color: new THREE.Color(color),\n side: THREE.DoubleSide,\n });\n const ribbonMesh = new THREE.Mesh(ribbonGeom, ribbonMat);\n ribbonMesh.position.copy(traceOrigin);\n ribbonMesh.quaternion.copy(quaternion);\n group.add(ribbonMesh);\n\n return group;\n}\n\n/**\n * Add floating 2D strip log panels beside drillholes in the 3D scene.\n *\n * Each entry in `stripLogs` is matched to a hole by `holeId`. A flat\n * rectangular panel is created for each matched pair and added to the scene\n * beside the corresponding drillhole.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {Array<{id: string, points: Array<{x:number,y:number,z:number}>}>} holes\n * Hole objects already rendered in the scene (same array passed to setDrillholes).\n * @param {Array<object>} stripLogs - Strip log definitions. Each must contain:\n * - `holeId` {string} — must match a `hole.id`\n * - `depths` {number[]} — downhole depth positions for each sample\n * - `values` {number[]} — numeric value at each depth\n * - `options` {object} — optional display overrides:\n * - `panelWidth` {number} scene-unit width of the panel (default 20)\n * - `lateralOffset` {number} scene-unit offset from the hole (default 15)\n * - `color` {string} CSS/hex line colour (default '#00bcd4')\n * - `valueMin` {number} explicit minimum value for scaling\n * - `valueMax` {number} explicit maximum value for scaling\n */\nexport function setStripLogs(sceneCtx, holes, stripLogs) {\n if (!sceneCtx.scene) return;\n\n clearStripLogs(sceneCtx);\n if (!stripLogs || stripLogs.length === 0) return;\n if (!holes || holes.length === 0) return;\n\n const holeById = new Map();\n holes.forEach((hole) => {\n if (hole.id != null) holeById.set(hole.id, hole);\n });\n\n stripLogs.forEach((stripLog) => {\n const hole = holeById.get(stripLog.holeId);\n if (!hole) return;\n\n const group = buildStripLogGroup(hole, stripLog);\n if (!group) return;\n\n sceneCtx.scene.add(group);\n sceneCtx.stripLogGroups.push(group);\n });\n}\n\n/**\n * Remove all strip log panels from the scene and free GPU resources.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function clearStripLogs(sceneCtx) {\n if (!sceneCtx.stripLogGroups) return;\n sceneCtx.stripLogGroups.forEach((group) => {\n if (sceneCtx.scene) sceneCtx.scene.remove(group);\n group.traverse((child) => {\n if (child.geometry) child.geometry.dispose();\n if (child.material) child.material.dispose();\n });\n });\n sceneCtx.stripLogGroups = [];\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';\nimport { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';\nimport { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';\n\n/** Glow colour for the selection outline (#ffffbb) */\nconst GLOW_COLOR = '#ffffbb';\n\n/** OutlinePass tuning: produces a soft ~3 px diffuse halo */\nconst EDGE_STRENGTH = 2.0;\nconst EDGE_THICKNESS = 1.5;\nconst EDGE_GLOW = 1.0;\n\n/**\n * Initialise an EffectComposer with a RenderPass and OutlinePass for\n * click-select glow. Attaches `_composer` and `_outlinePass` to sceneCtx.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function initSelectionGlow(sceneCtx) {\n const { renderer, scene, camera, container } = sceneCtx;\n if (!renderer || !scene || !camera) return;\n\n const width = container?.clientWidth || renderer.domElement.clientWidth || 1;\n const height = container?.clientHeight || renderer.domElement.clientHeight || 1;\n\n const composer = new EffectComposer(renderer);\n\n const renderPass = new RenderPass(scene, camera);\n composer.addPass(renderPass);\n\n const resolution = new THREE.Vector2(width, height);\n const outlinePass = new OutlinePass(resolution, scene, camera);\n outlinePass.visibleEdgeColor.set(GLOW_COLOR);\n outlinePass.hiddenEdgeColor.set(GLOW_COLOR);\n outlinePass.edgeStrength = EDGE_STRENGTH;\n outlinePass.edgeThickness = EDGE_THICKNESS;\n outlinePass.edgeGlow = EDGE_GLOW;\n outlinePass.pulsePeriod = 0;\n outlinePass.selectedObjects = [];\n composer.addPass(outlinePass);\n\n composer.setSize(width, height);\n\n sceneCtx._composer = composer;\n sceneCtx._outlinePass = outlinePass;\n}\n\n/**\n * Resize the effect composer and outline pass to match the new viewport\n * dimensions. Call this whenever the renderer is resized.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {number} width - New viewport pixel width\n * @param {number} height - New viewport pixel height\n */\nexport function resizeGlow(sceneCtx, width, height) {\n if (!sceneCtx._composer || !sceneCtx._outlinePass) return;\n sceneCtx._composer.setSize(width, height);\n sceneCtx._outlinePass.resolution.set(width, height);\n}\n\n/**\n * Apply or clear the glow selection highlight.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {THREE.Object3D|null} object - Object to highlight, or null to clear\n */\nexport function applySelection(sceneCtx, object) {\n if (!sceneCtx._outlinePass) return;\n sceneCtx._outlinePass.selectedObjects = object ? [object] : [];\n sceneCtx._selectedObject = object || null;\n}\n\n/**\n * Dispose the effect composer and free all GPU resources associated with the\n * selection glow.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function disposeSelectionGlow(sceneCtx) {\n if (sceneCtx._composer) {\n sceneCtx._composer.dispose();\n sceneCtx._composer = null;\n }\n sceneCtx._outlinePass = null;\n sceneCtx._selectedObject = null;\n sceneCtx.selectables = [];\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { getColorForValue } from '../data/blockModelLoader.js';\nimport { fitCameraToBounds } from './baselode3dCameraControls.js';\nimport { syncSelectables } from './sceneSelectables.js';\n\n// Six face definitions. neibDir locates the neighbour in that direction.\n// verts are ±1 scale factors of the half-extents (dx/2 etc.).\nconst FACE_DEFS = [\n { normal: [ 1, 0, 0], neibDir: [ 1, 0, 0], verts: [[ 1,-1,-1],[ 1, 1,-1],[ 1, 1, 1],[ 1,-1, 1]] },\n { normal: [-1, 0, 0], neibDir: [-1, 0, 0], verts: [[-1,-1, 1],[-1, 1, 1],[-1, 1,-1],[-1,-1,-1]] },\n { normal: [ 0, 1, 0], neibDir: [ 0, 1, 0], verts: [[-1, 1, 1],[ 1, 1, 1],[ 1, 1,-1],[-1, 1,-1]] },\n { normal: [ 0,-1, 0], neibDir: [ 0,-1, 0], verts: [[ 1,-1, 1],[-1,-1, 1],[-1,-1,-1],[ 1,-1,-1]] },\n { normal: [ 0, 0, 1], neibDir: [ 0, 0, 1], verts: [[-1,-1, 1],[ 1,-1, 1],[ 1, 1, 1],[-1, 1, 1]] },\n { normal: [ 0, 0,-1], neibDir: [ 0, 0,-1], verts: [[ 1,-1,-1],[-1,-1,-1],[-1, 1,-1],[ 1, 1,-1]] },\n];\n\n/**\n * Render block model data as a single merged mesh of exterior faces only.\n *\n * Adjacent blocks' shared faces are skipped so there are no coincident\n * polygons and therefore no z-fighting. Vertex colours are used so the\n * entire model is a single draw call.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {Array<Object>} data - Block rows (canonical column names)\n * @param {string} selectedProperty - Attribute column used for colouring\n * @param {Object} stats - Property statistics\n * @param {Object} [options]\n * @param {Object} [options.offset] - Optional {x, y, z} translation\n * @param {number} [options.opacity=1.0] - Initial material opacity (0–1)\n * @param {boolean} [options.autoCenter=true] - Auto-centre blocks at scene origin\n */\nexport function setBlocks(sceneCtx, data, selectedProperty, stats, options = {}) {\n if (!sceneCtx.scene) return;\n\n clearBlocks(sceneCtx);\n\n if (!data || !selectedProperty || !stats) return;\n\n const { autoCenter = true, opacity = 1.0 } = options;\n\n let rawMinX = Infinity, rawMaxX = -Infinity;\n let rawMinY = Infinity, rawMaxY = -Infinity;\n let rawMinZ = Infinity, rawMaxZ = -Infinity;\n\n data.forEach((row) => {\n const x = Number(row.x ?? row.center_x ?? 0);\n const y = Number(row.y ?? row.center_y ?? 0);\n const z = Number(row.z ?? row.center_z ?? 0);\n const dx = Number(row.dx ?? row.size_x ?? 1);\n const dy = Number(row.dy ?? row.size_y ?? 1);\n const dz = Number(row.dz ?? row.size_z ?? 1);\n rawMinX = Math.min(rawMinX, x - dx / 2);\n rawMaxX = Math.max(rawMaxX, x + dx / 2);\n rawMinY = Math.min(rawMinY, y - dy / 2);\n rawMaxY = Math.max(rawMaxY, y + dy / 2);\n rawMinZ = Math.min(rawMinZ, z - dz / 2);\n rawMaxZ = Math.max(rawMaxZ, z + dz / 2);\n });\n\n let offX = 0, offY = 0, offZ = 0;\n if (options.offset) {\n offX = Number(options.offset.x ?? 0);\n offY = Number(options.offset.y ?? 0);\n offZ = Number(options.offset.z ?? 0);\n } else if (autoCenter) {\n offX = -((rawMinX + rawMaxX) / 2);\n offY = -((rawMinY + rawMaxY) / 2);\n offZ = -((rawMinZ + rawMaxZ) / 2);\n }\n\n const minX = rawMinX + offX, maxX = rawMaxX + offX;\n const minY = rawMinY + offY, maxY = rawMaxY + offY;\n const minZ = rawMinZ + offZ, maxZ = rawMaxZ + offZ;\n\n const bkey = (x, y, z) => `${Math.round(x)},${Math.round(y)},${Math.round(z)}`;\n const blockSet = new Set(\n data.map(row => bkey(Number(row.x ?? 0), Number(row.y ?? 0), Number(row.z ?? 0)))\n );\n\n const positions = [];\n const normals = [];\n const colors = [];\n const indices = [];\n const quadToBlock = [];\n let vi = 0;\n\n data.forEach((row) => {\n const bx = Number(row.x ?? row.center_x ?? 0);\n const by = Number(row.y ?? row.center_y ?? 0);\n const bz = Number(row.z ?? row.center_z ?? 0);\n const dx = Number(row.dx ?? row.size_x ?? 1);\n const dy = Number(row.dy ?? row.size_y ?? 1);\n const dz = Number(row.dz ?? row.size_z ?? 1);\n const cx = bx + offX, cy = by + offY, cz = bz + offZ;\n\n const color = getColorForValue(row[selectedProperty], stats, THREE);\n const { r, g, b } = color;\n\n FACE_DEFS.forEach((face) => {\n const nbx = bx + face.neibDir[0] * dx;\n const nby = by + face.neibDir[1] * dy;\n const nbz = bz + face.neibDir[2] * dz;\n if (blockSet.has(bkey(nbx, nby, nbz))) return;\n\n const vBase = vi;\n face.verts.forEach(([sx, sy, sz]) => {\n positions.push(cx + sx * dx / 2, cy + sy * dy / 2, cz + sz * dz / 2);\n normals.push(face.normal[0], face.normal[1], face.normal[2]);\n colors.push(r, g, b);\n vi++;\n });\n indices.push(vBase, vBase + 1, vBase + 2, vBase, vBase + 2, vBase + 3);\n quadToBlock.push(row);\n });\n });\n\n if (positions.length === 0) return;\n\n const geometry = new THREE.BufferGeometry();\n geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));\n geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));\n geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));\n geometry.setIndex(indices);\n\n const material = new THREE.MeshLambertMaterial({\n vertexColors: true,\n transparent: opacity < 1,\n opacity,\n side: THREE.DoubleSide,\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.userData._isMergedBlocks = true;\n mesh.userData._quadToBlock = quadToBlock;\n mesh.userData._offset = { x: offX, y: offY, z: offZ };\n sceneCtx.scene.add(mesh);\n sceneCtx.blocks.push(mesh);\n syncSelectables(sceneCtx);\n\n if (sceneCtx.camera && sceneCtx.controls) {\n sceneCtx.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n fitCameraToBounds(sceneCtx, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n}\n\n/**\n * Remove all block meshes (and the highlight ghost mesh) from the scene.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function clearBlocks(sceneCtx) {\n sceneCtx.blocks.forEach((block) => {\n sceneCtx.scene.remove(block);\n block.geometry.dispose();\n block.material.dispose();\n });\n sceneCtx.blocks = [];\n if (sceneCtx._blockHighlightMesh) {\n sceneCtx.scene?.remove(sceneCtx._blockHighlightMesh);\n sceneCtx._blockHighlightMesh.geometry.dispose();\n sceneCtx._blockHighlightMesh.material.dispose();\n sceneCtx._blockHighlightMesh = null;\n }\n syncSelectables(sceneCtx);\n}\n\n/**\n * Update the opacity of all currently rendered blocks.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {number} opacity - New opacity value between 0 and 1\n */\nexport function setBlockOpacity(sceneCtx, opacity) {\n const clamped = Math.max(0, Math.min(1, Number(opacity)));\n sceneCtx.blocks.forEach((block) => {\n if (block.material) {\n block.material.opacity = clamped;\n block.material.transparent = clamped < 1;\n block.material.needsUpdate = true;\n }\n });\n}\n\n/**\n * Return (creating on first use) an invisible ghost box mesh positioned and\n * scaled to exactly cover one block. The OutlinePass uses its geometry\n * shape to draw the per-block glow; the box itself is invisible to the\n * normal render.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n * @param {object} blockRow - Block data row\n * @param {object} offset - Scene offset {x, y, z}\n * @returns {THREE.Mesh}\n */\nexport function getBlockHighlightMesh(sceneCtx, blockRow, offset) {\n const offX = offset?.x ?? 0;\n const offY = offset?.y ?? 0;\n const offZ = offset?.z ?? 0;\n const cx = Number(blockRow.x ?? blockRow.center_x ?? 0) + offX;\n const cy = Number(blockRow.y ?? blockRow.center_y ?? 0) + offY;\n const cz = Number(blockRow.z ?? blockRow.center_z ?? 0) + offZ;\n const dx = Number(blockRow.dx ?? blockRow.size_x ?? 1);\n const dy = Number(blockRow.dy ?? blockRow.size_y ?? 1);\n const dz = Number(blockRow.dz ?? blockRow.size_z ?? 1);\n if (!sceneCtx._blockHighlightMesh) {\n const geom = new THREE.BoxGeometry(1, 1, 1);\n const mat = new THREE.MeshBasicMaterial({ transparent: true, opacity: 0, depthWrite: false });\n sceneCtx._blockHighlightMesh = new THREE.Mesh(geom, mat);\n sceneCtx.scene.add(sceneCtx._blockHighlightMesh);\n }\n sceneCtx._blockHighlightMesh.position.set(cx, cy, cz);\n sceneCtx._blockHighlightMesh.scale.set(dx, dy, dz);\n return sceneCtx._blockHighlightMesh;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { applySelection } from './selectionGlow.js';\nimport { getBlockHighlightMesh } from './blockModelScene.js';\n\n/**\n * Raycast against `selectables` using the current pointer position and apply\n * the glow to the nearest hit object (or clear if nothing is hit).\n * For merged block meshes, redirects the glow to an invisible ghost box\n * sized and positioned to match only the hovered block.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function updateSelectionFromPointer(sceneCtx) {\n if (!sceneCtx._outlinePass || sceneCtx.selectables.length === 0) {\n if (sceneCtx._outlinePass) applySelection(sceneCtx, null);\n return;\n }\n const hits = sceneCtx.raycaster.intersectObjects(sceneCtx.selectables, true);\n if (hits.length === 0) {\n applySelection(sceneCtx, null);\n return;\n }\n const hit = hits[0];\n const obj = hit.object;\n if (obj?.userData?._isMergedBlocks) {\n const quadIndex = Math.floor(hit.faceIndex / 2);\n const blockRow = obj.userData._quadToBlock?.[quadIndex];\n if (blockRow) {\n applySelection(sceneCtx, getBlockHighlightMesh(sceneCtx, blockRow, obj.userData._offset));\n return;\n }\n }\n applySelection(sceneCtx, obj);\n}\n\n/**\n * Register a click listener on the renderer canvas. Handles block →\n * structural → drillhole priority. Stores the listener reference in\n * `sceneCtx.handleCanvasClick` for later removal in `dispose()`.\n *\n * @param {object} sceneCtx - Baselode3DScene instance\n */\nexport function attachCanvasClickHandler(sceneCtx) {\n const renderer = sceneCtx.renderer;\n if (!renderer) return;\n\n sceneCtx.handleCanvasClick = (event) => {\n if (event.button !== 0) return; // left click only\n\n // Ignore clicks inside the gizmo area\n if (sceneCtx.gizmo?.domElement) {\n const gizmoRect = sceneCtx.gizmo.domElement.getBoundingClientRect();\n if (\n event.clientX >= gizmoRect.left &&\n event.clientX <= gizmoRect.right &&\n event.clientY >= gizmoRect.top &&\n event.clientY <= gizmoRect.bottom\n ) {\n return;\n }\n }\n\n const rect = renderer.domElement.getBoundingClientRect();\n const localX = event.clientX - rect.left;\n const localY = event.clientY - rect.top;\n\n sceneCtx.pointer.x = ((localX / rect.width) * 2) - 1;\n sceneCtx.pointer.y = -((localY / rect.height) * 2) + 1;\n\n sceneCtx.raycaster.setFromCamera(sceneCtx.pointer, sceneCtx.camera);\n\n // Selection glow: raycast against registered selectables\n updateSelectionFromPointer(sceneCtx);\n\n // Check block clicks first (blocks take priority over drillholes)\n if (sceneCtx.blocks.length > 0) {\n const blockIntersects = sceneCtx.raycaster.intersectObjects(sceneCtx.blocks, false);\n if (blockIntersects.length > 0) {\n const hit = blockIntersects[0];\n const blockObj = hit.object;\n if (blockObj?.userData?._isMergedBlocks && sceneCtx.blockClickHandler) {\n const quadIndex = Math.floor(hit.faceIndex / 2);\n const blockData = blockObj.userData._quadToBlock[quadIndex];\n if (blockData) sceneCtx.blockClickHandler(blockData);\n }\n return;\n }\n }\n\n // Fall through to drillhole / structural click detection\n const drillHits = sceneCtx.raycaster.intersectObjects(sceneCtx.drillMeshes, true);\n const structHits = sceneCtx.raycaster.intersectObjects(sceneCtx.structuralMeshes, true);\n\n const drillDist = drillHits[0]?.distance ?? Infinity;\n const structDist = structHits[0]?.distance ?? Infinity;\n\n if (structDist < drillDist && structHits.length > 0) {\n const mesh = structHits[0].object;\n if (sceneCtx.drillholeClickHandler) {\n sceneCtx.drillholeClickHandler({ type: 'structure', ...mesh.userData });\n }\n return;\n }\n\n if (drillHits.length === 0) return;\n let obj = drillHits[0].object;\n while (obj && obj.parent && !obj.userData?.holeId) {\n obj = obj.parent;\n }\n const holeId = obj?.userData?.holeId;\n const project = obj?.userData?.project;\n if (holeId && sceneCtx.drillholeClickHandler) {\n sceneCtx.drillholeClickHandler({ holeId, project });\n }\n };\n\n renderer.domElement.addEventListener('click', sceneCtx.handleCanvasClick);\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';\nimport { FlyControls } from 'three/examples/jsm/controls/FlyControls';\nimport { ViewportGizmo } from 'three-viewport-gizmo';\nimport {\n buildViewSignature,\n emitViewChangeIfNeeded,\n fitCameraToBounds,\n focusOnLastBounds,\n getViewState,\n lookDown,\n pan,\n dolly,\n recenterCameraToOrigin,\n setControlMode,\n setFov,\n setViewState\n} from './baselode3dCameraControls.js';\nimport {\n initSelectionGlow,\n resizeGlow,\n applySelection,\n disposeSelectionGlow\n} from './selectionGlow.js';\nimport { setDrillholes as _setDrillholes, clearDrillholes as _clearDrillholes } from './drillholeScene.js';\nimport { setStripLogs as _setStripLogs, clearStripLogs as _clearStripLogs } from './stripLogScene.js';\nimport { setBlocks as _setBlocks, clearBlocks as _clearBlocks, setBlockOpacity as _setBlockOpacity } from './blockModelScene.js';\nimport {\n setStructuralDiscs as _setStructuralDiscs,\n clearStructuralDiscs as _clearStructuralDiscs,\n setStructuralDiscsVisible as _setStructuralDiscsVisible\n} from './structuralScene.js';\nimport { attachCanvasClickHandler as _attachCanvasClickHandler, updateSelectionFromPointer as _updateSelectionFromPointer } from './sceneClickHandler.js';\nimport { syncSelectables } from './sceneSelectables.js';\nimport {\n addRasterOverlay as _addRasterOverlay,\n removeRasterOverlay as _removeRasterOverlay,\n setRasterOverlayOpacity as _setRasterOverlayOpacity,\n setRasterOverlayVisibility as _setRasterOverlayVisibility,\n setRasterOverlayElevation as _setRasterOverlayElevation,\n getRasterOverlay as _getRasterOverlay,\n listRasterOverlays as _listRasterOverlays,\n clearRasterOverlays as _clearRasterOverlays,\n} from './rasterOverlayScene.js';\n\n/**\n * Baselode 3D Scene Manager\n * Manages THREE.js scene for rendering drillholes and block models in 3D.\n * Supports orbit and fly camera controls, assay coloring, and interactive selection.\n *\n * Rendering logic lives in the domain-specific modules; this class is a thin\n * orchestrator that owns the WebGL context and delegates to those modules.\n */\nclass Baselode3DScene {\n constructor() {\n this.container = null;\n this.scene = null;\n this.camera = null;\n this.renderer = null;\n this.controls = null;\n this.flyControls = null;\n this.gizmo = null;\n this.blocks = [];\n this.drillLines = [];\n this.drillMeshes = [];\n this.structuralGroup = null;\n this.structuralMeshes = [];\n this.stripLogGroups = [];\n this.frameId = null;\n this.clock = new THREE.Clock();\n this.handleCanvasClick = null;\n this.raycaster = new THREE.Raycaster();\n this.pointer = new THREE.Vector2();\n this.drillholeClickHandler = null;\n this.blockClickHandler = null;\n this.controlMode = 'orbit';\n this._tmpDir = new THREE.Vector3();\n this.viewChangeHandler = null;\n this._lastViewSignature = '';\n this._lastViewEmitMs = 0;\n this.selectables = [];\n this._selectedObject = null;\n this._composer = null;\n this._blockHighlightMesh = null;\n this._outlinePass = null;\n this.rasterOverlays = new Map();\n }\n\n init(container) {\n if (!container) return;\n this.container = container;\n\n const width = container.clientWidth;\n const height = container.clientHeight;\n\n // Scene\n this.scene = new THREE.Scene();\n this.scene.background = new THREE.Color(0xffffff);\n\n // Camera — lower near plane allows ultra-close zoom without clipping;\n // far plane set large enough for ultra-low-FOV (near-ortho) modes where\n // the camera must retreat several hundred kilometres to show a km-scale scene.\n this.camera = new THREE.PerspectiveCamera(28, width / height, 0.001, 10_000_000);\n this.camera.up.set(0, 0, 1);\n this.camera.position.set(50, 50, 50);\n this.camera.lookAt(0, 0, 0);\n\n // Renderer\n this.renderer = new THREE.WebGLRenderer({ antialias: true });\n this.renderer.setSize(width, height);\n this.renderer.setPixelRatio(window.devicePixelRatio);\n this.renderer.autoClear = false;\n container.appendChild(this.renderer.domElement);\n\n // Lighting\n const ambientLight = new THREE.AmbientLight(0xffffff, 1.2);\n this.scene.add(ambientLight);\n const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);\n directionalLight.position.set(10, 10, 5);\n this.scene.add(directionalLight);\n\n // Axes helper\n const axesHelper = new THREE.AxesHelper(20);\n this.scene.add(axesHelper);\n\n // Orbit controls\n this.controls = new OrbitControls(this.camera, this.renderer.domElement);\n this.controls.enableDamping = false;\n this.controls.screenSpacePanning = true;\n this.controls.enableZoom = true;\n this.controls.zoomSpeed = 10;\n this.controls.minDistance = 0.0001;\n this.controls.maxDistance = 5_000_000;\n this.controls.mouseButtons = {\n LEFT: THREE.MOUSE.PAN,\n MIDDLE: THREE.MOUSE.DOLLY,\n RIGHT: THREE.MOUSE.ROTATE\n };\n this.controls.touches = {\n ONE: THREE.TOUCH.ROTATE,\n TWO: THREE.TOUCH.DOLLY_PAN\n };\n this.controls.maxPolarAngle = Math.PI;\n\n // Fly controls (disabled by default)\n this.flyControls = new FlyControls(this.camera, this.renderer.domElement);\n this.flyControls.movementSpeed = 2000;\n this.flyControls.rollSpeed = Math.PI / 12;\n this.flyControls.dragToLook = true;\n this.flyControls.enabled = false;\n\n // Viewport gizmo\n this.gizmo = new ViewportGizmo(this.camera, this.renderer, {\n container: this.container,\n placement: 'top-right',\n size: 110,\n offset: { top: 12, right: 12 },\n animated: true,\n speed: 1.5\n });\n this.gizmo.attachControls(this.controls);\n\n _attachCanvasClickHandler(this);\n\n // On macOS, Chrome latches wheel events to whichever element is under the\n // cursor at the start of a gesture. If that element is an overlaid UI panel\n // (zoom slider, controls buttons) rather than the canvas, OrbitControls never\n // receives the event. Relay any wheel event that bubbles up to the container\n // but did NOT originate from the canvas itself.\n this._wheelRelay = (e) => {\n if (e.target === this.renderer.domElement) return; // already going to OrbitControls\n e.preventDefault();\n this.renderer.domElement.dispatchEvent(new WheelEvent('wheel', {\n clientX: e.clientX,\n clientY: e.clientY,\n deltaX: e.deltaX,\n deltaY: e.deltaY,\n deltaZ: e.deltaZ,\n deltaMode: e.deltaMode,\n ctrlKey: e.ctrlKey,\n shiftKey: e.shiftKey,\n altKey: e.altKey,\n bubbles: false,\n }));\n };\n this.container.addEventListener('wheel', this._wheelRelay, { passive: false });\n\n // Selection glow post-processing\n initSelectionGlow(this);\n\n // Animation loop\n const animate = () => {\n this.frameId = requestAnimationFrame(animate);\n const delta = this.clock.getDelta();\n this.renderer.clear();\n if (this.controlMode === 'fly' && this.flyControls?.enabled) {\n this.flyControls.update(delta);\n } else if (this.controls) {\n this.controls.update();\n }\n this._emitViewChangeIfNeeded();\n if (this._composer) {\n this._composer.render(delta);\n } else {\n this.renderer.render(this.scene, this.camera);\n }\n if (this.gizmo) this.gizmo.render();\n };\n animate();\n }\n\n resize() {\n if (!this.container || !this.camera || !this.renderer) return;\n const width = this.container.clientWidth;\n const height = this.container.clientHeight;\n this.camera.aspect = width / height;\n this.camera.updateProjectionMatrix();\n this.renderer.setSize(width, height);\n if (this.gizmo) this.gizmo.update();\n resizeGlow(this, width, height);\n }\n\n dispose() {\n if (this.frameId) cancelAnimationFrame(this.frameId);\n if (this.renderer && this.handleCanvasClick) {\n this.renderer.domElement.removeEventListener('click', this.handleCanvasClick);\n }\n if (this.gizmo) {\n this.gizmo.dispose();\n this.gizmo = null;\n }\n this.viewChangeHandler = null;\n _clearBlocks(this);\n _clearDrillholes(this);\n _clearStripLogs(this);\n _clearStructuralDiscs(this);\n _clearRasterOverlays(this);\n disposeSelectionGlow(this);\n if (this.container && this._wheelRelay) {\n this.container.removeEventListener('wheel', this._wheelRelay);\n }\n if (this.controls) this.controls.dispose();\n if (this.flyControls) this.flyControls.dispose();\n if (this.renderer) {\n this.renderer.dispose();\n if (this.container && this.renderer.domElement) {\n this.container.removeChild(this.renderer.domElement);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Data renderers — delegate to domain modules\n // ---------------------------------------------------------------------------\n\n setDrillholes(holes, options = {}) { _setDrillholes(this, holes, options); }\n\n /**\n * Add floating 2D strip log panels beside drillholes in the 3D scene.\n * Each panel is a flat rectangle offset from the hole collar, depth-registered\n * to the hole's vertical extent and containing a line graph of numeric data.\n *\n * @param {Array<object>} holes - Hole objects (same array as passed to setDrillholes)\n * @param {Array<object>} stripLogs - Strip log definitions. Each must contain:\n * - `holeId` {string} — must match a hole id\n * - `depths` {number[]} — downhole depth positions for each sample\n * - `values` {number[]} — numeric value at each depth\n * - `options` {object} — optional: panelWidth, lateralOffset, color, valueMin, valueMax\n */\n setStripLogs(holes, stripLogs) { _setStripLogs(this, holes, stripLogs); }\n\n /**\n * Remove all strip log panels from the scene and free GPU resources.\n */\n clearStripLogs() { _clearStripLogs(this); }\n\n /**\n * Render block model data as a single merged mesh of exterior faces only.\n * @param {Array<Object>} data - Block rows (canonical column names)\n * @param {string} selectedProperty - Attribute column used for colouring\n * @param {Object} stats - Property statistics\n * @param {Object} [options]\n */\n setBlocks(data, selectedProperty, stats, options = {}) { _setBlocks(this, data, selectedProperty, stats, options); }\n\n /**\n * Update the opacity of all currently rendered blocks.\n * @param {number} opacity - New opacity value between 0 and 1\n */\n setBlockOpacity(opacity) { _setBlockOpacity(this, opacity); }\n\n setStructuralDiscs(structures, holes, opts = {}) { _setStructuralDiscs(this, structures, holes, opts); }\n\n setStructuralDiscsVisible(visible) { _setStructuralDiscsVisible(this, visible); }\n\n // ---------------------------------------------------------------------------\n // Click handlers\n // ---------------------------------------------------------------------------\n\n setDrillholeClickHandler(handler) {\n this.drillholeClickHandler = handler;\n }\n\n /**\n * Register a click handler for block selection.\n * @param {Function|null} handler - Callback ``(blockData) => void``, or null to clear\n */\n setBlockClickHandler(handler) {\n this.blockClickHandler = typeof handler === 'function' ? handler : null;\n }\n\n // ---------------------------------------------------------------------------\n // Camera controls — delegate to baselode3dCameraControls\n // ---------------------------------------------------------------------------\n\n setViewChangeHandler(handler) {\n this.viewChangeHandler = typeof handler === 'function' ? handler : null;\n }\n\n getViewState() { return getViewState(this); }\n setViewState(viewState) { return setViewState(this, viewState); }\n\n _buildViewSignature(viewState) { return buildViewSignature(viewState); }\n _emitViewChangeIfNeeded() { emitViewChangeIfNeeded(this); }\n\n _fitCameraToBounds({ minX, maxX, minY, maxY, minZ, maxZ }) {\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n\n recenterCameraToOrigin(distance = 1000) { recenterCameraToOrigin(this, distance); }\n lookDown(distance = 2000) { lookDown(this, distance); }\n pan(dx = 0, dy = 0) { pan(this, dx, dy); }\n dolly(scale = 1.1) { dolly(this, scale); }\n focusOnLastBounds(padding = 1.2) { focusOnLastBounds(this, padding); }\n\n /**\n * Change the camera field-of-view while keeping the visible scene the same apparent size.\n * @param {number} fovDeg - Desired FOV in degrees\n */\n setCameraFov(fovDeg) { setFov(this, fovDeg); }\n\n /**\n * Set the scene background colour.\n * @param {'white'|'black'} colour\n */\n setBackground(colour) {\n if (!this.scene) return;\n this.scene.background = new THREE.Color(colour === 'black' ? 0x000000 : 0xffffff);\n }\n\n setControlMode(mode = 'orbit') { setControlMode(this, mode); }\n\n // ---------------------------------------------------------------------------\n // Selection glow public API\n // ---------------------------------------------------------------------------\n\n _syncSelectables() { syncSelectables(this); }\n\n /**\n * Register the objects that are candidates for click-select glow.\n * @param {THREE.Object3D[]} objects\n */\n setSelectableObjects(objects) {\n this.selectables = Array.isArray(objects) ? objects.slice() : [];\n }\n\n /**\n * Programmatically select an object (or pass null to clear).\n * @param {THREE.Object3D|null} object\n */\n selectObject(object) { applySelection(this, object || null); }\n\n /**\n * Return the currently selected object, or null if nothing is selected.\n * @returns {THREE.Object3D|null}\n */\n getSelectedObject() { return this._selectedObject || null; }\n\n /**\n * Dispose the effect composer and all GPU resources used by the selection glow.\n */\n disposeGlow() { disposeSelectionGlow(this); }\n\n /** @private */\n _updateSelectionFromPointer() { _updateSelectionFromPointer(this); }\n\n // ---------------------------------------------------------------------------\n // Raster overlay API — delegate to rasterOverlayScene\n // ---------------------------------------------------------------------------\n\n /**\n * Add a raster overlay layer (created with createRasterOverlay) to the scene.\n * @param {object} layer - Layer descriptor returned by createRasterOverlay()\n */\n addRasterOverlay(layer) { _addRasterOverlay(this, layer); }\n\n /**\n * Remove a raster overlay from the scene and dispose its GPU resources.\n * @param {string} id - Overlay id\n */\n removeRasterOverlay(id) { _removeRasterOverlay(this, id); }\n\n /**\n * Set the opacity of a raster overlay at runtime.\n * @param {string} id - Overlay id\n * @param {number} opacity - New opacity [0, 1]\n */\n setRasterOverlayOpacity(id, opacity) { _setRasterOverlayOpacity(this, id, opacity); }\n\n /**\n * Show or hide a raster overlay.\n * @param {string} id - Overlay id\n * @param {boolean} visible\n */\n setRasterOverlayVisibility(id, visible) { _setRasterOverlayVisibility(this, id, visible); }\n\n /**\n * Update the elevation (Z position) of a raster overlay.\n * @param {string} id - Overlay id\n * @param {number} elevation\n */\n setRasterOverlayElevation(id, elevation) { _setRasterOverlayElevation(this, id, elevation); }\n\n /**\n * Return a raster overlay by id, or undefined if not found.\n * @param {string} id\n * @returns {object|undefined}\n */\n getRasterOverlay(id) { return _getRasterOverlay(this, id); }\n\n /**\n * Return all raster overlay layers in insertion order.\n * @returns {object[]}\n */\n listRasterOverlays() { return _listRasterOverlays(this); }\n}\n\nexport default Baselode3DScene;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport './Baselode3DControls.css';\n\n/**\n * 3D scene control buttons component\n * Provides UI controls for camera manipulation in the 3D drillhole viewer\n * @param {Object} props - Component props\n * @param {string} props.controlMode - Current control mode ('orbit' or 'fly')\n * @param {Function} props.onToggleFly - Handler for toggling fly mode\n * @param {Function} props.onRecenter - Handler for recentering camera\n * @param {Function} props.onLookDown - Handler for top-down view\n * @param {Function} props.onFit - Handler for fitting camera to scene\n * @returns {JSX.Element} Control buttons component\n */\nfunction Baselode3DControls({\n controlMode = 'orbit',\n onToggleFly = () => {},\n onRecenter = () => {},\n onLookDown = () => {},\n onFit = () => {},\n darkBackground = false,\n onToggleDarkBackground = () => {},\n}) {\n return (\n <div className=\"baselode-3d-controls\">\n <button type=\"button\" className=\"ghost-button\" onClick={onRecenter}>\n Recenter to (0,0,0)\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onLookDown}>\n Look down\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onFit}>\n Fit to scene\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onToggleFly}>\n {controlMode === 'orbit' ? 'Enable fly controls' : 'Disable fly controls'}\n </button>\n <label className=\"baselode-3d-controls-checkbox\">\n <input\n type=\"checkbox\"\n checked={darkBackground}\n onChange={onToggleDarkBackground}\n />\n Dark background\n </label>\n </div>\n );\n}\n\nexport default Baselode3DControls;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport './BlockModelWidget.css';\n\n/**\n * A panel of controls for exploring a block model in the 3-D scene.\n *\n * Exports widget state (attribute selection, opacity) so that a wrapping\n * application can pass the values on to the 3-D scene. Renders:\n * - An attribute (color-by) drop-down selector\n * - A translucency / opacity slider\n * - A simple color-scale legend (numeric attributes)\n * - A popup card showing all attributes of the last clicked block\n *\n * @param {Object} props\n * @param {Array<string>} props.properties - List of attribute column names\n * @param {string} props.selectedProperty - Currently selected attribute\n * @param {Function} props.onPropertyChange - Called with new property name string\n * @param {number} props.opacity - Current opacity value (0–1, default 0.85)\n * @param {Function} props.onOpacityChange - Called with new opacity number\n * @param {Object|null} props.propertyStats - Stats object for the selected\n * property (``{type, min?, max?, categories?}`` from calculatePropertyStats)\n * @param {Object|null} props.clickedBlock - Block row data to display in the\n * popup, or null when no block is selected\n * @param {Function} props.onPopupClose - Called when the user dismisses the popup\n */\nfunction BlockModelWidget({\n properties = [],\n selectedProperty = '',\n onPropertyChange = () => {},\n opacity = 0.85,\n onOpacityChange = () => {},\n propertyStats = null,\n clickedBlock = null,\n onPopupClose = () => {},\n}) {\n return (\n <div className=\"bm-widget\">\n {/* Attribute selector */}\n <label className=\"bm-widget__label\" htmlFor=\"bm-property-select\">\n Color by\n </label>\n <select\n id=\"bm-property-select\"\n className=\"bm-widget__select\"\n value={selectedProperty}\n onChange={(e) => onPropertyChange(e.target.value)}\n >\n {properties.length === 0 && (\n <option value=\"\">— no attributes —</option>\n )}\n {properties.map((p) => (\n <option key={p} value={p}>{p}</option>\n ))}\n </select>\n\n {/* Color scale legend */}\n {propertyStats && propertyStats.type === 'numeric' && (\n <div className=\"bm-widget__scale\">\n <span className=\"bm-widget__scale-label bm-widget__scale-label--min\">\n {propertyStats.min?.toFixed(2) ?? '—'}\n </span>\n <div className=\"bm-widget__scale-bar\" />\n <span className=\"bm-widget__scale-label bm-widget__scale-label--max\">\n {propertyStats.max?.toFixed(2) ?? '—'}\n </span>\n </div>\n )}\n {propertyStats && propertyStats.type === 'categorical' && (\n <div className=\"bm-widget__categories\">\n {(propertyStats.categories || []).map((cat, i) => {\n const hue = Math.round((i / Math.max(propertyStats.categories.length, 1)) * 360);\n return (\n <span key={cat} className=\"bm-widget__category-chip\"\n style={{ background: `hsl(${hue},70%,50%)` }}>\n {cat}\n </span>\n );\n })}\n </div>\n )}\n\n {/* Opacity slider */}\n <label className=\"bm-widget__label\" htmlFor=\"bm-opacity-slider\">\n Opacity ({Math.round(opacity * 100)}%)\n </label>\n <input\n id=\"bm-opacity-slider\"\n type=\"range\"\n className=\"bm-widget__slider\"\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n value={opacity}\n onChange={(e) => onOpacityChange(parseFloat(e.target.value))}\n />\n\n {/* Block attribute popup */}\n {clickedBlock && (\n <div className=\"bm-widget__popup\">\n <div className=\"bm-widget__popup-header\">\n <span>Block attributes</span>\n <button\n type=\"button\"\n className=\"bm-widget__popup-close\"\n onClick={onPopupClose}\n aria-label=\"Close\"\n >\n ×\n </button>\n </div>\n <table className=\"bm-widget__popup-table\">\n <tbody>\n {Object.entries(clickedBlock).map(([key, value]) => (\n <tr key={key}>\n <th>{key}</th>\n <td>{value === null || value === undefined ? '—' : String(value)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n );\n}\n\nexport default BlockModelWidget;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Core photo table visualization helpers.\n *\n * Provides depth-ordered layout utilities, LOD image selection, and depth\n * marker generation for the CorePhotoTable component.\n */\n\n/**\n * Default LOD (Level of Detail) breakpoints.\n *\n * At low zoom values small/thumbnail images are loaded; at high zoom values\n * full-resolution images are used. Each entry specifies the minimum zoom level\n * that activates the corresponding LOD key.\n *\n * Photo objects may supply a ``lod_urls`` map keyed by these values. If a\n * photo has no ``lod_urls``, ``image_url`` is used regardless of zoom.\n */\nexport const DEFAULT_LOD_BREAKPOINTS = [\n { minZoom: 0, lodKey: 'thumb' },\n { minZoom: 4, lodKey: 'medium' },\n { minZoom: 7, lodKey: 'full' },\n];\n\n/**\n * Base pixels-per-metre at zoom level 5. At other zoom levels this value is\n * scaled linearly so that ``pixelsPerMetre = BASE_PIXELS_PER_METRE * zoom / 5``.\n */\nexport const BASE_PIXELS_PER_METRE = 50;\n\n/**\n * Select the image URL appropriate for the given zoom level.\n *\n * If the photo object contains a ``lod_urls`` map the function returns the\n * URL for the highest LOD whose ``minZoom`` is still ≤ the current zoom\n * level. If no ``lod_urls`` are present (or the resolved key is missing)\n * the function falls back to ``photo.image_url``.\n *\n * @param {Object} photo - Photo entry. Expected fields: ``image_url`` (string),\n * ``lod_urls`` (optional object mapping lodKey → URL string).\n * @param {number} zoom - Current zoom level (1–10).\n * @param {Array<{minZoom: number, lodKey: string}>} [lodBreakpoints] - LOD\n * breakpoints to use. Defaults to {@link DEFAULT_LOD_BREAKPOINTS}.\n * @returns {string} The selected image URL, or an empty string if none found.\n */\nexport function selectPhotoLodUrl(photo, zoom, lodBreakpoints = DEFAULT_LOD_BREAKPOINTS) {\n const lodUrls = photo.lod_urls;\n if (!lodUrls || typeof lodUrls !== 'object' || Array.isArray(lodUrls)) {\n return photo.image_url || '';\n }\n\n // Walk breakpoints and keep the last one whose minZoom ≤ zoom.\n let selectedKey = lodBreakpoints[0]?.lodKey ?? 'thumb';\n for (const bp of lodBreakpoints) {\n if (zoom >= bp.minZoom) {\n selectedKey = bp.lodKey;\n }\n }\n\n return lodUrls[selectedKey] || photo.image_url || '';\n}\n\n/**\n * Return a copy of ``photos`` sorted ascending by ``from_depth``.\n *\n * @param {Array<Object>} photos - Array of photo objects, each with a\n * numeric ``from_depth`` field.\n * @returns {Array<Object>} New sorted array.\n */\nexport function sortPhotosByDepth(photos) {\n return [...photos].sort((a, b) => (a.from_depth ?? 0) - (b.from_depth ?? 0));\n}\n\n/**\n * Group photos by their ``photo_set`` value.\n *\n * Photos without a ``photo_set`` (null, undefined, or empty string) are\n * placed in the ``'default'`` group. The returned object's keys preserve\n * insertion order (first-seen order within ``photos``).\n *\n * @param {Array<Object>} photos - Array of photo objects.\n * @returns {Object.<string, Array<Object>>} Map of set name → photo array.\n */\nexport function groupPhotosBySet(photos) {\n /** @type {Object.<string, Array<Object>>} */\n const sets = {};\n for (const photo of photos) {\n const key =\n photo.photo_set != null && photo.photo_set !== ''\n ? String(photo.photo_set)\n : 'default';\n if (!sets[key]) sets[key] = [];\n sets[key].push(photo);\n }\n return sets;\n}\n\n/**\n * Build an array of depth marker descriptors at regular intervals.\n *\n * Markers start at the first multiple of ``intervalMetres`` that is ≥\n * ``minDepth`` and continue up to and including ``maxDepth``.\n *\n * @param {number} minDepth - Shallowest depth of the view (m).\n * @param {number} maxDepth - Deepest depth of the view (m).\n * @param {number} [intervalMetres=10] - Spacing between markers in metres.\n * @returns {Array<{depth: number, label: string}>} Ordered marker descriptors.\n */\nexport function buildDepthMarkers(minDepth, maxDepth, intervalMetres = 10) {\n const markers = [];\n if (minDepth >= maxDepth || intervalMetres <= 0) return markers;\n\n const first = Math.ceil(minDepth / intervalMetres) * intervalMetres;\n // Guard against floating-point drift with a small epsilon.\n const epsilon = intervalMetres * 1e-9;\n for (let d = first; d <= maxDepth + epsilon; d += intervalMetres) {\n const rounded = Math.round(d * 1e6) / 1e6;\n markers.push({ depth: rounded, label: `${rounded} m` });\n }\n return markers;\n}\n\n/**\n * Choose an appropriate depth-marker interval (in metres) for the given zoom\n * level so that labels are legible without overcrowding.\n *\n * @param {number} zoom - Current zoom level (1–10).\n * @returns {number} Interval in metres.\n */\nexport function depthMarkerInterval(zoom) {\n if (zoom >= 9) return 1;\n if (zoom >= 7) return 2;\n if (zoom >= 5) return 5;\n if (zoom >= 3) return 10;\n return 20;\n}\n\n/**\n * Default filename generator used by {@link buildTrayPhotos}.\n *\n * @param {number} index - Zero-based tray index.\n * @returns {string}\n */\nexport function defaultTrayFilename(index) {\n return `tray_${String(index).padStart(3, '0')}.jpg`;\n}\n\n/**\n * Build a CorePhotoTable-compatible photos array from tray depth intervals\n * and a pair of image base URLs.\n *\n * This is the same transform performed internally by CorePhotoViewer; it is\n * exported so callers that need to combine multiple datasets (e.g. two\n * concurrent scan runs for the same hole) can build the array themselves and\n * pass it directly to CorePhotoTable.\n *\n * @param {string} holeId - Drillhole identifier stored on every photo entry.\n * @param {Array<{\n * fromDepth: number,\n * toDepth: number,\n * filename?: string,\n * photoSet?: string\n * }>} trays - One entry per image, in any depth order.\n * @param {string} thumbBaseUrl - URL prefix for thumbnail images.\n * @param {string} fullBaseUrl - URL prefix for full-resolution images.\n * @param {string} [photoSet='Tray Images'] - Default column label; overridden\n * per-tray via ``tray.photoSet``.\n * @param {function} [getFilename] - ``(index: number) => string`` filename\n * generator. ``index`` is the zero-based position in ``trays``. Defaults to\n * {@link defaultTrayFilename} (``tray_000.jpg``, ``tray_001.jpg``, …).\n * @returns {Array<Object>} Photos array suitable for ``CorePhotoTable.photos``.\n */\nexport function buildTrayPhotos(\n holeId,\n trays,\n thumbBaseUrl,\n fullBaseUrl,\n photoSet = 'Tray Images',\n getFilename = defaultTrayFilename,\n) {\n const thumb = (thumbBaseUrl ?? '').replace(/\\/$/, '');\n const full = (fullBaseUrl ?? '').replace(/\\/$/, '');\n return trays.map((tray, index) => {\n const filename = tray.filename ?? getFilename(index);\n const set = tray.photoSet ?? photoSet;\n return {\n hole_id: holeId,\n from_depth: tray.fromDepth,\n to_depth: tray.toDepth,\n photo_set: set,\n image_url: `${thumb}/${filename}`,\n lod_urls: {\n thumb: `${thumb}/${filename}`,\n full: `${full}/${filename}`,\n },\n };\n });\n}\n\n/**\n * Convert a depth interval in metres to a pixel height for the given zoom.\n *\n * Uses a linear scale so that the displayed height is always proportional to\n * the physical depth span, enabling correct alignment across photo columns.\n *\n * @param {number} depthInterval - Depth span in metres (``to_depth - from_depth``).\n * @param {number} zoom - Current zoom level (1–10).\n * @param {number} [basePixelsPerMetre] - Pixels per metre at zoom 5.\n * Defaults to {@link BASE_PIXELS_PER_METRE}.\n * @returns {number} Pixel height (always ≥ 1).\n */\nexport function depthIntervalToPixels(depthInterval, zoom, basePixelsPerMetre = BASE_PIXELS_PER_METRE) {\n const scale = zoom / 5;\n return Math.max(1, Math.round(depthInterval * basePixelsPerMetre * scale));\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport {\n BASE_PIXELS_PER_METRE,\n buildDepthMarkers,\n depthMarkerInterval,\n groupPhotosBySet,\n selectPhotoLodUrl,\n sortPhotosByDepth,\n} from './corePhotoViz.js';\nimport './CorePhotoTable.css';\n\nconst BASE_ZOOM = 5; // fixed layout zoom (no reflow on pan/zoom)\nconst ZOOM_FACTOR = 1.12; // scale multiplier per wheel tick\nconst SCALE_MIN = 0.05;\nconst SCALE_MAX = 40;\n\n/**\n * CorePhotoTable\n *\n * Renders a pannable/zoomable core photograph table with:\n *\n * - Depth-ordered images stacked from shallow → deep\n * - Multiple photo sets displayed side-by-side (one column per set)\n * - A depth ruler for spatial orientation\n * - Mouse-wheel zoom centred on cursor (Leaflet-style)\n * - Left-click drag to pan\n *\n * @param {Object} props\n * @param {Array<CorePhoto>} [props.photos=[]] - Photo entries to display.\n * Each entry should have: ``hole_id`` (string), ``from_depth`` (number),\n * ``to_depth`` (number), ``image_url`` (string), ``photo_set`` (string,\n * optional), ``lod_urls`` (object, optional).\n * @param {string} [props.holeId=''] - Hole identifier shown in the control bar.\n * @param {number} [props.initialZoom=5] - Starting zoom level (1–10) for LOD.\n */\nexport function CorePhotoTable({\n photos = [],\n holeId = '',\n initialZoom = 5,\n transform: controlledTransform,\n onTransformChange,\n}) {\n // ── Pan / zoom transform ────────────────────────────────────────────────\n // Supports both uncontrolled (internal state) and controlled modes.\n // Pass transform + onTransformChange to share state across multiple tables.\n const [internalTransform, setInternalTransform] = useState({ scale: 1, tx: 0, ty: 0 });\n const transform = controlledTransform ?? internalTransform;\n const transformRef = useRef(transform);\n transformRef.current = transform;\n\n // Always resolves functional updaters to a plain object before dispatching,\n // so onTransformChange is always called with a transform object, never a function.\n const setTransform = useCallback(\n (updater) => {\n const next = typeof updater === 'function' ? updater(transformRef.current) : updater;\n if (onTransformChange) {\n onTransformChange(next);\n } else {\n setInternalTransform(next);\n }\n },\n [onTransformChange],\n );\n\n const [dragging, setDragging] = useState(false);\n const dragOrigin = useRef(null);\n\n const viewportRef = useRef(null);\n\n // ── Derived layout values (fixed at BASE_ZOOM, no reflow on pan/zoom) ──\n\n const sorted = useMemo(() => sortPhotosByDepth(photos), [photos]);\n const grouped = useMemo(() => groupPhotosBySet(sorted), [sorted]);\n\n // Derive column order from the original (unsorted) photos so that callers\n // control left-to-right ordering by the sequence they pass photos in.\n const setNames = useMemo(() => {\n const seen = new Set();\n for (const p of photos) {\n const key = p.photo_set != null && p.photo_set !== '' ? String(p.photo_set) : 'default';\n seen.add(key);\n }\n return [...seen];\n }, [photos]);\n\n const { minDepth, maxDepth } = useMemo(() => {\n if (!sorted.length) return { minDepth: 0, maxDepth: 0 };\n const allTo = sorted.map((p) => p.to_depth ?? p.from_depth ?? 0);\n return {\n minDepth: sorted[0].from_depth ?? 0,\n maxDepth: Math.max(...allTo),\n };\n }, [sorted]);\n\n const pixelsPerMetre = (BASE_PIXELS_PER_METRE * BASE_ZOOM) / 5;\n\n const totalHeight = useMemo(\n () => Math.max(1, Math.round((maxDepth - minDepth) * pixelsPerMetre)),\n [minDepth, maxDepth, pixelsPerMetre],\n );\n\n const markers = useMemo(() => {\n const interval = depthMarkerInterval(BASE_ZOOM);\n return buildDepthMarkers(minDepth, maxDepth, interval);\n }, [minDepth, maxDepth]);\n\n const imageWidth = (540 * BASE_ZOOM) / 5; // fixed 540 px at base zoom\n\n // Effective LOD zoom derived from visual scale so thumbnails swap to full\n // resolution when the user zooms in close enough.\n const lodZoom = useMemo(\n () => Math.max(1, Math.min(10, Math.round(initialZoom * transform.scale))),\n [initialZoom, transform.scale],\n );\n\n // ── Wheel zoom (non-passive, centred on cursor) ─────────────────────────\n\n const handleWheel = useCallback((e) => {\n e.preventDefault();\n const factor = e.deltaY < 0 ? ZOOM_FACTOR : 1 / ZOOM_FACTOR;\n const rect = viewportRef.current.getBoundingClientRect();\n const cx = e.clientX - rect.left;\n const cy = e.clientY - rect.top;\n\n setTransform((prev) => {\n const newScale = Math.max(SCALE_MIN, Math.min(SCALE_MAX, prev.scale * factor));\n const ratio = newScale / prev.scale;\n return {\n scale: newScale,\n tx: cx - (cx - prev.tx) * ratio,\n ty: cy - (cy - prev.ty) * ratio,\n };\n });\n }, [setTransform]);\n\n useEffect(() => {\n const el = viewportRef.current;\n if (!el) return;\n el.addEventListener('wheel', handleWheel, { passive: false });\n return () => el.removeEventListener('wheel', handleWheel);\n }, [handleWheel]);\n\n // ── Left-drag pan ───────────────────────────────────────────────────────\n\n const handleMouseDown = useCallback((e) => {\n if (e.button !== 0) return;\n e.preventDefault();\n dragOrigin.current = {\n x: e.clientX,\n y: e.clientY,\n tx: transformRef.current.tx,\n ty: transformRef.current.ty,\n };\n setDragging(true);\n }, []);\n\n useEffect(() => {\n const handleMouseMove = (e) => {\n if (!dragOrigin.current) return;\n // Snapshot ref values before entering the async updater — the ref may\n // be nulled by handleMouseUp before React executes the updater.\n const { tx, ty, x, y } = dragOrigin.current;\n setTransform((prev) => ({\n ...prev,\n tx: tx + (e.clientX - x),\n ty: ty + (e.clientY - y),\n }));\n };\n const handleMouseUp = () => {\n dragOrigin.current = null;\n setDragging(false);\n };\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n return () => {\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n };\n }, [setTransform]);\n\n // ── Render ──────────────────────────────────────────────────────────────\n\n return (\n <div className=\"core-photo-table\">\n {/* Controls */}\n <div className=\"core-photo-controls\">\n {holeId && <span className=\"core-photo-hole-id\">{holeId}</span>}\n <span className=\"core-photo-zoom-label\">\n {Math.round(transform.scale * 100)}%\n </span>\n <button\n className=\"core-photo-zoom-btn\"\n onClick={() => setTransform({ scale: 1, tx: 0, ty: 0 })}\n aria-label=\"Reset view\"\n title=\"Reset view\"\n >\n ⌂\n </button>\n </div>\n\n {/* Column label strip */}\n {photos.length > 0 && (\n <div className=\"core-photo-col-headers\">\n <div className=\"core-photo-ruler-spacer\" />\n {setNames.map((name) => (\n <div\n key={name}\n className=\"core-photo-set-header\"\n style={{ width: imageWidth }}\n >\n {name}\n </div>\n ))}\n </div>\n )}\n\n {/* Body */}\n {photos.length === 0 ? (\n <div className=\"core-photo-empty\">No photos to display.</div>\n ) : (\n <div\n className={`core-photo-scroll${dragging ? ' is-dragging' : ''}`}\n ref={viewportRef}\n onMouseDown={handleMouseDown}\n >\n <div\n className=\"core-photo-inner\"\n style={{\n height: totalHeight,\n transform: `translate(${transform.tx}px, ${transform.ty}px) scale(${transform.scale})`,\n transformOrigin: '0 0',\n }}\n >\n {/* Depth ruler */}\n <div\n className=\"core-photo-depth-ruler\"\n style={{ height: totalHeight }}\n >\n {markers.map(({ depth, label }) => (\n <div\n key={depth}\n className=\"core-photo-depth-marker\"\n style={{\n top: Math.round((depth - minDepth) * pixelsPerMetre),\n }}\n >\n {label}\n </div>\n ))}\n </div>\n\n {/* Photo set columns */}\n {setNames.map((setName) => (\n <div\n key={setName}\n className=\"core-photo-col-body\"\n style={{ height: totalHeight, width: imageWidth }}\n >\n {grouped[setName].map((photo) => {\n const fromDepth = photo.from_depth ?? 0;\n const toDepth = photo.to_depth ?? fromDepth;\n const top = Math.round(\n (fromDepth - minDepth) * pixelsPerMetre,\n );\n const height = Math.max(\n 2,\n Math.round((toDepth - fromDepth) * pixelsPerMetre),\n );\n const src = selectPhotoLodUrl(photo, lodZoom);\n\n return (\n <div\n key={`${photo.hole_id ?? ''}-${fromDepth}-${toDepth}-${setName}`}\n className=\"core-photo-item\"\n style={{ top, height, width: imageWidth }}\n title={`${fromDepth}–${toDepth} m`}\n >\n {src ? (\n <img\n src={src}\n alt={`Core ${fromDepth}–${toDepth} m`}\n loading=\"lazy\"\n />\n ) : (\n <div className=\"core-photo-no-image\" />\n )}\n {height >= 18 && (\n <span className=\"core-photo-item-label\">\n {fromDepth}–{toDepth} m\n </span>\n )}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nexport default CorePhotoTable;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useMemo } from 'react';\nimport CorePhotoTable from './CorePhotoTable.jsx';\nimport { buildTrayPhotos, defaultTrayFilename } from './corePhotoViz.js';\n\n/**\n * CorePhotoViewer\n * ===============\n *\n * A self-contained, pannable/zoomable core box photograph viewer for use in\n * any React application. Point it at a sequence of tray images (e.g. from an\n * NVCL HyLogger dataset) and it renders them depth-aligned with a ruler,\n * mouse-wheel zoom, and left-drag panning.\n *\n *\n * ## Minimal usage\n *\n * ```jsx\n * import { CorePhotoViewer } from 'baselode';\n * import 'baselode/style.css'; // required\n *\n * <div style={{ width: '100%', height: '80vh' }}>\n * <CorePhotoViewer\n * holeId=\"09RTD001\"\n * trays={[\n * { fromDepth: 57.103, toDepth: 60.603 },\n * { fromDepth: 60.603, toDepth: 64.003 },\n * // … one entry per image, in ascending depth order\n * ]}\n * thumbBaseUrl=\"/data/09RTD001/thumb\"\n * fullBaseUrl=\"/data/09RTD001/full\"\n * />\n * </div>\n * ```\n *\n *\n * ## What you must supply\n *\n * ### 1 · Tray depth intervals (`trays`)\n *\n * An array of objects, one per image file, each containing the downhole depth\n * range (metres) that the tray covers:\n *\n * ```js\n * [\n * { fromDepth: 57.103, toDepth: 60.603 },\n * { fromDepth: 60.603, toDepth: 64.003 },\n * ]\n * ```\n *\n * **Where to get these values:**\n * - Core scanning system export (HyLogger, Corescan, etc.) — usually a CSV\n * with from/to depths per tray.\n * - LIMS or core database — query from/to depths for the hole ordered by depth.\n * - Manual entry or calculation from known tray length and start depth.\n *\n *\n * ### 2 · Image directories (`thumbBaseUrl` / `fullBaseUrl`)\n *\n * Two URL prefixes pointing to directories that contain the image files:\n *\n * | Prop | Purpose | Typical size |\n * |----------------|-------------------------------------------|----------------|\n * | `thumbBaseUrl` | Low-resolution thumbnails — always loaded | ~10–50 KB each |\n * | `fullBaseUrl` | High-resolution originals — loaded on zoom| 1–10 MB each |\n *\n * If you only have one resolution pass the same URL for both props.\n *\n * The directories must be **browser-accessible** — e.g.:\n * - Served from your web server's `public/` folder\n * - An S3 bucket / CDN with CORS enabled (`Access-Control-Allow-Origin: *`)\n * - A Vercel / Netlify static asset path\n *\n * The viewer never loads all full-res images at once; it swaps to full\n * resolution only when the user zooms in past a threshold.\n *\n *\n * ### 3 · Container size\n *\n * The component fills 100 % of its parent's width and height. Wrap it in a\n * `<div>` with an explicit height (e.g. `height: '80vh'`).\n *\n *\n * ## Filename conventions\n *\n * By default filenames are generated as `tray_000.jpg`, `tray_001.jpg`, …\n * (zero-padded three-digit index, 0-based).\n *\n * Override per-tray via `tray.filename`:\n * ```js\n * { fromDepth: 3.4, toDepth: 6.8, filename: 'DDH001_box002.jpg' }\n * ```\n *\n * Or supply a custom generator for the whole dataset:\n * ```jsx\n * <CorePhotoViewer\n * getFilename={(index) => `box_${String(index + 1).padStart(4, '0')}.jpg`}\n * // …\n * />\n * ```\n *\n *\n * ## Multiple photo sets (columns)\n *\n * If you have parallel image sets (e.g. \"Wet\" and \"Dry\") for the same hole,\n * interleave the trays and tag each with `photoSet`:\n *\n * ```js\n * trays={[\n * { fromDepth: 0, toDepth: 3.4, photoSet: 'Wet', filename: 'wet_000.jpg' },\n * { fromDepth: 0, toDepth: 3.4, photoSet: 'Dry', filename: 'dry_000.jpg' },\n * { fromDepth: 3.4, toDepth: 6.8, photoSet: 'Wet', filename: 'wet_001.jpg' },\n * { fromDepth: 3.4, toDepth: 6.8, photoSet: 'Dry', filename: 'dry_001.jpg' },\n * ]}\n * ```\n *\n *\n * ## Interaction\n *\n * | Input | Action |\n * |------------------------|-------------------------------------|\n * | Mouse wheel | Zoom in / out (centred on cursor) |\n * | Left-click drag | Pan |\n * | ⌂ button in header | Reset to default view |\n *\n *\n * @param {Object} props\n * @param {string} [props.holeId='']\n * Drillhole identifier — shown in the header bar.\n *\n * @param {Array<{\n * fromDepth: number,\n * toDepth: number,\n * filename?: string,\n * photoSet?: string\n * }>} [props.trays=[]]\n * Tray depth intervals. `fromDepth` / `toDepth` in metres downhole.\n * Optional `filename` overrides the auto-generated name for that tray.\n * Optional `photoSet` overrides the column label for that tray.\n *\n * @param {string} [props.thumbBaseUrl='']\n * URL prefix for thumbnail images (no trailing slash needed).\n * Example: `'https://cdn.example.com/holes/09RTD001/thumb'`\n *\n * @param {string} [props.fullBaseUrl='']\n * URL prefix for full-resolution images.\n * Example: `'https://cdn.example.com/holes/09RTD001/full'`\n *\n * @param {string} [props.photoSet='Tray Images']\n * Default column label used when `tray.photoSet` is absent.\n *\n * @param {function} [props.getFilename]\n * `(index: number) => string` — custom filename generator.\n * `index` is 0-based. Defaults to `tray_NNN.jpg`.\n *\n * @param {number} [props.initialZoom=5]\n * Starting LOD level (1–10). Controls when the viewer switches from\n * thumbnails to full-res as the user zooms in. 5 is a sensible default.\n */\nexport function CorePhotoViewer({\n holeId = '',\n trays = [],\n thumbBaseUrl = '',\n fullBaseUrl = '',\n photoSet: defaultPhotoSet = 'Tray Images',\n getFilename = defaultTrayFilename,\n initialZoom = 5,\n transform,\n onTransformChange,\n}) {\n const photos = useMemo(\n () => buildTrayPhotos(holeId, trays, thumbBaseUrl, fullBaseUrl, defaultPhotoSet, getFilename),\n [holeId, trays, thumbBaseUrl, fullBaseUrl, defaultPhotoSet, getFilename],\n );\n\n return (\n <CorePhotoTable\n photos={photos}\n holeId={holeId}\n initialZoom={initialZoom}\n transform={transform}\n onTransformChange={onTransformChange}\n />\n );\n}\n\nexport default CorePhotoViewer;\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * @module grade_blocks\n *\n * Loading and Three.js rendering of 3D polygonal grade block meshes.\n *\n * A grade block is a closed polyhedral mesh defined by:\n * - An array of 3-D vertices [[x,y,z], ...]\n * - An array of triangle indices [[i,j,k], ...]\n * - Optional attributes and material hints\n *\n * Schema version \"1.0\" is the only version supported.\n */\n\nimport * as THREE from 'three';\n\n// ---------------------------------------------------------------------------\n// Data model helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse and validate a raw JSON object (or JSON string) and return a\n * GradeBlockSet plain object.\n *\n * @param {object|string} input - Parsed JSON object or a JSON string.\n * @returns {{ schema_version: string, units: string, blocks: GradeBlock[] }}\n * @throws {Error} If schema_version is not \"1.0\" or required fields are missing.\n */\nexport function loadGradeBlocksFromJson(input) {\n const data = typeof input === 'string' ? JSON.parse(input) : input;\n\n if (data.schema_version !== '1.0') {\n throw new Error(\n `Unsupported schema_version: ${JSON.stringify(data.schema_version)}. Expected \"1.0\".`\n );\n }\n\n if (!Array.isArray(data.blocks)) {\n throw new Error('\"blocks\" must be a JSON array.');\n }\n\n const blocks = data.blocks.map((raw, i) => {\n if (raw.id == null) throw new Error(`Block at index ${i} is missing required field \"id\".`);\n if (raw.name == null) throw new Error(`Block \"${raw.id}\" is missing required field \"name\".`);\n if (!Array.isArray(raw.vertices)) throw new Error(`Block \"${raw.id}\" is missing required field \"vertices\".`);\n if (!Array.isArray(raw.triangles)) throw new Error(`Block \"${raw.id}\" is missing required field \"triangles\".`);\n\n return {\n id: raw.id,\n name: raw.name,\n vertices: raw.vertices,\n triangles: raw.triangles,\n attributes: raw.attributes ?? {},\n material: raw.material ?? {},\n };\n });\n\n return {\n schema_version: data.schema_version,\n units: data.units ?? '',\n blocks,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Three.js geometry\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a grade block to a Three.js BufferGeometry.\n *\n * @param {object} block - A grade block object (from loadGradeBlocksFromJson).\n * @returns {THREE.BufferGeometry}\n */\nexport function gradeBlockToThreeGeometry(block) {\n const geometry = new THREE.BufferGeometry();\n\n // Flatten vertices: [[x,y,z], ...] -> Float32Array\n const positionData = new Float32Array(block.vertices.length * 3);\n block.vertices.forEach(([x, y, z], i) => {\n positionData[i * 3] = x;\n positionData[i * 3 + 1] = y;\n positionData[i * 3 + 2] = z;\n });\n geometry.setAttribute('position', new THREE.BufferAttribute(positionData, 3));\n\n // Flatten triangles: [[i,j,k], ...] -> Uint32Array\n const indexData = new Uint32Array(block.triangles.length * 3);\n block.triangles.forEach(([a, b, c], i) => {\n indexData[i * 3] = a;\n indexData[i * 3 + 1] = b;\n indexData[i * 3 + 2] = c;\n });\n geometry.setIndex(new THREE.BufferAttribute(indexData, 1));\n\n return geometry;\n}\n\n// ---------------------------------------------------------------------------\n// Scene helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Create Three.js meshes for all blocks and add them to the scene.\n *\n * @param {THREE.Scene} scene\n * @param {{ blocks: object[] }} blockSet - Result of loadGradeBlocksFromJson.\n * @param {object} [options]\n * @param {number} [options.defaultOpacity=1.0] - Fallback opacity when not set per block.\n * @returns {THREE.Group}\n */\nexport function addGradeBlocksToScene(scene, blockSet, options = {}) {\n const { defaultOpacity = 1.0 } = options;\n const group = new THREE.Group();\n\n blockSet.blocks.forEach((block) => {\n const geometry = gradeBlockToThreeGeometry(block);\n\n const color = block.material?.color ?? '#888888';\n const opacity = block.material?.opacity ?? defaultOpacity;\n const transparent = opacity < 1.0;\n\n const material = new THREE.MeshStandardMaterial({\n color: new THREE.Color(color),\n opacity,\n transparent,\n side: THREE.DoubleSide,\n flatShading: true,\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.userData = {\n id: block.id,\n attributes: block.attributes,\n };\n\n // Edge highlight — hidden by default, shown on selection\n const edgeGeo = new THREE.EdgesGeometry(geometry, 15);\n const edgeMat = new THREE.LineBasicMaterial({ color: '#ffffbb', linewidth: 1 });\n const edgeLines = new THREE.LineSegments(edgeGeo, edgeMat);\n edgeLines.visible = false;\n mesh.add(edgeLines);\n\n group.add(mesh);\n });\n\n scene.add(group);\n return group;\n}\n"],"names":["HOLE_ID","LATITUDE","LONGITUDE","ELEVATION","AZIMUTH","DIP","FROM","TO","MID","PROJECT_ID","EASTING","NORTHING","CRS","DEPTH","ALPHA","BETA","STRIKE","GEOLOGY_CODE","GEOLOGY_DESCRIPTION","BASELODE_DATA_MODEL_DRILL_COLLAR","BASELODE_DATA_MODEL_DRILL_SURVEY","BASELODE_DATA_MODEL_DRILL_ASSAY","BASELODE_DATA_MODEL_DRILL_GEOLOGY","BASELODE_DATA_MODEL_STRUCTURAL_POINT","BASELODE_DATA_MODEL_GEOPHYSICS","GEOPHYSICS_NULL_SENTINEL","DEFAULT_COLUMN_MAP","_COLUMN_LOOKUP","standardCol","variations","variation","normalized","normalizeFieldName","name","standardizeColumns","row","columnMap","sourceColumnMap","lookup","rawName","expectedName","normalizedKey","normalizedValue","renamed","col","value","key","mapped","standardizeRowArray","rows","ASSAY_NON_VALUE_FIELDS","DATA_LOG_PREFIX","toError","error","fallbackMessage","message","withDataErrorContext","context","baseError","wrapped","logDataWarning","logDataInfo","normalizeRow","rawRow","extractIdFields","extractInterval","holeIdRaw","holeId","project","from","to","intervalsToHole","intervals","sorted","a","b","points","iv","rest","pointData","_a","parseAssayHoleIds","file","resolve","reject","holeIds","Papa","results","hid","hasAssayValue","k","v","parseAssayHoleIdsWithAssays","byHole","parseAssayHole","config","wanted","interval","hole","parseAssaysCSV","holes","normalizeCsvRow","pickFirstPresent","keys","fallback","DEFAULT_TRACE_PLOT_COUNT","reorderHoleIds","ids","focusId","matchIdx","id","selected","_","idx","coerceChartTypeForProperty","property","chartType","categoricalProps","commentProps","numericDefaultChartType","buildTraceConfigsForHoleIds","focusedHoleId","plotCount","defaultProp","ordered","DISPLAY_NUMERIC","DISPLAY_CATEGORICAL","DISPLAY_COMMENT","DISPLAY_HIDDEN","DISPLAY_TADPOLE","CHART_OPTIONS","HIDDEN_COLUMNS","COMMENT_COLUMN_NAMES","classifyColumns","allCols","byType","hasValue","r","hasNumeric","t","getChartOptions","displayType","defaultChartType","opts","deriveAssayProps","h","numericCols","categoricalCols","commentCols","loadAssayMetadata","loadAssayHole","buildAssayState","numericProps","columnMeta","traceConfigs","loadAssayFile","state","parseSurveyCSV","err","norm","lat","toNumber","lng","surveyDepth","dip","azimuth","maxDepth","n","desurveyTraces","collars","surveys","collarByKey","c","refLat","_b","refLng","_c","_d","refMetersPerDegLat","refMetersPerDegLon","grouped","s","stations","collar","lat0","lng0","metersPerDegLat","metersPerDegLon","baseX","baseY","accX","accY","accZ","i","curr","prev","currDepth","currAzimuth","currDip","prevDepth","prevAzimuth","prevDip","deltaMD","inc1","toInclination","inc2","az1","degToRad","az2","beta","rf","dx","dy","dzDown","pointsWithGeo","p","d","dipDeg","val","incDeg","clamped","normalizeHoleIdValue","canonicalizeHoleIdRows","holeIdCol","preferred","resolved","angle","directionCosines","azRad","dipRad","ca","cb","cc","segmentDisplacement","deltaMd","az0","dip0","dip1","method","dc0","dc1","azAvg","dipAvg","dcAvg","dot","dogleg","desurvey","rowsCollars","rowsSurveys","options","step","safeStep","collarsCanonical","surveysCanonical","collarsByHole","surveysByHole","out","x","y","z","mdCursor","azPrev","dipPrev","firstRecord","s0","s1","md0","segmentSteps","mdIncrement","stepIdx","weight","azInterp","dipInterp","disp","record","minimumCurvatureDesurvey","tangentialDesurvey","balancedTangentialDesurvey","buildTraces","nearestByMeasuredDepth","traceRows","midMd","best","bestDist","md","dist","attachAssayPositions","assays","traces","assaysCanonical","tracesCanonical","tracesByHole","assay","nearest","merged","parseDrillholesCSV","order","pts","toArray","source","sortByColumns","columns","av","bv","validateNoOverlappingIntervals","label","prevToByHole","fromValue","toValue","prevTo","parseCsv","papaParseConfig","standardizeRow","loadTable","kind","loadCollars","crs","keepAll","tableOptions","standardized","hasXY","hasLatLon","result","loadSurveys","required","loadAssays","loadGeology","hasCode","hasDescription","code","description","keep","joinAssaysToTraces","onCols","keyOf","tracesByKey","trace","filterByProject","projectId","coerceNumeric","next","column","assembleDataset","geology","structures","metadata","GEOMETRY_COLS","BLOCK_COL_MAP","_blockColLookup","canon","variants","normalizeBlockRow","parseBlockModelCSV","data","propertyColumns","loadBlockModelMetadata","calculatePropertyStats","values","min","max","getBlockStats","propertyKeys","filterBlocks","criteria","condition","calculateBlockVolume","sum","dz","getColorForValue","stats","THREEInstance","range","hue","detectSchema","first","hasInterval","hasPoint","extractStructuralPoint","depth","extractStructuralInterval","mid","validateStructuralPoints","valid","errors","messages","az","parseStructuralPointsCSV","point","parseStructuralIntervalsCSV","groupRowsByHole","byId","parseStructuralCSV","schema","parsed","parseAssayCsvTextToHoles","csvText","_dip","_az","rowWithoutStructural","parseGeologyCsvText","parseUnifiedDataset","assayCsv","structuralCsv","geologyCsv","assayHoles","structuralHoles","geologyHoles","sh","existing","significantIntercepts","assayField","minGrade","minLength","fromCol","toCol","holeCol","holeIntervals","qualifying","grade","runs","currentRun","f","run","totalFrom","totalTo","totalLength","weightedSum","totalWeight","len","avgGrade","nSamples","DEPTH_ALIASES","resolveDepth","alias","detectValueColumns","skipSet","numericCount","parseGeophysicsCSV","rawRows","valueCols","samples","channels","depths","geophysicsToStripLogs","geophysicsHoles","channel","stripLogs","parseMnemonicLine","line","dotIdx","mnem","afterDot","unitMatch","unit","valueAndDesc","lastColon","splitSections","lasText","sections","current","raw","parseHeaderSection","lines","map","trimmed","parseLasFile","versionSection","wellSection","curveSection","dataSection","version","well","curveEntries","_f","_e","_h","_g","nullSentinel","_i","valueCurves","units","channelData","tokens","interpolateTrace","sortedTraceRows","targetMd","segIdx","md1","p0","p1","segLen","ddx","ddy","ddz","dlen","alphaBetaToNormal","alphaDeg","betaDeg","drillDir","betaZeroAxis","betaHandedness","D","U","dotDU","crossUD","crossUDLen","R","crossDR","crossDRLen","B","N_perp0","betaRad","cosB","sinB","dotND","crossDN","N_perp","theta","cosT","sinT","Nx","Ny","Nz","NLen","computeStructuralPositions","holeTrace","pos","nx","ny","nz","alpha","betaVal","BASELODE_TEMPLATE_NAME","BASELODE_LIGHT_TEMPLATE_NAME","BASELODE_COLORWAY","BASELODE_LIGHT","BASELODE_TEMPLATE","BASELODE_LIGHT_TEMPLATE","BASELODE_DARK","BASELODE_DARK_TEMPLATE_NAME","BASELODE_DARK_TEMPLATE","FALLBACK_COLOUR","COMMODITY_COLOURS","LITHOLOGY_COLOURS","BUILTIN_COLOUR_MAPS","getColour","colourMap","keyLower","mapKey","mapColour","resolveColourMap","nameOrMap","available","NUMERIC_LINE_COLOR","NUMERIC_MARKER_COLOR","commodityColourForProperty","token","low","colour","ERROR_COLOR","STRIPLOG_COMPACT_MARGIN","STRIPLOG_AXIS_TICK_FONT_SIZE","STRIPLOG_AXIS_TITLE_FONT_SIZE","normalizeAxisTitle","applyStriplogLayoutDefaults","layout","xTitle","yTitle","holeHasData","buildIntervalPoints","isCategorical","rawPoints","seen","fromVal","toVal","depthVal","rawVal","buildCategoricalConfig","template","safe","resolvedCmap","fallbackPalette","uniqueCategories","pickColour","cat","colorByCategory","category","seg","buildNumericConfig","color","isBar","isMarkersOnly","isLineOnly","lineColor","markerColor","baseTrace","errorConfig","buildPlotConfig","buildCategoricalStripLogConfig","categoryCol","DEFAULT_PALETTE","buildTadpoleConfig","tailScale","colorBy","palette","depthCol","dipCol","azCol","colorMap","byCat","shapes","group","length","showLegend","buildStructuralStripConfig","labelCol","records","lv","textY","texts","rec","wrapComment","text","charsPerLine","words","word","buildCommentsConfig","commentCol","bgColor","borderColor","textColor","comment","textXs","textYs","hovers","hasComment","buildStrikeDipSymbol","symbolSize","xCol","yCol","strike","strikeRad","dxS","dyS","tickLen","dxD","dyD","DEFAULT_NUMERIC_CHART_TYPE","resolveChartType","requestedChartType","chartOptions","opt","TracePlot","graph","holeOptions","propertyOptions","onConfigChange","containerRef","useRef","selectedHoleId","effectiveChartType","renderError","setRenderError","useState","useEffect","isComment","isTadpole","target","plotData","plotConfig","Plotly","resizeObserver","jsx","jsxs","e","mergeHoleSets","primary","extra","eh","buildCommentPoints","useDrillholeTraceGrid","initialFocusedHoleId","sourceFile","extraHoles","setHoles","setHoleIds","setNumericProps","setCategoricalProps","setCommentProps","setColumnMeta","setDefaultProp","setTraceConfigs","setError","setFocusedHoleId","loadingHoles","setLoadingHoles","loadedSourceFileRef","uniqueIds","newIds","orderedHoleIds","cfg","already","loading","props","configs","useMemo","labeledHoleOptions","traceGraphs","allProps","holePropertyOptions","handleConfigChange","index","patch","ASSAY_COLOR_PALETTE_10","buildEqualRangeColorScale","colors","finiteValues","binCount","bins","percentileLow","percentileHigh","idxLow","idxHigh","lower","upper","formatBinLabel","formatVal","getEqualRangeBinIndex","scale","bin","getEqualRangeColor","fallbackColor","normalizePoint","projectTraceToSection","origin","ox","oy","cosA","sinA","sectionWindow","width","projected","half","planView","depthSlice","top","bottom","sectionView","section","getHoleId","tracesAsSegments","segments","payload","intervalsAsTubes","radius","annotationsFromIntervals","syncSelectables","sceneCtx","DEFAULT_COLOR_MAP","resolveColor","structureType","hex","THREE","dipAzimuthToNormal","buildStructuralDiscs","discThickness","opacity","yAxis","xVal","yVal","zVal","dipVal","azVal","normal","geom","mat","mesh","setStructuralDiscs","clearStructuralDiscs","maxDiscs","input","sampled","enriched","child","setStructuralDiscsVisible","visible","_overlayIdCounter","normalizeBounds","bounds","minX","minY","maxX","maxY","height","loadTexture","url","createdObjectUrl","texture","createRasterOverlay","elevation","renderOrder","normalizedBounds","centerX","centerY","geometry","material","addRasterOverlay","layer","removeRasterOverlay","setRasterOverlayOpacity","setRasterOverlayVisibility","setRasterOverlayElevation","getRasterOverlay","listRasterOverlays","clearRasterOverlays","buildViewSignature","viewState","toNum","getViewState","setViewState","camera","up","emitViewChangeIfNeeded","now","signature","fitCameraToBounds","minZ","maxZ","centerZ","sizeX","sizeY","sizeZ","distance","recenterCameraToOrigin","lookDown","pan","dolly","focusOnLastBounds","padding","FOV_MIN_DEG","FOV_MAX_DEG","setFov","fovDeg","clampedFov","currentDist","currentFovRad","frustumHeight","newFovRad","newDist","dir","setControlMode","mode","LOW_ASSAY_GREY","getMeasuredDepthRange","p2","md2","segStart","segEnd","getWeightedIntervalValue","assayIntervals","weightTotal","candidate","overlapStart","overlap","getAssaySegmentColor","assayScale","colorHex","getCategoryHexColor","seededUnit","normalizeDrillholeRenderOptions","collectAssayValues","assayIntervalsByHole","selectedAssayVariable","allAssayValues","buildHoleUserData","normalizeHoleKey","randomSegmentColor","segmentIndex","seed","base","band","hash","getDominantCategory","bestOverlap","resolveAssayIntervalsForHole","exact","byNormalized","getSegmentColor","depthRange","setDrillholes","clearDrillholes","preserveView","isCategoricalVariable","tmpVec","defaultColor","sphereGeom","sphereMat","sphere","cylinderGeom","segmentColor","cylinderMat","STRIP_LOG_DEFAULT_PANEL_WIDTH","STRIP_LOG_DEFAULT_LATERAL_OFFSET","STRIP_LOG_DEFAULT_COLOR","normalizeStripLogOptions","getHoleVerticalExtent","topZ","botZ","buildStripLogLinePoints","panelWidth","panelHeight","valueMin","valueMax","depthScale","depthRef","minDepth","autoMin","autoMax","vMin","valRange","tDepth","tVal","localX","localY","buildLineRibbonGeometry","halfWidth","positions","indices","vi","buildStripLogGroup","stripLog","toe","holeDirRaw","holeLength","holeDir","lateralOffset","worldZ","lateralDir","panelNormal","traceOrigin","rotMatrix","quaternion","measuredDepths","linePoints","ribbonGeom","ribbonMat","ribbonMesh","setStripLogs","clearStripLogs","holeById","GLOW_COLOR","EDGE_STRENGTH","EDGE_THICKNESS","EDGE_GLOW","initSelectionGlow","renderer","scene","container","composer","EffectComposer","renderPass","RenderPass","resolution","outlinePass","OutlinePass","resizeGlow","applySelection","object","disposeSelectionGlow","FACE_DEFS","setBlocks","selectedProperty","clearBlocks","autoCenter","rawMinX","rawMaxX","rawMinY","rawMaxY","rawMinZ","rawMaxZ","offX","offY","offZ","bkey","blockSet","normals","quadToBlock","bx","by","bz","cx","cy","cz","g","face","nbx","nby","nbz","vBase","sx","sy","sz","block","setBlockOpacity","getBlockHighlightMesh","blockRow","offset","updateSelectionFromPointer","hits","hit","obj","quadIndex","attachCanvasClickHandler","event","gizmoRect","rect","blockIntersects","blockObj","blockData","drillHits","structHits","drillDist","Baselode3DScene","ambientLight","directionalLight","axesHelper","OrbitControls","FlyControls","ViewportGizmo","_attachCanvasClickHandler","animate","delta","_clearBlocks","_clearDrillholes","_clearStripLogs","_clearStructuralDiscs","_clearRasterOverlays","_setDrillholes","_setStripLogs","_setBlocks","_setBlockOpacity","_setStructuralDiscs","_setStructuralDiscsVisible","handler","objects","_updateSelectionFromPointer","_addRasterOverlay","_removeRasterOverlay","_setRasterOverlayOpacity","_setRasterOverlayVisibility","_setRasterOverlayElevation","_getRasterOverlay","_listRasterOverlays","Baselode3DControls","controlMode","onToggleFly","onRecenter","onLookDown","onFit","darkBackground","onToggleDarkBackground","BlockModelWidget","properties","onPropertyChange","onOpacityChange","propertyStats","clickedBlock","onPopupClose","DEFAULT_LOD_BREAKPOINTS","BASE_PIXELS_PER_METRE","selectPhotoLodUrl","photo","zoom","lodBreakpoints","lodUrls","selectedKey","bp","sortPhotosByDepth","photos","groupPhotosBySet","sets","buildDepthMarkers","intervalMetres","markers","epsilon","rounded","depthMarkerInterval","defaultTrayFilename","buildTrayPhotos","trays","thumbBaseUrl","fullBaseUrl","photoSet","getFilename","thumb","full","tray","filename","set","depthIntervalToPixels","depthInterval","basePixelsPerMetre","BASE_ZOOM","ZOOM_FACTOR","SCALE_MIN","SCALE_MAX","CorePhotoTable","initialZoom","controlledTransform","onTransformChange","internalTransform","setInternalTransform","transform","transformRef","setTransform","useCallback","updater","dragging","setDragging","dragOrigin","viewportRef","setNames","allTo","pixelsPerMetre","totalHeight","imageWidth","lodZoom","handleWheel","factor","newScale","ratio","el","handleMouseDown","handleMouseMove","tx","ty","handleMouseUp","setName","fromDepth","toDepth","src","CorePhotoViewer","defaultPhotoSet","loadGradeBlocksFromJson","blocks","gradeBlockToThreeGeometry","positionData","indexData","addGradeBlocksToScene","defaultOpacity","transparent","edgeGeo","edgeMat","edgeLines"],"mappings":"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"}
|