baselode 0.1.2 → 0.1.4
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 +1461 -1092
- package/dist/baselode.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +2 -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/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/structuralScene.js","../src/viz/baselode3dCameraControls.js","../src/viz/baselode3dScene.js","../src/viz/Baselode3DControls.jsx"],"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\";\n\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\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 [AZIMUTH]: [\"azimuth\", \"az\", \"dip_direction\", \"dipdir\", \"dip direction\", \"dipdrn\", \"dipdirection\", \"dip_dir\", \"computed_plane_azimuth\", \"calc_dipdir\", \"calc_dipdir_deg\", \"dipdrn\", \"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 [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]);\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 BASELODE_DATA_MODEL_DRILL_COLLAR,\n BASELODE_DATA_MODEL_DRILL_SURVEY,\n BASELODE_DATA_MODEL_DRILL_ASSAY\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\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 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 * 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.structures - Structural data (optional)\n * @param {Object} components.metadata - Dataset metadata (optional)\n * @returns {{collars: Array, surveys: Array, assays: Array, structures: Array, metadata: Object}} Complete dataset\n */\nexport function assembleDataset({\n collars = [],\n surveys = [],\n assays = [],\n structures = [],\n metadata = {}\n} = {}) {\n return {\n collars: toArray(collars),\n surveys: toArray(surveys),\n assays: toArray(assays),\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 * Parse block model CSV file containing 3D block/voxel data\n * Expected columns: center_x, center_y, center_z, size_x, size_y, size_z, plus property columns\n * @param {File|Blob} file - Block model CSV file\n * @returns {Promise<{data: Array<Object>, properties: Array<string>}>} Parsed blocks 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 complete: (results) => {\n const data = results.data.filter((row) =>\n row.center_x !== null &&\n row.center_y !== null &&\n row.center_z !== null\n );\n\n const excludeColumns = ['center_x', 'center_y', 'center_z', 'size_x', 'size_y', 'size_z'];\n const propertyColumns = Object.keys(data[0] || {}).filter(\n (key) => !excludeColumns.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 * Calculate statistics for a property column in block model data\n * @param {Array<Object>} data - Block model data array\n * @param {string} property - Property column name to analyze\n * @returns {{type: 'numeric'|'categorical', min?: number, max?: number, 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.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 * Generate a color for a property value based on its statistics\n * @param {*} value - Property value to colorize\n * @param {{type: 'numeric'|'categorical', min?: number, max?: number, 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; // 240 is blue, 0 is red\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 * 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 * 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 * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport async function parseUnifiedDataset({ assayCsv, structuralCsv } = {}) {\n const [assayHoles, structuralHoles] = 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 ]);\n\n // Merge holes from both sources by holeId\n const byId = new Map(assayHoles.map((h) => [h.holeId, { ...h, points: [...h.points] }]));\n for (const sh of structuralHoles) {\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// 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\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/** Color for error bars */\nexport const ERROR_COLOR = '#6b7280';\n\n/** Default compact strip-log margins */\nexport const STRIPLOG_COMPACT_MARGIN = { l: 4, r: 4, t: 4, b: 4 };\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 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}`;\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 * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildCategoricalConfig(points, property) {\n if (!points.length) return { data: [], layout: {} };\n const sorted = [...points].sort((a, b) => b.z - a.z);\n const segments = [];\n for (let i = 0; i < sorted.length; i += 1) {\n const curr = sorted[i];\n const next = sorted[i + 1];\n const y0 = curr.z;\n const y1 = next ? next.z : curr.z - 20;\n if (y1 === y0) continue;\n const catVal = curr.val == null ? '' : String(curr.val).trim();\n if (!catVal || /^(nan|null|none)$/i.test(catVal)) continue;\n segments.push({ y0, y1, category: catVal, fromVal: curr.from, toVal: curr.to });\n }\n\n const palette = ['#4e79a7', '#f28e2b', '#e15759', '#76b7b2', '#59a14f', '#edc948', '#b07aa1', '#ff9da7', '#9c755f', '#bab0ac', '#d4a6c8', '#86bcb6'];\n\n // Assign colors by category value (same category → same color, matching Python view.py)\n const uniqueCategories = [...new Set(segments.map((s) => s.category))];\n const colorMap = Object.fromEntries(\n uniqueCategories.map((cat, i) => [cat, palette[i % palette.length]])\n );\n\n const shapes = segments.map((seg) => ({\n type: 'rect',\n xref: 'x',\n yref: 'y',\n x0: 0,\n x1: 1,\n y0: seg.y0,\n y1: seg.y1,\n fillcolor: colorMap[seg.category],\n line: { width: 0 }\n }));\n\n const textTrace = {\n x: segments.map(() => 0.5),\n y: segments.map((s) => (s.y0 + s.y1) / 2),\n mode: 'text',\n text: segments.map((s) => s.category),\n textposition: 'middle center',\n showlegend: false,\n hoverinfo: 'text',\n customdata: segments.map((s) => [Math.min(s.fromVal, s.toVal), Math.max(s.fromVal, s.toVal)]),\n hovertemplate: `Category: %{text}<br>from: %{customdata[0]} to: %{customdata[1]}<extra></extra>`\n };\n\n const layout = {\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed', zeroline: false },\n shapes,\n showlegend: false,\n title: property || undefined\n };\n\n return { data: [textTrace], 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 * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildNumericConfig(points, property, chartType) {\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 baseTrace = {\n x: points.map((p) => p.val),\n y: points.map((p) => p.z),\n hovertemplate: `${property}: %{x}<br>from: %{customdata[0]} to: %{customdata[1]}<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: NUMERIC_LINE_COLOR },\n error_y: errorConfig\n }\n : {\n ...baseTrace,\n type: 'scatter',\n mode: isMarkersOnly ? 'markers' : isLineOnly ? 'lines' : 'lines+markers',\n line: { color: NUMERIC_LINE_COLOR, width: 2 },\n marker: { size: 7, color: NUMERIC_MARKER_COLOR },\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 };\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 * @returns {{data: Array, layout: Object}} Complete Plotly configuration\n */\nexport function buildPlotConfig({ points, isCategorical, property, chartType }) {\n if (!points || !points.length || !property) return { data: [], layout: {} };\n if (isCategorical || chartType === 'categorical') {\n return buildCategoricalConfig(points, property);\n }\n return buildNumericConfig(points, property, chartType);\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';\n\nconst DEFAULT_PALETTE = [\n '#0f172a', '#1e3a5f', '#7c3aed', '#dc2626', '#16a34a',\n '#d97706', '#0ea5e9', '#db2777', '#65a30d', '#9333ea',\n];\n\nconst STRIPLOG_COMPACT_MARGIN = { l: 4, r: 4, t: 4, b: 4 };\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 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 * @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} = {}) {\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 };\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 * @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} = {}) {\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 });\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 };\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 * @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} = {}) {\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 });\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}–${rec.to} m: ${rec.comment}`);\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 };\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 * @returns {JSX.Element}\n */\nfunction TracePlot({ config, graph, holeOptions = [], propertyOptions = [], onConfigChange }) {\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 });\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', 'zoom2d', 'zoomIn2d', 'zoomOut2d', '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]);\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 data 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 */\nimport * as THREE from 'three';\nimport { AZIMUTH, DIP } from '../data/datamodel.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 const dip = s[DIP] != null ? Number(s[DIP]) : null;\n const az = s[AZIMUTH] != null ? Number(s[AZIMUTH]) : null;\n\n if (xVal == null || yVal == null || zVal == null || dip == null || az == null) continue;\n if (!Number.isFinite(xVal) || !Number.isFinite(yVal) || !Number.isFinite(zVal)) continue;\n if (!Number.isFinite(dip) || !Number.isFinite(az)) continue;\n\n const normal = dipAzimuthToNormal(dip, az);\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,\n azimuth: az,\n comments: s.comments,\n };\n\n group.add(mesh);\n }\n\n return group;\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/**\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 { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';\nimport { FlyControls } from 'three/examples/jsm/controls/FlyControls';\nimport { ViewportGizmo } from 'three-viewport-gizmo';\nimport { getColorForValue } from '../data/blockModelLoader.js';\nimport { buildEqualRangeColorScale, getEqualRangeBinIndex, getEqualRangeColor } from './assayColorScale.js';\nimport {\n buildViewSignature,\n emitViewChangeIfNeeded,\n fitCameraToBounds,\n focusOnLastBounds,\n getViewState,\n lookDown,\n pan,\n dolly,\n recenterCameraToOrigin,\n setControlMode,\n setViewState\n} from './baselode3dCameraControls.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 * @private\n */\nfunction 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 * @private\n */\nfunction 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 * @private\n */\nfunction 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 * Normalize drillhole rendering options with defaults\n * @private\n */\nfunction normalizeDrillholeRenderOptions(options = {}) {\n return {\n preserveView: Boolean(options.preserveView),\n assayIntervalsByHole: options.assayIntervalsByHole || null,\n selectedAssayVariable: options.selectedAssayVariable || ''\n };\n}\n\n/**\n * Collect all numeric assay values from interval data\n * @private\n */\nfunction 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 * @private\n */\nfunction buildHoleUserData(hole) {\n return {\n holeId: hole.id,\n project: hole.project\n };\n}\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 */\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.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.controlMode = 'orbit';\n this._tmpDir = new THREE.Vector3();\n this.viewChangeHandler = null;\n this._lastViewSignature = '';\n this._lastViewEmitMs = 0;\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\n // Lower near plane to allow ultra-close zoom without clipping\n this.camera = new THREE.PerspectiveCamera(28, width / height, 0.001, 100000);\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 - balanced for clean colors without bleeding\n const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);\n this.scene.add(ambientLight);\n\n const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);\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 // Controls\n this.controls = new OrbitControls(this.camera, this.renderer.domElement);\n this.controls.enableDamping = false; // remove inertial damping for snappier response\n this.controls.screenSpacePanning = true;\n this.controls.enableZoom = true;\n this.controls.zoomSpeed = 1.2;\n this.controls.minDistance = 0.003; // 10x closer zoom\n this.controls.maxDistance = 40000;\n // Controls: left drag = pan, scroll = zoom, right drag = rotate\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, toggled on demand)\n this.flyControls = new FlyControls(this.camera, this.renderer.domElement);\n this.flyControls.movementSpeed = 2000; // faster fly speed\n this.flyControls.rollSpeed = Math.PI / 12;\n this.flyControls.dragToLook = true;\n this.flyControls.enabled = false;\n\n // Viewport gizmo (interactive axis helper)\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 this._attachCanvasClickHandler();\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 this.renderer.render(this.scene, this.camera);\n if (this.gizmo) this.gizmo.render();\n };\n animate();\n }\n\n setViewChangeHandler(handler) {\n this.viewChangeHandler = typeof handler === 'function' ? handler : null;\n }\n\n getViewState() {\n return getViewState(this);\n }\n\n setViewState(viewState) {\n return setViewState(this, viewState);\n }\n\n _buildViewSignature(viewState) {\n return buildViewSignature(viewState);\n }\n\n _emitViewChangeIfNeeded() {\n emitViewChangeIfNeeded(this);\n }\n\n _attachCanvasClickHandler() {\n const renderer = this.renderer;\n if (!renderer) return;\n\n this.handleCanvasClick = (event) => {\n if (event.button !== 0) return; // left click only\n\n // Ignore clicks inside the gizmo area to avoid conflicts\n if (this.gizmo?.domElement) {\n const gizmoRect = this.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 this.pointer.x = ((localX / rect.width) * 2) - 1;\n this.pointer.y = -((localY / rect.height) * 2) + 1;\n\n this.raycaster.setFromCamera(this.pointer, this.camera);\n const intersects = this.raycaster.intersectObjects(this.drillMeshes, true);\n if (intersects.length === 0) return;\n let obj = intersects[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 && this.drillholeClickHandler) {\n this.drillholeClickHandler({ holeId, project });\n }\n };\n\n renderer.domElement.addEventListener('click', this.handleCanvasClick);\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 }\n\n setBlocks(data, selectedProperty, stats) {\n if (!this.scene) return;\n\n this._clearBlocks();\n\n if (!data || !selectedProperty || !stats) return;\n\n let minX = Infinity, maxX = -Infinity;\n let minY = Infinity, maxY = -Infinity;\n let minZ = Infinity, maxZ = -Infinity;\n\n data.forEach((row) => {\n const {\n center_x = 0,\n center_y = 0,\n center_z = 0,\n size_x = 1,\n size_y = 1,\n size_z = 1\n } = row;\n\n minX = Math.min(minX, center_x - size_x / 2);\n maxX = Math.max(maxX, center_x + size_x / 2);\n minY = Math.min(minY, center_y - size_y / 2);\n maxY = Math.max(maxY, center_y + size_y / 2);\n minZ = Math.min(minZ, center_z - size_z / 2);\n maxZ = Math.max(maxZ, center_z + size_z / 2);\n\n const geometry = new THREE.BoxGeometry(size_x, size_y, size_z);\n const color = getColorForValue(row[selectedProperty], stats, THREE);\n const material = new THREE.MeshStandardMaterial({\n color,\n transparent: true,\n opacity: 0.7,\n side: THREE.DoubleSide\n });\n\n const block = new THREE.Mesh(geometry, material);\n block.position.set(center_x, center_y, center_z);\n this.scene.add(block);\n this.blocks.push(block);\n });\n\n if (this.camera && this.controls) {\n this.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n }\n\n setDrillholes(holes, options = {}) {\n if (!this.scene) return;\n\n this._clearDrillholes();\n if (!holes || holes.length === 0) return;\n\n const { preserveView, assayIntervalsByHole, selectedAssayVariable } = normalizeDrillholeRenderOptions(options);\n const allAssayValues = 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); // cylinder default axis\n\n holes.forEach((hole, idx) => {\n // Improved color palette: use golden angle for better distribution\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 this.scene.add(sphere);\n this.drillLines.push(sphere);\n this.drillMeshes.push(sphere);\n }\n return;\n }\n\n const group = new THREE.Group();\n group.userData = buildHoleUserData(hole);\n const assayIntervals = selectedAssayVariable\n ? this._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 = this._getSegmentColor({\n selectedAssayVariable,\n assayIntervals,\n assayScale,\n holeId: hole.id,\n segmentIndex: i,\n p1,\n p2\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 this.drillMeshes.push(mesh);\n }\n\n this.scene.add(group);\n this.drillLines.push(group);\n });\n\n if (this.camera && this.controls) {\n this.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n if (!preserveView) {\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n }\n }\n\n _getSegmentColor({ selectedAssayVariable, assayIntervals, assayScale, holeId, segmentIndex, p1, p2 }) {\n if (!selectedAssayVariable) {\n return randomSegmentColor(holeId, segmentIndex);\n }\n // Special mode: color by presence of any assay data\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 // Check for ANY overlap with assay intervals\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 const value = getWeightedIntervalValue(assayIntervals, depthRange.segStart, depthRange.segEnd);\n return getAssaySegmentColor(value, assayScale);\n }\n\n _resolveAssayIntervalsForHole(hole, assayIntervalsByHole) {\n if (!assayIntervalsByHole || !hole) return [];\n const holeId = hole.id || hole.holeId;\n if (!holeId) return [];\n\n // Try exact match first\n const exact = assayIntervalsByHole[holeId];\n if (Array.isArray(exact) && exact.length) return exact;\n\n // Try normalized (case-insensitive) match\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\n _fitCameraToBounds({ minX, maxX, minY, maxY, minZ, maxZ }) {\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n\n recenterCameraToOrigin(distance = 1000) {\n recenterCameraToOrigin(this, distance);\n }\n\n lookDown(distance = 2000) {\n lookDown(this, distance);\n }\n\n pan(dx = 0, dy = 0) {\n pan(this, dx, dy);\n }\n\n dolly(scale = 1.1) {\n dolly(this, scale);\n }\n\n focusOnLastBounds(padding = 1.2) {\n focusOnLastBounds(this, padding);\n }\n\n _clearBlocks() {\n this.blocks.forEach((block) => {\n this.scene.remove(block);\n block.geometry.dispose();\n block.material.dispose();\n });\n this.blocks = [];\n }\n\n _clearDrillholes() {\n this.drillLines.forEach((line) => {\n this.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 this.drillLines = [];\n this.drillMeshes = [];\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\n if (this.gizmo) {\n this.gizmo.dispose();\n this.gizmo = null;\n }\n\n this.viewChangeHandler = null;\n\n this._clearBlocks();\n this._clearDrillholes();\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 setDrillholeClickHandler(handler) {\n this.drillholeClickHandler = handler;\n }\n\n setControlMode(mode = 'orbit') {\n setControlMode(this, mode);\n }\n}\n\nexport default Baselode3DScene;\n\nfunction normalizeHoleKey(value) {\n return `${value ?? ''}`.trim().toLowerCase();\n}\n\nfunction 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\nfunction 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 * 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"],"names":["HOLE_ID","LATITUDE","LONGITUDE","ELEVATION","AZIMUTH","DIP","FROM","TO","MID","PROJECT_ID","EASTING","NORTHING","CRS","DEPTH","ALPHA","BETA","STRIKE","BASELODE_DATA_MODEL_DRILL_COLLAR","BASELODE_DATA_MODEL_DRILL_SURVEY","BASELODE_DATA_MODEL_DRILL_ASSAY","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","parseCsv","papaParseConfig","standardizeRow","loadTable","kind","loadCollars","crs","keepAll","tableOptions","standardized","hasXY","hasLatLon","result","loadSurveys","required","loadAssays","joinAssaysToTraces","onCols","keyOf","tracesByKey","trace","filterByProject","projectId","coerceNumeric","next","column","assembleDataset","structures","metadata","parseBlockModelCSV","data","excludeColumns","propertyColumns","calculatePropertyStats","values","min","max","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","parseUnifiedDataset","assayCsv","structuralCsv","assayHoles","structuralHoles","sh","existing","NUMERIC_LINE_COLOR","NUMERIC_MARKER_COLOR","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","segments","y0","y1","catVal","palette","uniqueCategories","colorMap","cat","shapes","seg","buildNumericConfig","isBar","isMarkersOnly","isLineOnly","baseTrace","errorConfig","buildPlotConfig","DEFAULT_PALETTE","buildTadpoleConfig","tailScale","colorBy","depthCol","dipCol","azCol","byCat","color","group","length","showLegend","buildStructuralStripConfig","labelCol","fromCol","toCol","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","label","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","payload","intervalsAsTubes","radius","annotationsFromIntervals","DEFAULT_COLOR_MAP","resolveColor","structureType","map","hex","THREE","dipAzimuthToNormal","buildStructuralDiscs","discThickness","opacity","yAxis","xVal","yVal","zVal","normal","geom","mat","mesh","buildViewSignature","viewState","toNum","_e","_f","_g","_h","_i","getViewState","setViewState","camera","up","emitViewChangeIfNeeded","now","signature","fitCameraToBounds","minX","maxX","minY","maxY","minZ","maxZ","centerX","centerY","centerZ","sizeX","sizeY","sizeZ","distance","recenterCameraToOrigin","lookDown","pan","dolly","focusOnLastBounds","padding","setControlMode","mode","LOW_ASSAY_GREY","getMeasuredDepthRange","p1","p2","md1","md2","segStart","segEnd","getWeightedIntervalValue","assayIntervals","weightedSum","weightTotal","candidate","overlapStart","overlap","getAssaySegmentColor","assayScale","colorHex","normalizeDrillholeRenderOptions","collectAssayValues","assayIntervalsByHole","selectedAssayVariable","allAssayValues","buildHoleUserData","Baselode3DScene","container","height","ambientLight","directionalLight","axesHelper","OrbitControls","FlyControls","ViewportGizmo","animate","delta","handler","renderer","event","gizmoRect","rect","localX","localY","intersects","obj","selectedProperty","center_x","center_y","center_z","size_x","size_y","size_z","geometry","material","block","preserveView","tmpVec","defaultColor","sphereGeom","sphereMat","sphere","dir","len","cylinderGeom","segmentColor","cylinderMat","segmentIndex","randomSegmentColor","depthRange","exact","normalizeHoleKey","byNormalized","line","child","seed","base","seededUnit","band","input","hash","Baselode3DControls","controlMode","onToggleFly","onRecenter","onLookDown","onFit"],"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/structuralPositions.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/structuralScene.js","../src/viz/baselode3dCameraControls.js","../src/viz/baselode3dScene.js","../src/viz/Baselode3DControls.jsx","../src/viz/BlockModelWidget.jsx"],"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\";\n\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\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 [AZIMUTH]: [\"azimuth\", \"az\", \"dip_direction\", \"dipdir\", \"dip direction\", \"dipdrn\", \"dipdirection\", \"dip_dir\", \"computed_plane_azimuth\", \"calc_dipdir\", \"calc_dipdir_deg\", \"dipdrn\", \"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 [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]);\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 BASELODE_DATA_MODEL_DRILL_COLLAR,\n BASELODE_DATA_MODEL_DRILL_SURVEY,\n BASELODE_DATA_MODEL_DRILL_ASSAY\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\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 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 * 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.structures - Structural data (optional)\n * @param {Object} components.metadata - Dataset metadata (optional)\n * @returns {{collars: Array, surveys: Array, assays: Array, structures: Array, metadata: Object}} Complete dataset\n */\nexport function assembleDataset({\n collars = [],\n surveys = [],\n assays = [],\n structures = [],\n metadata = {}\n} = {}) {\n return {\n collars: toArray(collars),\n surveys: toArray(surveys),\n assays: toArray(assays),\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 * 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 * 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 * @returns {Promise<{holes: Array<{holeId: string, points: Array<Object>}>}>}\n */\nexport async function parseUnifiedDataset({ assayCsv, structuralCsv } = {}) {\n const [assayHoles, structuralHoles] = 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 ]);\n\n // Merge holes from both sources by holeId\n const byId = new Map(assayHoles.map((h) => [h.holeId, { ...h, points: [...h.points] }]));\n for (const sh of structuralHoles) {\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 * 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// 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\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/** Color for error bars */\nexport const ERROR_COLOR = '#6b7280';\n\n/** Default compact strip-log margins */\nexport const STRIPLOG_COMPACT_MARGIN = { l: 4, r: 4, t: 4, b: 4 };\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 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}`;\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 * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildCategoricalConfig(points, property) {\n if (!points.length) return { data: [], layout: {} };\n const sorted = [...points].sort((a, b) => b.z - a.z);\n const segments = [];\n for (let i = 0; i < sorted.length; i += 1) {\n const curr = sorted[i];\n const next = sorted[i + 1];\n const y0 = curr.z;\n const y1 = next ? next.z : curr.z - 20;\n if (y1 === y0) continue;\n const catVal = curr.val == null ? '' : String(curr.val).trim();\n if (!catVal || /^(nan|null|none)$/i.test(catVal)) continue;\n segments.push({ y0, y1, category: catVal, fromVal: curr.from, toVal: curr.to });\n }\n\n const palette = ['#4e79a7', '#f28e2b', '#e15759', '#76b7b2', '#59a14f', '#edc948', '#b07aa1', '#ff9da7', '#9c755f', '#bab0ac', '#d4a6c8', '#86bcb6'];\n\n // Assign colors by category value (same category → same color, matching Python view.py)\n const uniqueCategories = [...new Set(segments.map((s) => s.category))];\n const colorMap = Object.fromEntries(\n uniqueCategories.map((cat, i) => [cat, palette[i % palette.length]])\n );\n\n const shapes = segments.map((seg) => ({\n type: 'rect',\n xref: 'x',\n yref: 'y',\n x0: 0,\n x1: 1,\n y0: seg.y0,\n y1: seg.y1,\n fillcolor: colorMap[seg.category],\n line: { width: 0 }\n }));\n\n const textTrace = {\n x: segments.map(() => 0.5),\n y: segments.map((s) => (s.y0 + s.y1) / 2),\n mode: 'text',\n text: segments.map((s) => s.category),\n textposition: 'middle center',\n showlegend: false,\n hoverinfo: 'text',\n customdata: segments.map((s) => [Math.min(s.fromVal, s.toVal), Math.max(s.fromVal, s.toVal)]),\n hovertemplate: `Category: %{text}<br>from: %{customdata[0]} to: %{customdata[1]}<extra></extra>`\n };\n\n const layout = {\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed', zeroline: false },\n shapes,\n showlegend: false,\n title: property || undefined\n };\n\n return { data: [textTrace], 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 * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildNumericConfig(points, property, chartType) {\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 baseTrace = {\n x: points.map((p) => p.val),\n y: points.map((p) => p.z),\n hovertemplate: `${property}: %{x}<br>from: %{customdata[0]} to: %{customdata[1]}<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: NUMERIC_LINE_COLOR },\n error_y: errorConfig\n }\n : {\n ...baseTrace,\n type: 'scatter',\n mode: isMarkersOnly ? 'markers' : isLineOnly ? 'lines' : 'lines+markers',\n line: { color: NUMERIC_LINE_COLOR, width: 2 },\n marker: { size: 7, color: NUMERIC_MARKER_COLOR },\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 };\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 * @returns {{data: Array, layout: Object}} Complete Plotly configuration\n */\nexport function buildPlotConfig({ points, isCategorical, property, chartType }) {\n if (!points || !points.length || !property) return { data: [], layout: {} };\n if (isCategorical || chartType === 'categorical') {\n return buildCategoricalConfig(points, property);\n }\n return buildNumericConfig(points, property, chartType);\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';\n\nconst DEFAULT_PALETTE = [\n '#0f172a', '#1e3a5f', '#7c3aed', '#dc2626', '#16a34a',\n '#d97706', '#0ea5e9', '#db2777', '#65a30d', '#9333ea',\n];\n\nconst STRIPLOG_COMPACT_MARGIN = { l: 4, r: 4, t: 4, b: 4 };\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 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 * @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} = {}) {\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 };\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 * @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} = {}) {\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 });\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 };\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 * @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} = {}) {\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 });\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}–${rec.to} m: ${rec.comment}`);\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 };\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 * @returns {JSX.Element}\n */\nfunction TracePlot({ config, graph, holeOptions = [], propertyOptions = [], onConfigChange }) {\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 });\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', 'zoom2d', 'zoomIn2d', 'zoomOut2d', '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]);\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 data 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 */\nimport * as THREE from 'three';\nimport { AZIMUTH, DIP } from '../data/datamodel.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 * 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 { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';\nimport { FlyControls } from 'three/examples/jsm/controls/FlyControls';\nimport { ViewportGizmo } from 'three-viewport-gizmo';\nimport { getColorForValue } from '../data/blockModelLoader.js';\nimport { buildEqualRangeColorScale, getEqualRangeBinIndex, getEqualRangeColor } from './assayColorScale.js';\nimport { computeStructuralPositions } from '../data/structuralPositions.js';\nimport { buildStructuralDiscs } from './structuralScene.js';\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';\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 * @private\n */\nfunction 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 * @private\n */\nfunction 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 * @private\n */\nfunction 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 * Normalize drillhole rendering options with defaults\n * @private\n */\nfunction normalizeDrillholeRenderOptions(options = {}) {\n return {\n preserveView: Boolean(options.preserveView),\n assayIntervalsByHole: options.assayIntervalsByHole || null,\n selectedAssayVariable: options.selectedAssayVariable || ''\n };\n}\n\n/**\n * Collect all numeric assay values from interval data\n * @private\n */\nfunction 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 * @private\n */\nfunction buildHoleUserData(hole) {\n return {\n holeId: hole.id,\n project: hole.project\n };\n}\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 */\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 }\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\n // Lower near plane to allow ultra-close zoom without clipping\n this.camera = new THREE.PerspectiveCamera(28, width / height, 0.001, 100000);\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 - balanced for clean colors without bleeding\n const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);\n this.scene.add(ambientLight);\n\n const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);\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 // Controls\n this.controls = new OrbitControls(this.camera, this.renderer.domElement);\n this.controls.enableDamping = false; // remove inertial damping for snappier response\n this.controls.screenSpacePanning = true;\n this.controls.enableZoom = true;\n this.controls.zoomSpeed = 1.2;\n this.controls.minDistance = 0.003; // 10x closer zoom\n this.controls.maxDistance = 40000;\n // Controls: left drag = pan, scroll = zoom, right drag = rotate\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, toggled on demand)\n this.flyControls = new FlyControls(this.camera, this.renderer.domElement);\n this.flyControls.movementSpeed = 2000; // faster fly speed\n this.flyControls.rollSpeed = Math.PI / 12;\n this.flyControls.dragToLook = true;\n this.flyControls.enabled = false;\n\n // Viewport gizmo (interactive axis helper)\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 this._attachCanvasClickHandler();\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 this.renderer.render(this.scene, this.camera);\n if (this.gizmo) this.gizmo.render();\n };\n animate();\n }\n\n setViewChangeHandler(handler) {\n this.viewChangeHandler = typeof handler === 'function' ? handler : null;\n }\n\n getViewState() {\n return getViewState(this);\n }\n\n setViewState(viewState) {\n return setViewState(this, viewState);\n }\n\n _buildViewSignature(viewState) {\n return buildViewSignature(viewState);\n }\n\n _emitViewChangeIfNeeded() {\n emitViewChangeIfNeeded(this);\n }\n\n _attachCanvasClickHandler() {\n const renderer = this.renderer;\n if (!renderer) return;\n\n this.handleCanvasClick = (event) => {\n if (event.button !== 0) return; // left click only\n\n // Ignore clicks inside the gizmo area to avoid conflicts\n if (this.gizmo?.domElement) {\n const gizmoRect = this.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 this.pointer.x = ((localX / rect.width) * 2) - 1;\n this.pointer.y = -((localY / rect.height) * 2) + 1;\n\n this.raycaster.setFromCamera(this.pointer, this.camera);\n\n // Check block clicks first (blocks are on top of drillholes in priority)\n if (this.blocks.length > 0) {\n const blockIntersects = this.raycaster.intersectObjects(this.blocks, false);\n if (blockIntersects.length > 0) {\n const hit = blockIntersects[0];\n const blockObj = hit.object;\n if (blockObj?.userData?._isMergedBlocks && this.blockClickHandler) {\n // Merged geometry: each quad = 2 triangles, so quad index = faceIndex / 2\n const quadIndex = Math.floor(hit.faceIndex / 2);\n const blockData = blockObj.userData._quadToBlock[quadIndex];\n if (blockData) this.blockClickHandler(blockData);\n }\n return; // consumed by block click\n }\n }\n\n // Fall through to drillhole click detection\n const drillHits = this.raycaster.intersectObjects(this.drillMeshes, true);\n const structHits = this.raycaster.intersectObjects(this.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 // Structural disc clicked — fire handler with structural metadata\n const mesh = structHits[0].object;\n if (this.drillholeClickHandler) {\n this.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 && this.drillholeClickHandler) {\n this.drillholeClickHandler({ holeId, project });\n }\n };\n\n renderer.domElement.addEventListener('click', this.handleCanvasClick);\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 }\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 * Accepts rows with canonical column names ``x``, ``y``, ``z``, ``dx``,\n * ``dy``, ``dz`` (produced by :func:`parseBlockModelCSV`).\n *\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 (from :func:`calculatePropertyStats`)\n * @param {Object} [options]\n * @param {Object} [options.offset] - Optional ``{x, y, z}`` translation applied\n * to all block centres before rendering. When omitted the scene defaults to\n * auto-centering by shifting the extent centre to the origin.\n * @param {number} [options.opacity=1.0] - Initial material opacity (0–1)\n * @param {boolean} [options.autoCenter=true] - If true and no offset is\n * supplied, translate block centres so the extent centre sits at the origin.\n */\n setBlocks(data, selectedProperty, stats, options = {}) {\n if (!this.scene) return;\n\n this._clearBlocks();\n\n if (!data || !selectedProperty || !stats) return;\n\n const { autoCenter = true, opacity = 1.0 } = options;\n\n // Compute raw extent from data\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 // Determine coordinate offset\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 // Translated scene extents (used for camera fit)\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 // Neighbour lookup: \"rx,ry,rz\" keyed on rounded data-space block centres.\n // A face is interior (skipped) when the neighbour centre exists in the set.\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 // Six face definitions. neibDir is used to locate the neighbour in that\n // direction. verts are ±1 scale factors of the half-extents (dx/2 etc.).\n const 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 const positions = [];\n const normals = [];\n const colors = [];\n const indices = [];\n const quadToBlock = []; // quad index → original row (for click detection)\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 // Skip face if an adjacent block occupies the neighbouring cell\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, // safe — all interior faces are already removed\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n mesh.userData._isMergedBlocks = true;\n mesh.userData._quadToBlock = quadToBlock;\n this.scene.add(mesh);\n this.blocks.push(mesh);\n\n if (this.camera && this.controls) {\n this.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n }\n\n /**\n * Update the opacity of all currently rendered blocks.\n * @param {number} opacity - New opacity value between 0 (transparent) and 1 (opaque)\n */\n setBlockOpacity(opacity) {\n const clamped = Math.max(0, Math.min(1, Number(opacity)));\n this.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 * Register a click handler for block selection.\n * The handler is called with the full block row data when a block is clicked.\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 setDrillholes(holes, options = {}) {\n if (!this.scene) return;\n\n this._clearDrillholes();\n if (!holes || holes.length === 0) return;\n\n const { preserveView, assayIntervalsByHole, selectedAssayVariable } = normalizeDrillholeRenderOptions(options);\n const allAssayValues = 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); // cylinder default axis\n\n holes.forEach((hole, idx) => {\n // Improved color palette: use golden angle for better distribution\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 this.scene.add(sphere);\n this.drillLines.push(sphere);\n this.drillMeshes.push(sphere);\n }\n return;\n }\n\n const group = new THREE.Group();\n group.userData = buildHoleUserData(hole);\n const assayIntervals = selectedAssayVariable\n ? this._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 = this._getSegmentColor({\n selectedAssayVariable,\n assayIntervals,\n assayScale,\n holeId: hole.id,\n segmentIndex: i,\n p1,\n p2\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 this.drillMeshes.push(mesh);\n }\n\n this.scene.add(group);\n this.drillLines.push(group);\n });\n\n if (this.camera && this.controls) {\n this.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n if (!preserveView) {\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n }\n }\n\n _getSegmentColor({ selectedAssayVariable, assayIntervals, assayScale, holeId, segmentIndex, p1, p2 }) {\n if (!selectedAssayVariable) {\n return randomSegmentColor(holeId, segmentIndex);\n }\n // Special mode: color by presence of any assay data\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 // Check for ANY overlap with assay intervals\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 const value = getWeightedIntervalValue(assayIntervals, depthRange.segStart, depthRange.segEnd);\n return getAssaySegmentColor(value, assayScale);\n }\n\n _resolveAssayIntervalsForHole(hole, assayIntervalsByHole) {\n if (!assayIntervalsByHole || !hole) return [];\n const holeId = hole.id || hole.holeId;\n if (!holeId) return [];\n\n // Try exact match first\n const exact = assayIntervalsByHole[holeId];\n if (Array.isArray(exact) && exact.length) return exact;\n\n // Try normalized (case-insensitive) match\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\n _fitCameraToBounds({ minX, maxX, minY, maxY, minZ, maxZ }) {\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n\n recenterCameraToOrigin(distance = 1000) {\n recenterCameraToOrigin(this, distance);\n }\n\n lookDown(distance = 2000) {\n lookDown(this, distance);\n }\n\n pan(dx = 0, dy = 0) {\n pan(this, dx, dy);\n }\n\n dolly(scale = 1.1) {\n dolly(this, scale);\n }\n\n focusOnLastBounds(padding = 1.2) {\n focusOnLastBounds(this, padding);\n }\n\n _clearBlocks() {\n this.blocks.forEach((block) => {\n this.scene.remove(block);\n block.geometry.dispose();\n block.material.dispose();\n });\n this.blocks = [];\n }\n\n setStructuralDiscs(structures, holes, opts = {}) {\n if (!this.scene) return;\n this._clearStructuralDiscs();\n if (!structures?.length || !holes?.length) return;\n\n // Uniform-sample down to maxDiscs to keep render performance reasonable\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 this.structuralGroup = buildStructuralDiscs(enriched, opts);\n this.scene.add(this.structuralGroup);\n this.structuralGroup.traverse(child => {\n if (child.isMesh) this.structuralMeshes.push(child);\n });\n }\n\n /**\n * Change the camera field-of-view while keeping the visible scene the same apparent size.\n * FOV is clamped to [FOV_MIN_DEG, FOV_MAX_DEG]. Delegates to setFov in baselode3dCameraControls.\n * @param {number} fovDeg - Desired FOV in degrees\n */\n setCameraFov(fovDeg) {\n setFov(this, fovDeg);\n }\n\n setStructuralDiscsVisible(visible) {\n if (this.structuralGroup) {\n this.structuralGroup.visible = Boolean(visible);\n }\n }\n\n _clearStructuralDiscs() {\n if (this.structuralGroup) {\n this.scene.remove(this.structuralGroup);\n this.structuralGroup.traverse(child => {\n if (child.isMesh) {\n child.geometry.dispose();\n child.material.dispose();\n }\n });\n this.structuralGroup = null;\n }\n this.structuralMeshes = [];\n }\n\n _clearDrillholes() {\n this.drillLines.forEach((line) => {\n this.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 this.drillLines = [];\n this.drillMeshes = [];\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\n if (this.gizmo) {\n this.gizmo.dispose();\n this.gizmo = null;\n }\n\n this.viewChangeHandler = null;\n\n this._clearBlocks();\n this._clearDrillholes();\n this._clearStructuralDiscs();\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 setDrillholeClickHandler(handler) {\n this.drillholeClickHandler = handler;\n }\n\n setControlMode(mode = 'orbit') {\n setControlMode(this, mode);\n }\n}\n\nexport default Baselode3DScene;\n\nfunction normalizeHoleKey(value) {\n return `${value ?? ''}`.trim().toLowerCase();\n}\n\nfunction 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\nfunction 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 * 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"],"names":["HOLE_ID","LATITUDE","LONGITUDE","ELEVATION","AZIMUTH","DIP","FROM","TO","MID","PROJECT_ID","EASTING","NORTHING","CRS","DEPTH","ALPHA","BETA","STRIKE","BASELODE_DATA_MODEL_DRILL_COLLAR","BASELODE_DATA_MODEL_DRILL_SURVEY","BASELODE_DATA_MODEL_DRILL_ASSAY","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","parseCsv","papaParseConfig","standardizeRow","loadTable","kind","loadCollars","crs","keepAll","tableOptions","standardized","hasXY","hasLatLon","result","loadSurveys","required","loadAssays","joinAssaysToTraces","onCols","keyOf","tracesByKey","trace","filterByProject","projectId","coerceNumeric","next","column","assembleDataset","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","parseUnifiedDataset","assayCsv","structuralCsv","assayHoles","structuralHoles","sh","existing","interpolateTrace","sortedTraceRows","targetMd","segIdx","md1","p0","p1","segLen","ddx","ddy","ddz","len","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","NUMERIC_LINE_COLOR","NUMERIC_MARKER_COLOR","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","segments","y0","y1","catVal","palette","uniqueCategories","colorMap","cat","shapes","seg","buildNumericConfig","isBar","isMarkersOnly","isLineOnly","baseTrace","errorConfig","buildPlotConfig","DEFAULT_PALETTE","buildTadpoleConfig","tailScale","colorBy","depthCol","dipCol","azCol","byCat","color","group","length","showLegend","buildStructuralStripConfig","labelCol","fromCol","toCol","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","label","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","payload","intervalsAsTubes","radius","annotationsFromIntervals","DEFAULT_COLOR_MAP","resolveColor","structureType","map","hex","THREE","dipAzimuthToNormal","buildStructuralDiscs","discThickness","opacity","yAxis","xVal","yVal","zVal","dipVal","azVal","normal","geom","mat","mesh","buildViewSignature","viewState","toNum","_e","_f","_g","_h","_i","getViewState","setViewState","camera","up","emitViewChangeIfNeeded","now","signature","fitCameraToBounds","minX","maxX","minY","maxY","minZ","maxZ","centerX","centerY","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","weightedSum","weightTotal","candidate","overlapStart","overlap","getAssaySegmentColor","assayScale","colorHex","normalizeDrillholeRenderOptions","collectAssayValues","assayIntervalsByHole","selectedAssayVariable","allAssayValues","buildHoleUserData","Baselode3DScene","container","height","ambientLight","directionalLight","axesHelper","OrbitControls","FlyControls","ViewportGizmo","animate","delta","handler","renderer","event","gizmoRect","rect","localX","localY","blockIntersects","hit","blockObj","quadIndex","blockData","drillHits","structHits","drillDist","obj","selectedProperty","autoCenter","rawMinX","rawMaxX","rawMinY","rawMaxY","rawMinZ","rawMaxZ","offX","offY","offZ","bkey","blockSet","FACE_DEFS","positions","normals","indices","quadToBlock","vi","bx","by","bz","cx","cy","cz","g","face","nbx","nby","nbz","vBase","sx","sy","sz","geometry","material","block","preserveView","tmpVec","defaultColor","sphereGeom","sphereMat","sphere","cylinderGeom","segmentColor","cylinderMat","segmentIndex","randomSegmentColor","depthRange","exact","normalizeHoleKey","byNormalized","maxDiscs","input","sampled","enriched","child","visible","line","seed","base","seededUnit","band","hash","Baselode3DControls","controlMode","onToggleFly","onRecenter","onLookDown","onFit","BlockModelWidget","properties","onPropertyChange","onOpacityChange","propertyStats","clickedBlock","onPopupClose"],"mappings":"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"}
|